在c#列表中添加结构引用
本文关键字:结构 引用 添加 列表 | 更新日期: 2023-09-27 18:17:07
试图保持我的c#代码优化,我发现,如果我有一个结构元素列表,每次插入将是一个完整的复制-这是我想避免的。
在c++中,我会简单地保留一个指针列表,所以我想知道我是否可以使用c#做同样的事情,也许通过将列表作为对结构的引用列表。不幸的是,结构不能变成一个类,因为它是XNA库的一部分(Vector3,矩阵等…)在任何情况下——如果可能的话,格式和用法会是什么样子?
谢谢。
基本上没有。选择:
- 使用一个类(你已经说过你不能)
- 盒子
- 写一个类来包装它(本质上是手动装箱)
- 使用一个数组,并访问它只有直接在数组的索引(不复制到一个变量);这是直接与数组中的项目对话(没有复制)
作为最后一个例子;
if(arr[idx].X == 20) SomeMethod(ref arr[idx]);
. x和在someemethod中的任何用法都是直接访问数组中的值,而不是复制。这只适用于向量(数组),不适用于列表。
一个不可能引用结构体的列表的原因是:它允许你在列表中存储堆栈上变量的地址;数组通常比堆栈上的变量寿命长,所以这将是非常不安全的
在c#中不能创建可存储的结构引用,但是可以为值类型创建引用类型包装器。也就是说,对于一个小结构体,与复制内存相关的开销不会很高。你的分析是否表明这是一个问题?
下面是一个用引用类型包装值类型的例子。请注意,只有当对特定值的所有访问都是通过包装引用类型时,这才有效。这违反了标准的绝缘规则(因为公共字段),但这是一个有点特殊的情况。
public sealed class Reference<T>
where T: struct
{
public T Value;
public Reference(T value)
{
Value = value;
}
}
另一件值得注意的事情是,Reference包装器本身可以接受值null,尽管其内容不可为空。如果您愿意,还可以添加隐式或显式转换操作符以使其更透明。
如果结构体是由简单类型组成的,则可以创建指针数组。是的,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);
}
}
}
很抱歉这么长的代码,但我想展示结构指针的所有特性。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)
。