C# 创建不复制C的不可变数组#

C# 创建不复制C的不可变数组#,c#,optimization,C#,Optimization,查看ImmutableArray的源代码 公共静态不可变数组创建(参数T[]项) { if(items==null) { 返回Create(); } //我们不能相信传入的数组永远不会被调用方改变。 //调用方可能已显式传入数组(不依赖于编译器params关键字) //然后可以在调用后更改数组,从而违反不可变规则 //此结构提供的保证。因此,我们始终复制数组以确保它不会更改。 返回CreateDefensiveCopy(项目); } 现在我有了一个我知道不会改变的数组,我想创建一个不可变数组,

查看ImmutableArray的源代码

公共静态不可变数组创建(参数T[]项)
{
if(items==null)
{
返回Create();
}
//我们不能相信传入的数组永远不会被调用方改变。
//调用方可能已显式传入数组(不依赖于编译器params关键字)
//然后可以在调用后更改数组,从而违反不可变规则
//此结构提供的保证。因此,我们始终复制数组以确保它不会更改。
返回CreateDefensiveCopy(项目);
}
现在我有了一个我知道不会改变的数组,我想创建一个不可变数组,以允许客户端安全地访问我的数组

有没有办法(可能是恶意攻击)创建一个ImmutableArray,它只使用我的原始数组,而不是复制它

编辑


刚刚看到了这个功能的要求。它还提供了最快的使用技巧:

如果您知道数组的确切长度,您可以使用
ImmutableArray.CreateBuilder
加上将从
Builder
的内部创建
ImmutableArray
,而无需复制它:

var builder = ImmutableArray.CreateBuilder<int>(4);
builder.Add(1);
builder.Add(2);
builder.Add(3);
builder.Add(4);
ImmutableArray<int> array = builder.MoveToImmutable();
var builder=ImmutableArray.CreateBuilder(4);
增加(1);
增加(2);
增加(3);
增加(4);
ImmutableArray=builder.MoveToImmutable();
如果
builder.Capacity!=生成器计数


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

这可能是一个坏主意,它们可以对您使用相同的技巧,但您可以通过反射进行欺骗:

public static ImmutableArray<T> GetImmutableArray<T>(T[] arr)
{
    var immutableArray = ImmutableArray.Create(new T[0]);
    var boxed = ((object) immutableArray);
    var t = boxed.GetType();
    var fi = t.GetField("array", BindingFlags.NonPublic | BindingFlags.Instance);
    fi.SetValue(boxed, arr);
    return (ImmutableArray<T>)boxed;
}

反射成本必须与数组复制成本进行权衡

您需要
不可变数组
还是
IReadOnlyList
就足够了?如果是后者,则始终可以实现一个非常轻量级的数组包装器,以满足您的需要:

public class ImmutableArrayWrapper<T>: IReadOnlyList<T>
{
     public static ImmutableArrayWrapper<T> Wrap(T[] array)
         => new ImmutableArrayWrapper(array);

    private readonly T[] innerArray;

    private ImmutableArrayWrapper(T[] arr) {
         if (arr == null)
             throw new ArgumentNullException();

         innerArray = arr; }

    public int Count => innerArray.Count();
    public T this[int index] => innerArray[index];

    //IEnumerable<T>...
}
公共类ImmutableArrayWrapper:IReadOnlyList
{
公共静态ImmutableArrayWrapper包装(T[]数组)
=>新的ImmutableArrayRapper(阵列);
私有只读T[]内部数组;
专用不可变阵列说话者(T[]arr){
如果(arr==null)
抛出新ArgumentNullException();
innerArray=arr;}
public int Count=>innerArray.Count();
public T this[int index]=>innerArray[index];
//我数不清。。。
}
现在,您可以安全地将包装传递给客户端。

他们建议最快的方法是使用System.Runtime.CompilerServices.Unsafe:

ImmutableArray<T> im = Unsafe.As<T[], ImmutableArray<T>>(ref array);
ImmutableArray im=Unsafe.As(ref数组);

还有另外两种黑客方法,都在这里提出:(一种在回答中,一种在评论中)

  • 将一个结构类型封送到另一个结构类型
  • 不安全地互相投掷
  • 第一个涉及创建一个新的结构类型来镜像ImmutableArray的布局(它是一个
    T[]
    字段),并更改CLR(运行时)看到的该结构的类型。结构将如下所示:

    var arr = new int[] { 1, 2, 3 };
    Console.WriteLine("Arr: " + string.Join(",", arr)); //Arr: 1,2,3
    var imm = GetImmutableArray(arr);
    Console.WriteLine("ImmutableArray: " + string.Join(",", imm)); //ImmutableArray: 1,2,3
    arr[0] = 234;
    imm[0] = 235; //Compile Error
    Console.WriteLine("ImmutableArray: " + string.Join(",", imm)); //ImmutableArray: 234,2,3
    
    public struct HackImmutableArray<T>
    {
        public T[] Array;
    }
    

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

    如果您知道您的元素不会更改,为什么不简单地使用普通数组?为什么要尝试避免复制?这是铸造性能问题吗?“你过早地优化了吗?”斯威普。演出这是一个向量库,所以必须快速。通过vector.Add中的数组进行额外的迭代大约会减半performance@HimBromBeere. 因为我需要为客户端代码提供对数组的只读访问权限,那么返回IReadOnlyList呢?@MineR我喜欢坏主意。总是让我觉得自己更像一个程序员;-)。谢谢你。除非真的有必要,否则我会保留它,但它可能只是有用的。我建议使用像ImmutableArray这样的结构,因为这意味着与array@YairHalberstadt使用
    不可变数组
    的问题是
    创建
    复制数组内容;这是OP想要避免的开销。我就是OP;-)。我的意思是使用结构而不是类,比如不可变数组does@YairHalberstadt哈哈,对不起。好吧,如果使用结构是必须的,那么这个解决方案就不行了;如果您使用的是
    IReadOnlyListz
    引用,那么将包装更改为结构没有帮助。
    public struct HackImmutableArray<T>
    {
        public T[] Array;
    }
    
    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;
    }
    
    using System.Runtime.CompilerServices;
    
    static ImmutableArray<T> HackyMakeImmutable<T>(T[] array)
    {
        return Unsafe.As<T[], ImmutableArray<T>>(ref array);
    }