Why can't I return a link to the dictionary value?

public class PropertyManager { private Dictionary<ElementPropertyKey, string> _values = new Dictionary<ElementPropertyKey, string>(); private string[] _values2 = new string[1]; private List<string> _values3 = new List<string>(); public PropertyManager() { _values[new ElementPropertyKey(5, 10, "Property1")] = "Value1"; _values2[0] = "Value2"; _values3.Add("Value3"); } public ref string GetPropertyValue(ElementPropertyKey key) { return ref _values[key]; //Does not compile. Error: An expression cannot be used in this context because it may not be returned by reference. } public ref string GetPropertyValue2(ElementPropertyKey key) { return ref _values2[0]; //Compiles } public ref string GetPropertyValue3(ElementPropertyKey key) { return ref _values3[0]; //Does not compile. Error: An expression cannot be used in this context because it may not be returned by reference. } } 

In the above example, GetPropertyValue2 compiles, but GetPropertyValue and GetPropertyValue3 do not. What is wrong with returning a value from a dictionary or list as a reference while it works for an array?

+5
source share
1 answer

I would like to add my answer to "pot", perhaps this makes things more clear. So why does this not work for lists and dictionaries? Well, if you have code like this:

 static string Test() { Dictionary<int, string> s = new Dictionary<int, string>(); return s[0]; } 

This (in debug mode) translates to this IL code:

 IL_0000: nop IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.Dictionary`2<int32, string>::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.0 IL_0009: callvirt instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<int32, string>::get_Item(!0) IL_000e: stloc.1 IL_000f: br.s IL_0011 IL_0011: ldloc.1 IL_0012: ret 

This, in turn, means that what you do with one line of code ( return s[0] ) is actually a three-step process: calling the method, storing the return value in a local variable and then returning the stored value in this local variable . And, as links provided by others pointed out, returning a local variable by reference is not possible (if the local variable is not a local local reputation, but, as others have pointed out, again, since Diciotionary<TKey,TValue> and List<T> have no API reverse links, this is not possible).

And now, why does this work for an array? If you look at how massive indexing is handled more closely (that is, at the level of the IL code), you can see that there is no method call to index the array. Instead, a special opcode is added to the code called ldelem (or a variant of it). Code like this:

  static string Test() { string[] s = new string[2]; return s[0]; } 

translates this into IL:

 IL_0000: nop IL_0001: ldc.i4.2 IL_0002: newarr [mscorlib]System.String IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldc.i4.0 IL_000a: ldelem.ref IL_000b: stloc.1 IL_000c: br.s IL_000e IL_000e: ldloc.1 IL_000f: ret 

Of course, this looks the same as for the dictionary, but I think the key difference is that here the indexer generates an IL-native call, not a property call (i.e. a method). And if you look at all the possible ldelem options on MSDN here , you can see that there is one of them called ldelema that can load the address of an element directly into the heap. And indeed, if you write a piece of code as follows:

 static ref string Test() { string[] s = new string[2]; return ref s[0]; } 

This translates to the following IL code using the ldelema opcode with a direct link:

 IL_0000: nop IL_0001: ldc.i4.2 IL_0002: newarr [mscorlib]System.String IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldc.i4.0 IL_000a: ldelema [mscorlib]System.String IL_000f: stloc.1 IL_0010: br.s IL_0012 IL_0012: ldloc.1 IL_0013: ret 

So, basically, array indexers are different, and under the hood, arrays have support for loading an element by reference to the evaluation stack via IL's own calls. Since Dictionary<TKey,TValue> and other collections implement indexers as properties that lead to method calls, they can only do this if the method explicitly specifying ref returns.

+2
source

Source: https://habr.com/ru/post/1274464/


All Articles