如何处理结构体中的数组?



我正在寻找一种方法来用项目中的数组替换我的一些 List 对象以提高性能。原因是我要替换的列表不会经常更改大小(有时只更改一次(,因此将它们替换为数组是有意义的。另外,我想尽可能防止堆上的分配。

所以我的想法是创建一个具有两个成员"Count"和"Array"的结构(ArrayStruct(。该结构将具有一些添加/删除/获取/设置/等功能...元素。计数将保留数组中可用元素的计数,并且数组的大小只会增加,永远不会减少。 请注意,这是针对非常特殊的情况,因为我知道数组大小不会很大。

我遇到的主要问题是当我将对已经存在的 ArrayStruct 对象(在示例中命名为 a(的引用传递给另一个对象(在示例中名为 b(时 - 我可以编辑其中的数组,但是一旦我在 a 中调整它的大小,我就会在 b 中得到一个超出范围的异常。这是因为我通过System.Array.Resize更新引用的方式,以便对象 b 不以某种方式归因于此更改吗?

我唯一能想到的想法是尝试覆盖赋值运算符,以便它在赋值时创建一个新数组......但显然这是行不通的。

我的数组结构

public struct ArrayStruct<T>
{
[SerializeField] private int m_Count;
[SerializeField] private T[] m_Array;
public int Count => m_Count;
public void Initialize(int startSize)
{
m_Count = 0;
m_Array = new T[startSize];
}
public T GetAt(int index)
{
return m_Array[index];
}
public void SetAt(int index, T newValue)
{
m_Array[index] = newValue;
}
public void Add(T newElement)
{
if (m_Array == null) {
m_Array = new T[1];
} else if (m_Array.Length == m_Count) {
System.Array.Resize(ref m_Array, m_Count + 1);
}
m_Array[m_Count] = newElement;
m_Count++;
}
public void Remove(T element)
{
for (int i = 0; i < m_Count; ++i) {
if (!element.Equals(m_Array[i])) {
continue;
}
for (int j = index; j < m_Count - 1; ++j) {
m_Array[j] = m_Array[j + 1];
}
m_Count--;
m_Array[m_Count] = default(T);
break;
}
}
//Trying to overload the = operating by creating a auto cast, this gives a compile error
/*public static implicit operator ArrayStruct<T>(ArrayStruct<T> otherArray)
{
var newArray = new ArrayStruct<T>();
newArray.m_Count = otherArray.Count;
newArray.m_Array = new T[otherArray.Length];
otherArray.m_Array.CopyTo(newArray.m_Array);
return newArray;
}*/

展示问题的示例

var a = new ArrayStruct<string>()
a.Add("hello");
var b = a;
print (b.GetAt(0)); //"hello"
a.SetAt(0, "new value for a");
print(b.GetAt(0));//"new value for a"  , changing a changed b because the array is the same in both, this is bad
a.Add("resizing array");
print (b.GetAt(1)); //"Out of range exception" ,  because the array was resized for a but not for b therefore the connection broke

那么你们中的任何人知道当我将结构分配给另一个变量时如何使数组的新副本? 当然,我知道我可以使用一个函数来做这样的事情 b = new ArrayStruct(a(; 但我希望它是隐含的。或者,如果有一种方法可以防止对我也有用的任务。 var a = new ArrayStruct((;//let this work 变量 b = a;防止这种情况发生

如果您有任何其他解决方案可以方便地用数组替换列表,请告诉我

那么你们中的任何人有没有想到当我将结构分配给另一个变量时如何使数组的新副本?当然,我知道我可以使用一个函数来做这样的事情,所以 b = new ArrayStruct(a(;但我希望它是隐含的。

你不能这么做。如果a是一个结构体并且你执行var b = a,那么它将始终b设置为a的浅拷贝;没有办法改变这种行为。

做像b = new ArrayStruct(a)这样的事情是你能做的最好的事情。

你不明白"="运算符在那里做什么。对象是通过引用定义的,这意味着当你写var b = a;时,你实际上是在要求你希望避免发生的事情,即 - 对象"b",指向同一个对象,即"a"。 您传递对象"b"以指向对象"a"的引用,该对象使用new关键字保存您定义的相关对象的地址var a = new ArrayStruct<string>()

运算符重载不起作用,因为不能将其用于与在其中编写运算符的类相同类型的转换。你要么必须创建一个单独的对象(类(,这会破坏你试图修复的点,要么传递m_array:public static implicit operator ArrayStruct<T>(T[] array)

你也可以做这样的事情:

public static ArrayStruct<T> CopyFrom (ArrayStruct<T> array)
{
var newArray = new ArrayStruct<T>();
newArray.m_Array = new T[array.Count + 1];
newArray.Count = array.Count;
Array.Copy(array.m_Array, 0, newArray.m_Array, 0, array.m_Array.Length);
return newArray;
}

现在它应该完全按照您想要的方式工作。

var a = new ArrayStruct<string>();
a.Add("hello");
var b = ArrayStruct<string>.CopyFrom(a);
a.SetAt(0, "new value for a");

a.Add("resizing array");
Console.WriteLine(b.Count); // b returns 1
Console.WriteLine(a.Count); // a returns 2

至于为什么你的索引越界了,即它没有保留在"a"中找到的引用,这都与引用有关,再次。

在下面的代码System.Array.Resize(ref m_Array, Count + 1);中,当您添加带有行a.Add("resizing array");的新元素时,您将更改在"a"中找到的对象m_array的引用。这实质上意味着您的"a"对象的m_array(这是 array[] 类型的另一个对象(现在指向一个新对象(数组 [] 类型(,该对象具有新大小。 好的,但是对象"a"现在使用新引用,而对象"b"仍在使用旧引用。对象"b"中的m_array只有一个元素,而"a"中的m_array对象具有新添加的元素"resize array"以及任何以前添加的元素。

我相信这就是System.Array.Resize的工作方式?如果我错了,请编辑我。

下面是一个资源,解释了如何对对象进行深度克隆,如果这是您真正希望做的(即创建一个对象的全新副本,以及其中的任何其他对象(


对象的深度克隆

最新更新