我知道 C# 不处理指针,但我想知道当我知道列表的第一个元素时是否可以获取列表的引用?例如:假设我有一个定义为的列表
List<T> abc
如果我有 abc[0],我可以得到 abc 的参考吗?我是 C# 新手,如果我的问题看起来很奇怪,我深表歉意。在 C/C++ 中,我可以使用 &abc[0] 获取数组 abc 的地址。当我们知道集合中的一项时,C# 是否为我们提供了类似的工具来帮助我们参考集合本身?谢谢,
我知道 C# 不处理指针,但我想知道当我知道列表的第一个元素时是否可以获取列表的引用?例如:假设我有一个定义为的列表
List<T> abc
如果我有 abc[0],我可以得到 abc 的参考吗?我是 C# 新手,如果我的问题看起来很奇怪,我深表歉意。在 C/C++ 中,我可以使用 &abc[0] 获取数组 abc 的地址。当我们知道集合中的一项时,C# 是否为我们提供了类似的工具来帮助我们参考集合本身?谢谢,
集合在 C# 中的工作方式与在 C++ 中的工作方式不同,例如,您可以将相同的对象添加到多个不同的集合中,因此要求获取对象所在列表的引用实际上没有意义包含在,因为它可能在许多列表中(或没有,甚至在同一个列表中多次)
object myObject = new object();
List<object> list = new List<object>();
list.Add(myObject);
object[] someArray = new object[] { myObject };
Assert.AreEqual(list[0], someArray[0]);
如果它对您有所帮助,您可以将 C# 中的列表视为指针列表,这些指针引用存储在指针本身对您隐藏的位置的对象,尽管要了解实际上实现可能更复杂(并且也无关紧要)。
如果列表中的对象与该列表的列表内容之间存在关系,则由您来明确声明并跟踪该 realtionsip 是什么,例如通过Parent
列表中对象的属性
List<T> myList = new List<T>();
// Whenever an item is added to myList set the Parent property
myList.Add(item);
item.Parent = myList;
这是 Windows 窗体为了维护容器中的控件与包含这些控件的容器之间的关系而执行的操作。显然,如果有人试图将同一个对象添加到多个列表中,您应该决定该怎么做。
现在可以使用该System.Runtime.InteropServices.CollectionsMarshal.AsSpan
方法,从 .NET 5.0 开始。
如文档所示,在使用跨度或从跨度中获取的项目引用时,不应将项目添加到列表中或从列表中删除。从技术上讲,这也应该扩展为Capacity
不应该更改也不应该TrimExcess()
调用的。如果在主动使用 span 或其引用时使用这些操作,则列表的内部内存可能不再与 span 中的内存相同。
// Create a simple list with three items.
var list = new List<int>();
list.Add(123);
list.Add(456);
list.Add(789);
// Print list to console.
Console.WriteLine("List items:");
foreach (var item in list)
Console.WriteLine(item);
Console.WriteLine();
// Get a reference to the second item in the list.
// WARNING: DO NOT ADD/REMOVE ITEMS FROM THE LIST WHILE USING THIS SPAN
// OR ANY REFERENCES DERIVED FROM THIS SPAN!
var listSpan = CollectionsMarshal.AsSpan(list);
ref var secondItem = ref listSpan[1];
Console.WriteLine($"Referenced value (original): {secondItem}");
// Change the referenced list item.
secondItem = 0;
Console.WriteLine($"Referenced value (modified): {secondItem}");
Console.WriteLine();
// Print the list to console.
Console.WriteLine("List items:");
foreach (var item in list)
Console.WriteLine(item);
Console.WriteLine();
你应该得到这样的输出:
List items:
123
456
789
Referenced value (original): 456
Referenced value (modified): 0
List items:
123
0
789
如果您正在设计集合中项目的类型,那么您可以向项目的类型添加一个“指向”包含列表的属性;当您构造每个项目时,传入包含列表并将其保存在属性中。
像这样的东西:
class ListItem
{
public List<ListItem> Parent { get; set; }
public ListItem(List<ListItem> parent)
{
Parent = parent;
}
}
ListItem listItem = new ListItem(abc);
abc.Add(listItem);
// Get collection from item.
List<T> def = listItem.Parent;
除非 abc[0] 的类型明确包含对列表的引用。事实上,如果没有明确的引用,你也不能在 C++ 中做到这一点。
想一想,在 C++ 中,如果你能预料firstElemPtr == arrayPtr
到,这仅仅是因为数组以这种方式存储元素,而且它只适用于数组;其他一切都只是偶然。
现在考虑在指向第一个元素的指针之前分配其他东西(可能是元素计数)的任何列表结构。你的假设将不再有效。
考虑:
unsafe static void Main() {
int[] arr = new int[100];
fixed(int* ptr = arr) {
// ptr is a pointer to the zeroth item in the array
}
}
但是,不安全代码在 c# 中并不常见,应仅限于性能关键区域(即使在那时也要谨慎使用)。特别是,请注意我们已经通过这样做“固定”了阵列 - 请注意,只有在固定时ptr
才是可靠的。在块之外,GC 可以自由地重新定位数组,使其无效。fixed
ptr
澄清:我不建议你这样做,但是:这样的事情是完全可能的。
List 的定义有点像这样:
public class List<T> : IList<T> blabla
{
private T [] data;
public T this[int index] {
get { return data[index]; }
set { data[index]=value; }
}
... blabla
}
是的,它不是任何类型的链表。所以你必须相应地使用它。尽管由于 .NET 值类型很小并且类类型都是引用,但通常没有繁重的数组复制操作,例如使用 C++ 就可以实现,因此通用 puprose 集合的这种实现已经足够好(除非被滥用)。从学术角度来看,Vector 可能是一个更好的名称,但事实并非如此。
您将无法获得对数据数组的引用,因为它是私有变量。你也不需要这个。如果您需要枚举器,请显式使用 GetEnumerator 或 foreach 进行隐式使用。