Mik*_*ike 20 .net c# dictionary
考虑以下C#代码:
var d = new Dictionary<int, int>();
d[0] = 0;
d[0]++;
Run Code Online (Sandbox Code Playgroud)
执行此代码后d [0]的值是多少?我希望d [0] == 0,因为Dictionary <>的Item属性返回一个值类型 int,可能在堆栈上,然后递增.然而,令人惊讶的是,当您实际运行此代码时,您会发现d [0] == 1.
上面的示例表现得好像索引器正在返回引用类型,但现在考虑以下内容:
var d = new Dictionary<int, int>();
d[0] = 0;
var a = d[0];
a++;
Run Code Online (Sandbox Code Playgroud)
执行此代码后d [0]的值是多少?这次我们按预期得到d [0] == 0,所以索引器肯定没有返回引用.
有谁知道为什么我们看到这种行为?
use*_*407 19
C#规范7.6.9后缀增量和减量运算符:
x ++或x--形式的后缀增量或减量操作的运行时处理包括以下步骤:
- 如果x被归类为属性或索引器访问:
- 将评估实例表达式(如果x不是静态的)和与x关联的参数列表(如果x是索引器访问),并将结果用于后续的get和set访问器调用.
- 调用x的get访问器并保存返回的值.
- 调用所选运算符,并将保存的值x作为其参数.
- 使用运算符返回的值作为其value参数调用x的set访问器.
- x的保存值成为操作的结果.
这实际上与值类型与引用类型语义无关,因为--并且++不应该更改实例但返回具有新值的新实例.
public static class Test {
public static void Main() {
TestReferenceType();
TestValueType();
}
public static void TestReferenceType() {
var d=new Dictionary<int,BoxedInt>();
BoxedInt a=0;
d[0]=a;
d[0]++;
d[1]=2;
BoxedInt b=d[1];
b++;
Console.WriteLine("{0}:{1}:{2}:{3}",a,d[0],d[1],b);
}
public static void TestValueType() {
var d=new Dictionary<int,int>();
int a=0;
d[0]=a;
d[0]++;
d[1]=2;
int b=d[1];
b++;
Console.WriteLine("{0}:{1}:{2}:{3}",a,d[0],d[1],b);
}
public class BoxedInt {
public int Value;
public BoxedInt(int value) {
Value=value;
}
public override string ToString() {
return Value.ToString();
}
public static implicit operator BoxedInt(int value) {
return new BoxedInt(value);
}
public static BoxedInt operator++(BoxedInt value) {
return new BoxedInt(value.Value+1);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这两种测试方法将打印相同的字符串0:1:2:3.如您所见,即使使用引用类型,您也必须调用set accessor来观察字典中的更新值.
您的代码以这种方式工作,因为的索引d返回值 参考的int(这是值类型)。您的代码基本上与此相同:
var d = new Dictionary<int, int>();
d[0] = 0;
d[0] = d[0] + 1; // 1. Access the indexer of `d`
// 2. Increment it (returned another int, which is of course a value type)
// 3. Store the new int to d[0] again (d[0] now equals to 1)
Run Code Online (Sandbox Code Playgroud)
d[0]0在第二个代码示例中返回的原因是由于值类型的语义,特别是以下行:
var a = d[0]; // a is copied by value, not a **reference** to d[0],
// so they are two separate integers from here
a++; // a is incremented, but d[0] is not, because they are **two** separate integers
Run Code Online (Sandbox Code Playgroud)
我发现Jon Skeet 对引用类型和值类型的区别的解释非常有帮助。