创建一个ImmutableArray而不复制C#

Yai*_*adt 3 c# optimization

http://source.roslyn.io/#System.Collections.Immutable/System/Collections/Immutable/ImmutableArray.cs上查看ImmutableArray的源代码

public static ImmutableArray<T> Create<T>(params T[] items)
{
    if (items == null)
    {
        return Create<T>();
    }

    // We can't trust that the array passed in will never be mutated by the caller.
    // The caller may have passed in an array explicitly (not relying on compiler params keyword)
    // and could then change the array after the call, thereby violating the immutable
    // guarantee provided by this struct. So we always copy the array to ensure it won't ever change.
    return CreateDefensiveCopy(items);
}
Run Code Online (Sandbox Code Playgroud)

现在我有一个我知道不会改变的数组,我想创建一个ImmutableArray来允许客户端安全地访问我的数组.

是否有任何方法(可能是一个肮脏的黑客)创建一个ImmutableArray只会使用我的原始数组,而不是复制它?

编辑

刚看到这个请求就是这个功能.它还提供最快的黑客使用:https://github.com/dotnet/corefx/issues/28064

xan*_*tos 7

如果你知道数组的确切长度,你可以使用ImmutableArray.CreateBuilder<>加号.MoveToImmutable(),它将ImmutableArray<>从内部创建一个Builder而不复制它:

var builder = ImmutableArray.CreateBuilder<int>(4);
builder.Add(1);
builder.Add(2);
builder.Add(3);
builder.Add(4);
ImmutableArray<int> array = builder.MoveToImmutable();
Run Code Online (Sandbox Code Playgroud)

如果,该方法.MoveToImmutable()将抛出异常builder.Capacity != builder.Count

请注意,构建器的其他方法(如.ToImmutable())将创建该数组的副本.

  • 当从头开始构建新的不可变数组时,这是最好的解决方案。但是,当已经有一个只需要使其不可变的数组时,这并没有帮助,因为它仍然需要复制该数组。可能值得在答案中包含此信息。 (2认同)

Mis*_*sky 6

在.NET 8预览版中不再需要使用Unsafe

他们公开了一些额外的方法来访问内部构造函数。

使用ImmutableCollectionsMarshal.AsImmutableArray<T>

ImmutableArray<T> im = ImmutableCollectionsMarshal.AsImmutableArray(array);
Run Code Online (Sandbox Code Playgroud)


Ami*_*miś 5

还有另外两种黑客方法,均在这里建议:/sf/answers/265932131/(一种在答案中,一种在评论中)。

  1. 将一种结构类型编组为另一种结构类型。
  2. 不安全地将一个投射到另一个。

第一个涉及创建一个新的结构类型,该结构类型反映 ImmutableArray(这是单个T[]字段)的布局,并更改 CLR(运行时)所看到的该结构的类型。该结构如下所示:

public struct HackImmutableArray<T>
{
    public T[] Array;
}
Run Code Online (Sandbox Code Playgroud)
  1. 编组:

    static ImmutableArray<T> HackyMakeImmutable<T>(T[] array)
    {
        var arrayObject = (object)new HackImmutableArray<T> { Array = array };
        var handle = GCHandle.Alloc(arrayObject, GCHandleType.Pinned);
        var immutable = (ImmutableArray<T>)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
        handle.Free();
        return immutable;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 不安全的转换(这里写的好帮手,可以在这篇博客文章中找到)。转换使用System.Runtime.CompilerServices.Unsafe NuGetUnsafe中可用的静态类

    using System.Runtime.CompilerServices;
    
    static ImmutableArray<T> HackyMakeImmutable<T>(T[] array)
    {
        return Unsafe.As<T[], ImmutableArray<T>>(ref array);
    }
    
    Run Code Online (Sandbox Code Playgroud)

第二个选项“不安全”,但相当安全,因为我们可以肯定地假设 ImmutableArray 的结构布局不会改变,这是一个定义功能,而且它也可能比任何其他解决方案快得多。