gpt4 book ai didi

ref - 为什么我不能返回对字典值的引用?

转载 作者:行者123 更新时间:2023-12-04 20:30:15 25 4
gpt4 key购买 nike

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.
}
}

在上面的示例中 GetPropertyValue2 编译,但 GetPropertyValue 和 GetPropertyValue3 不编译。
从字典或列表返回一个值作为引用有什么问题,而它对数组有效?

最佳答案

我想将我的答案添加到“锅”中,也许它会使事情更清楚一些。
那么,为什么这不适用于列表和字典呢?好吧,如果你有一段这样的代码:

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

这(在 Debug模式下)转换为以下 IL 代码:
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

这反过来意味着你用一行代码( return s[0] )所做的实际上是一个三步过程:调用方法,将返回值存储在局部变量中,然后返回存储在该局部变量中的值多变的。而且,正如其他人提供的链接所指出的,通过引用返回局部变量是不可能的(除非局部变量是 ref 局部变量,但正如其他人再次指出的那样,因为 Diciotionary<TKey,TValue>List<T>没有按引用返回 API,这也不可能)。

现在,为什么它适用于数组?如果您查看如何更密切地处理数组索引(即在 IL 代码级别),您可以看到没有用于数组索引的方法调用。相反,一个特殊的操作码被添加到称为 ldelem(或它的一些变体)的代码中。像这样的代码:
 static string Test()
{
string[] s = new string[2];
return s[0];
}

在 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

当然,这看起来和字典一样,但我认为关键的区别在于这里的索引器生成一个 IL-native 调用,而不是一个属性(即方法)调用。如果您查看 MSDN here 上所有可能的 ldelem 变体,可以看到有一个叫 德莱玛它可以将元素的地址直接加载到堆中。事实上,如果你写一段这样的代码:
static ref string Test()
{
string[] s = new string[2];
return ref s[0];
}

这转化为以下 IL 代码,利用直接引用加载 ldelema 操作码:
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

所以基本上,数组索引器是不同的,在引擎盖下,数组支持通过本地 IL 调用引用评估堆栈来加载元素。自 Dictionary<TKey,TValue>和其他集合将索引器实现为属性,这会导致方法调用,只有当调用的方法明确指定 ref 返回时,它们才能执行此操作。

关于ref - 为什么我不能返回对字典值的引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48075723/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com