2011-12-08 79 views
6

试图让我的C#代码优化,我发现,如果我有结构元素的列表,每个插入会是一个完整副本 - 这是我想避免的。添加参考结构到C#列表

在C++中,我只想保持指针列表,所以我在想,如果我可以做同样的使用C#,也许是有列表,以结构引用列表。 不幸的是,结构不能变成一类,因为它是XNA库(的Vector3,矩阵等) 在任何情况下的一部分 - 将如何具体格式和使用样子,如果它是可能的吗?

谢谢。

+1

创建一个存储结构的类。对于过早优化要非常小心,指针数组的缓存局限性很差。 –

+2

结构比参考大多少?结构应该很小;如果它们比参考文件大得多,那么复制时间就是你的瓶颈,那么你可能不想首先使用结构。你能提供更多细节吗? –

+0

那么,引用只需要4个字节,所以比浮点或整数大的东西已经比这个大。如果你做数学,即使我非常谦虚,只存储一个简单的Vector4,它的比例已经是4:1,这与大型结构没有任何关系。 – Adi

回答

5

不,基本上。选项:

  • 使用一个类(你已经说你不能)
  • 框它
  • 编写把它包装类(主要是,手动拳击)
  • 使用的数组,通过索引直接在数组中访问它只有(不复制到变量中);然后直接与阵列中的项目交谈(无副本)

作为最后一个例子;

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

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

一个原因参于结构的列表是不可能的:它允许你我店,在列表中,在栈上的变量的地址;该阵列通常会超越堆栈上的变量,因此,如果结构由简单类型,那么你可以让数组的指针,这将是可笑的不安全

+0

我记得一些有趣的错误,你可以通过无意识地将参考结构存储在堆栈中的C中来创建它。 –

+1

你可以在C中做很多事情,你真的不应该这样做。 –

+0

@Rhhound,尤其是当黑客发现你不应该做的事情并将数据注入到损坏的堆栈中以劫持应用程序时。 –

5

您不能创建在C#中的结构storeable引用,但你可以创建自己的价值类型的引用类型的包装。也就是说,复制内存的开销对于小型结构来说不会很高。你的分析表明这是一个问题?


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

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

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

另一件事值得一提的是,参考包装本身可以采取null值,但其内容是不可为空。如果您愿意,您还可以添加隐式或显式转换运算符,使其更透明。

+0

用于询问分析数据。 – Scott

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); 
     } 

    } 
} 

对不起,长的代码,但我想显示结构指针的所有功能。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)

+0

这不会将数组的位置固定在创建代理的范围之外,因此,在GC期间移动数组时,可能会导致崩溃。 –

+0

@DanBryant - 有趣。我如何强制GC收集原始数组并演示崩溃? – ja72

+0

您可能会在分配数组之前尝试分配一堆小对象,然后在分配数组之后引用它们(以便GC知道它们必须保持一段时间),然后停止引用它们并强制使用GC.Collect进行GC 。我相信这会触发Gen0的堆压缩,甚至可能将阵列提升到Gen1;这些中的任何一个都应该将数组移到内存中。请注意,它不能保证崩溃;它可能会默默地破坏记忆。如果你在新GCd空间中分配了足够大的数组,那么访问你的原始指针,你可能会观察到它。 –