考虑以下C#代码:
var d = new Dictionary<int, int>();
d[0] = 0;
d[0]++;
这段代码执行后,d的值是多少?我认为应该是d == 0,因为Dictionary<>的Item属性返回一个值类型 int
,大概是在堆栈上,然后递增。然而,令人惊讶的是,当您实际运行这段代码时,您会发现d == 1。
上面的示例的行为就像索引器返回引用类型一样,但现在考虑以下几点:
var d = new Dictionary<int, int>();
d[0] = 0;
var a = d[0];
a++;
这段代码执行后,d的值是多少?这一次我们得到了预期的d == 0,所以索引器绝对不会返回引用。
有人知道为什么我们会看到这种行为吗?
发布于 2015-02-02 09:26:35
C#规范7.6.9 Postfix increment and decrement operators
x++或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);
}
}
}
这两个测试方法将打印相同的字符串0:1:2:3
。正如您所看到的,即使使用引用类型,您也必须调用set accessor来观察字典中的更新值。
发布于 2015-02-02 09:38:29
第二个示例不会像您想象的那样工作,因为int不是引用类型。当您从字典中取出值时,您就分配了一个新的变量,在本例中为a
。
第一个示例编译为:
d[0] = d[0] + 1;
但是,将第二个示例编译为:
int a = d[0];
a++;
来详细说明这一点。索引器的操作类似于属性,而属性是getter/setter函数。在这种情况下,d[0] =
将调用索引器的setter属性。d[0] + 1
将调用索引器的getter属性。
int不是引用对象。第二个例子适用于类,但不适用于整数。属性也不返回对变量的引用。如果修改索引器的返回值,那么修改的是一个全新的变量,而不是实际存储在字典中的变量。这就是为什么第一个示例能按预期工作,而第二个不能。
与第二个示例不同,第一个示例再次更新索引器。
如果你这样做的话,第二个例子和第一个一样。
int a = d[0];
a++;
d[0] = a;
这更多的是一个理解属性和索引器的概念。
class MyArray<T>
{
private T[] array;
public MyArray(T[] _array)
{
array = _array;
}
public T this[int i]
{
get { return array[i];
set { array[i] = value; }
}
}
现在,对于普通数组,请考虑以下内容
int[] myArray = new int[] { 0, 1, 2, 3, 4 };
int a = myArray[2]; // index 2 is 1
// a is now 1
a++; // a is now 2
// myArray[2] is still 1
上面的代码是这样的,因为int不是引用类型,索引器不返回引用,因为它不是由ref传递的,而是作为普通函数的返回值。
现在考虑MyArray的以下几点
var myArray = new MyArray<int>(new int[] { 0, 1, 2, 3, 4 });
int a = myArray[2]; // index 2 is 1
// a is now 1
a++; // a is now 2
// myArray[2] is still 1
您期望myArray2创建一个对myArray2的引用吗?你不应该这么做。Dictionary
也是如此
这是Dictionary
的索引器
// System.Collections.Generic.Dictionary<TKey, TValue>
[__DynamicallyInvokable]
public TValue this[TKey key]
{
[__DynamicallyInvokable]
get
{
int num = this.FindEntry(key);
if (num >= 0)
{
return this.entries[num].value;
}
ThrowHelper.ThrowKeyNotFoundException();
return default(TValue);
}
[__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
set
{
this.Insert(key, value, false);
}
}
我认为现在应该很清楚了,为什么第一个是这样工作的,为什么第二个也是这样。
发布于 2015-02-02 16:42:20
您在示例中使用的Dictionary之类的泛型的伟大之处在于,使用您指定的类型参数生成专门的字典。当你声明一个Dictionary’和‘KeyValuePair’都是字面上的'int‘类型,也就是值类型。“int”值未装箱。所以..。
var d = new Dictionary<int, int>();
d[0] = 0;
d[0]++;
在功能上等同于...
int i = 0;
i++;
https://stackoverflow.com/questions/28269825
复制相似问题