Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting a ref/address from a list in C#

I am aware that C# does not deal with pointers but I wonder if I can get a ref to a list when I know its first element? For example: Let's say I have a list defined as

List<T> abc  

If I have abc[0], can I get a reference of abc? I am new to C#, I apologize if my question seems weird. In C/C++, I can get the address of an array abc by using &abc[0]. Does C# provide us with similar tool that help us refer back to the collection itself when we know one item in the collection? Thanks,

like image 990
user1205746 Avatar asked Sep 10 '12 14:09

user1205746


2 Answers

Collections don't work the same way in C# as they do in C++, for example you can add the same object to multiple different collections and so it doesn't really make sense to ask to get a reference to the list that an object is contained in, as it could be in many lists (or none, or even in the same list multiple times)

object myObject = new object();

List<object> list = new List<object>();
list.Add(myObject);

object[] someArray = new object[] { myObject  };
Assert.AreEqual(list[0], someArray[0]);

If it helps you can think of lists in C# as being lists of pointers references to the objects being stored where the pointer itself is hidden from you, although understand that in reality the implementation may be more complicated (and is also irrelevant).

If there is a relationship between the objects in a list and the list contents of that list then its up to you to explicitly declare and keep track of what that realtionsip is, for example through a Parent property on the object in the list

List<T> myList = new List<T>();

// Whenever an item is added to myList set the Parent property
myList.Add(item);
item.Parent = myList;

This is what Windows Forms does in order to maintain the relationship between the controls in a container, and the container in which those controls are contained. Obviously you should decide what to do if someone tries to add the same object to multiple lists.

like image 168
Justin Avatar answered Oct 09 '22 17:10

Justin


This is now possible, starting with .NET 5.0, by using the System.Runtime.InteropServices.CollectionsMarshal.AsSpan method.

As indicated by the documentation, items should not be added to or removed from the list while using the span or item references taken from the span. Technically this should also be extended to say that the Capacity should not be changed nor should TrimExcess() be called. If these operations are used while actively using the span or its references, then the internal memory of the list may no longer be the same as the memory in the 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();

You should get output like this:

List items:
123
456
789

Referenced value (original): 456
Referenced value (modified): 0

List items:
123
0
789
like image 22
Thomas Kaiser Avatar answered Oct 09 '22 19:10

Thomas Kaiser