试图保持我的C#代码优化,我发现如果我有一个结构元素列表,每个插入都是一个完整的副本 - 我想避免的。
在C ++中我只是保留一个指针列表,所以我想知道我是否可以使用C#做同样的事情,也许将列表作为结构引用列表。 不幸的是,结构不能变成一个类,因为它是XNA库的一部分(Vector3,Matrix等...) 无论如何 - 如果可能,格式和用法会如何?
感谢。
答案 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)
。