6

Now that C# supports ref return values, is there some way (or alternative) to use List<T> where T is a value type and get ref from the indexer? From the documentation it seems that it returns simply T, which means that there is no way to update a structure in place without copying it out of the list, updating, and copying back in.

Is there any way to use List<T> while getting a ref indexer, or some alternative data structure that does the same? I'm aware that it is possible to do this with arrays, but I'm looking for a dynamic growable array.

Jakub Arnold
  • 85,596
  • 89
  • 230
  • 327
  • 1
    You do not need to use ref. When you update a List in a method it automatically update the same list in the parent. The List is passed like a pointer in c language. – jdweng Aug 13 '18 at 22:04
  • @jdweng I don't think you understand the question, it's about the items in the list, not the list itself. – Selman Genç Aug 13 '18 at 22:06
  • A bit of the obvious; You could wrap the value type in a reference type, but I don't think that's a solution you want to hear. Alternatively you could create a new `IList`type; although I am not sure the `ref` modifier is valid. – Stefan Aug 13 '18 at 22:09
  • Although the ref indexer is supported: https://stackoverflow.com/questions/49400277/c-sharp-indexers-with-ref-return-gets-that-also-support-sets – Stefan Aug 13 '18 at 22:10
  • @Stefan Wrapping the value in a reference type is exactly what you'd want to avoid. At least for the projects I'm working on, value types are used for performance reasons, and wrapping in references/boxing just completely breaks that. – Jakub Arnold Aug 13 '18 at 22:30
  • Same applies to items in the list. You do not have to copy out and put back in. Just reference item. MyList item3 = myList[3] does not make a copy of the item, just a link to the item. So changing an property in item3 changes the property in myList. – jdweng Aug 13 '18 at 23:11
  • You can use reflection to get to the underlying array field in the List, and then use ref on that. You'd have to balance the reflection cost vs any benefit you'd gain from the ref into the array. – MineR Aug 13 '18 at 23:34
  • It does work : static void Main(string[] args) { List myList = new List() { 1, 2, 3, 4, 5, 6, 7, 8 }; ChangeList(myList); } static void ChangeList(List l) { l[0] = 5; }} – jdweng Aug 13 '18 at 23:35
  • @JakubArnold: good point – Stefan Aug 14 '18 at 08:29
  • @jdweng It works for `int`, but not for `struct` (value type with fields), where `l[0].x = 5` is impossible, forcing you to do `var foo = l[0]; foo.x = 5; l[0] = foo` where `foo` is say `struct Foo { int x; }`. That's the problem that we're trying to solve – Jakub Arnold Aug 14 '18 at 14:11
  • Why can't you convert the structures to classes? – jdweng Aug 14 '18 at 15:05
  • @jdweng Classes are heap allocated reference types, while structs are allocated in place. This means if you have a `List` where T is a class, you actually have a small list of pointers and every access points somewhere on the heap. If T is a struct, they're all tightly packed in a single block of memory, which makes iterations very cache friendly, and overall many of accesses will be close to each other. It's basically for performance, and it has a huge impact. – Jakub Arnold Aug 15 '18 at 09:25
  • Structures are older than classes and classes were developed to add features that were not in classes. Maybe add you structure to a class to get best of both worlds. – jdweng Aug 15 '18 at 09:39
  • @jdweng There's nothing older or newer about structures and classes in .NET. It has both reference and value types from the beginning. `struct` defines a value type, `class` defines a reference type. These were built into the .NET since the beginning. They mainly differ in their allocation/memory semantics. For example by default, structs (value types) are passed by value (unless ref is used), while classes are passed by reference (ref has a different meaning). One uses `struct` over `class` either for semantic reasons (`DateTime` for example), or for performance, which is my case. – Jakub Arnold Aug 15 '18 at 11:08
  • My only point is structures were designed to be backwards compatible with older languages. So I think you are answering you own question. It looks like you want to pass by reference and not by value. – jdweng Aug 15 '18 at 11:15
  • Why would structures in any way be related to compatibility with older languages? Anyway, reference semantics for structs are achieved by `ref`, which is what this question is about. Reference types are completely irrelevant to this question, because they have different semantics. The only thing in question is why `List` doesn't support the new ref returns. – Jakub Arnold Aug 16 '18 at 11:37

1 Answers1

1

Looking at the examples given in this documentation and this blog post, arrays support ref return values via indexer, but List<T> doesn't (I have tried).

So I don't think there is anyway to use ref return on the list indexer until it is implement in the framework.

Selman Genç
  • 100,147
  • 13
  • 119
  • 184
  • Thanks for the links, I probably missed this in the question, but I'm aware of the ability to do ref return on arrays (see older relevant question https://stackoverflow.com/questions/39905920/is-there-a-listt-like-dynamic-array-that-allows-access-to-the-internal-array-d). Is there no other data structure in .NET that implements a `ref return` indexer with a dynamic array? Or a popular library that can take place in the meantime? I can't be the only one who wants to use this. – Jakub Arnold Aug 13 '18 at 22:32
  • @JakubArnold you're not the only one :) I made a `FastList` type by copying the `List` source code and adapting it for my needs. You can do the same. – Lucas Trzesniewski Aug 14 '18 at 10:41