简体   繁体   English

如何在 C# 中创建一个包含数组但不使用堆的结构?

[英]How do i create a struct in C# that contains an array but does not use heap?

What i need:我需要的:

  • a polygon with arbitrary amount of vertices ( or at least up to max number of vertices )具有任意数量顶点的多边形(或至少达到max顶点数)
  • it should be a struct, so that it can be fast and can be assigned / passed by value它应该是一个结构体,以便它可以快速并且可以按值分配/传递

It seems like i can't use arrays or collections for storing vertices, because then my polygon struct would point to objects on a heap, and when one polygon is assigned to another one by value only shallow copy would be performed, and i would have both polygons pointing to the same vertex array.似乎我不能使用数组或集合来存储顶点,因为这样我的多边形结构将指向堆上的对象,并且当一个多边形按值分配给另一个多边形时,只会执行浅拷贝,我会两个多边形都指向同一个顶点数组。 For example:例如:

Polygon a = new Polygon(); 
Polygon b = a; 
// both polygons would be changed 
b.vertices[0] = 5; 

Then how do i create a struct that can have arbitrary number (or some fixed number) of vertices, but without using heap at all?那么我如何创建一个可以具有任意数量(或某个固定数量)顶点但根本不使用堆的结构?

I could just use lots of variables like v1, v2, v3 ... v10 etc, but i want to keep my code clean, more or less.我可以使用很多变量,如v1, v2, v3 ... v10等,但我想或多或少地保持我的代码干净。

You have the option to define your array with the fixed keyword, which puts it in the stack.您可以选择使用fixed关键字定义数组,该关键字将其放入堆栈中。

But you cannot directly access the elements of the array, unless you are in an unsafe context and use pointers.但是您不能直接访问数组的元素,除非您处于unsafe上下文中并使用指针。

To get the following behavior:要获得以下行为:

    static void Main(string[] args)
    {
        FixedArray vertices = new FixedArray(10);
        vertices[0] = 4;

        FixedArray copy = vertices;
        copy[0]  = 8;
        Debug.WriteLine(vertices[0]);
        // 4
        Debug.WriteLine(copy[0]);
        // 8
    }

Then use the following class definition:然后使用以下类定义:

public unsafe struct FixedArray 
{
    public const int MaxSize = 100;

    readonly int size;
    fixed double data[MaxSize];

    public FixedArray(int size) : this(new double[size])
    { }

    public FixedArray(double[] values)
    {
        this.size = Math.Min(values.Length, MaxSize);
        for (int i = 0; i < size; i++)
        {
            data[i] = values[i];
        }
    }

    public double this[int index]
    {
        get
        {
            if (index>=0 && index<size)
            {
                return data[index];
            }
            return 0;
        }
        set
        {
            if (index>=0 && index<size)
            {
                data[index] = value;
            }
        }
    }

    public double[] ToArray()
    {
        var array = new double[size];
        for (int i = 0; i < size; i++)
        {
            array[i] = data[i];
        }
        return array;
    }        
}

A couple of things to consider.有几件事需要考虑。 The above needs to be compiled with the unsafe option.以上内容需要使用unsafe选项进行编译。 Also the MaxSize but be a constant, and the storage required cannot exceed this value. MaxSize也是一个常数,并且需要的存储不能超过这个值。 I am using an indexer this[int] to access the elements (instead of a field) and also have a method to convert to a native array with ToArray() .我正在使用索引器this[int]来访问元素(而不是字段),并且还有一种方法可以使用ToArray()转换为本地数组。 The constructor can also take a native array, or it will use an empty array to initialize the values.构造函数也可以采用本机数组,或者使用空数组来初始化值。 This is to ensure that new FixedArray(10) for example will have initialized at least 10 values in the fixed array (instead of being undefined as it is the default).这是为了确保例如new FixedArray(10)将在固定数组中初始化至少 10 个值(而不是未定义,因为它是默认值)。

Read more about this usage of fixed from Microsoft or search for C# Fixed Size Buffers . 从 Microsoft阅读有关此fixed用法的更多信息或搜索C# Fixed Size Buffers

  • Heap array field堆数组字段

     struct StdArray { int[] vertices; Foo(int size) { vertices = new int[size]; } }
  • Stack array field堆栈数组字段

     unsafe struct FixedArray { fixed int vertices[100]; int size; Foo(int size) { this.size = size; // no initialization needed for `vertices` } }

If it suits your logic, you could use a Span<T> , which is allocated on the stack.如果它适合您的逻辑,您可以使用在堆栈上分配的Span<T> Read more here 在这里阅读更多

One other way to just copy the array with a copy constructor使用复制构造函数复制数组的另一种方法

public Polygon(Polygon other)
{
    this.vertices = other.vertices.Clone() as int[];
}

then然后

var a = new Polygon();
a.vertices[0] = 5;

var b = new Polygon(a):
Debug.WriteLine(a.vertices[0]);
// 5
Debug.WriteLine(b.vertices[0]);
// 5

b.vertices[0] = 10;
Debug.WriteLine(a.vertices[0]);
// 5
Debug.WriteLine(b.vertices[0]);
// 10

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM