将结构引用添加到C#列表中

时间:2011-12-08 16:31:33

标签: c#

试图保持我的C#代码优化,我发现如果我有一个结构元素列表,每个插入都是一个完整的副本 - 我想避免的。

在C ++中我只是保留一个指针列表,所以我想知道我是否可以使用C#做同样的事情,也许将列表作为结构引用列表。 不幸的是,结构不能变成一个类,因为它是XNA库的一部分(Vector3,Matrix等...) 无论如何 - 如果可能,格式和用法会如何?

感谢。

3 个答案:

答案 0 :(得分:5)

您无法在C#中创建对结构的可存储引用,但您可以为您的值类型创建引用类型包装器。也就是说,与复制内存相关的开销对于小型结构来说不会很高。您的分析是否表明这是一个问题?


以下是包含在引用类型中的值类型的示例。请注意,这仅适用于对特定值的所有访问都是通过包装引用类型的情况。这违反了标准的绝缘规则(因为公共领域),但这是一个特例。

public sealed class Reference<T>
    where T: struct
{
    public T Value;

    public Reference(T value)
    {
        Value = value;
    }
}

另一件值得注意的事情是,Reference包装器本身可以采用null值,尽管它的内容是不可为空的。如果愿意,您还可以添加隐式或显式转换运算符以使其更透明。

答案 1 :(得分:5)

不,基本上。选项:

  • 使用课程(你已经说过你不能)
  • 盒子
  • 写一个包装它的类(基本上是手动装箱)
  • 使用数组,并通过索引直接在数组中访问 (不复制到变量中);然后直接与数组中的项目进行对话(无副本)

作为最后一个例子;

if(arr[idx].X == 20) SomeMethod(ref arr[idx]);

.X和SomeMethod中的任何用法都是直接在数组中访问值,而不是副本。这只适用于矢量(数组),而不是列表。

无法构建refs列表的一个原因是:它允许您在列表中存储堆栈中变量的地址;数组通常比堆栈上的变量更长,所以这将是非常不安全的

答案 2 :(得分:0)

如果结构由简单类型组成,那么你可以创建一个指针数组。 C#中的指针对于像你这样的情况非常有用。但是有一些限制。原始结构必须存储在Array而不是List<>中。查看使用unsafe构建标志编译的下面示例。

[StructLayout(LayoutKind.Sequential)]
public struct Vec3
{
    public double X, Y, Z;
    public double Mag { get { return Math.Sqrt(X * X + Y * Y + Z * Z); } }
}

public unsafe class Vec3ArrayProxy
{
    Vec3*[] ptr = null; //internal array of pointers

    public Vec3ArrayProxy(Vec3[] array)
    {
        ptr = new Vec3*[array.Length]; //allocate array
        fixed (Vec3* src = array) //src holds pointer from source array
        {
            for (int i = 0; i < array.Length; i++)
            {
                ptr[i] = &src[i]; //take address of i-th element
            }                
        }
    }

    public Vec3ArrayProxy(Vec3ArrayProxy other)
    {
        //just use all the existing pointers
        ptr = (Vec3*[])other.ptr.Clone();
        //or I could say:
        //ptr = other.ptr;
    }
    // Access values with index
    public Vec3 this[int index]
    {
        get { return *ptr[index]; }
        set { *ptr[index] = value; }
    }
    public int Count { get { return ptr.Length; } }
    // Access the array of pointers
    public Vec3*[] PtrArray { get { return ptr; } }
    // Copy the values of original array into new array
    public Vec3[] ToArrayCopy()
    {
        Vec3[] res = new Vec3[ptr.Length];
        for (int i = 0; i < res.Length; i++)
        {
            res[i] = *ptr[i];
        }
        return res;
    }

}


unsafe class Program
{
    static void Main(string[] args)
    {
        const int N = 10; //size of array

        // Allocate array in memory
        Vec3[] array = new Vec3[N];

        // Assign values into array
        for (int i = 0; i < N; i++)
        {
            array[i] = new Vec3() { X = i, Y = 0, Z = 0 };
        }

        //Build proxy to array (with pointers)
        Vec3Array A = new Vec3Array(array);
        // Reference the same pointers as A
        Vec3Array B = new Vec3Array(A); 

        // Change the original array
        array[4].X = -4;

        // Or change via a copy
        A.PtrArray[5]->Y = -5;  

        // Or assign a new value
        B[0] = B[9];            

        // Show contents of array via proxy A
        Console.WriteLine("{0,-6}|{1,6}|{2,6}|{3,6}|{4,6}", 
            "i", "X", "Y", "Z", "Mag");
        for (int i = 0; i < N; i++)
        {
            Console.WriteLine("{0,6}|{1,6:F2}|{2,6:F2}|{3,6:F2}|{4,6:F3}", 
                i + 1, A[i].X, A[i].Y, A[i].Z, A[i].Mag);
        }

    }
}

很抱歉长代码,但我想展示struct指针的所有功能。 List<>不起作用的原因是因为您无法获取指向列表元素的指针。如果你真的真的必须使用List<>,那么使用以下代码从私有字段_items中提取数组:

    static T[] ExtractArray(List<T> list)
    {
        //list.TrimExcess();
        var t = list.GetType();
        var items = t.GetField("_items", 
            BindingFlags.NonPublic | BindingFlags.Instance);
        return items.GetValue(list) as T[];
    }

粗俗,它可以工作,一旦您在List<>上进行了一次反射,就可以将结果缓存在静态字段中,并且每次只调用items.GetValue(list)