首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在C#中,为什么dictionary[0]++可以工作?

在C#中,为什么dictionary[0]++可以工作?
EN

Stack Overflow用户
提问于 2015-02-02 09:07:26
回答 3查看 3.3K关注 0票数 22

考虑以下C#代码:

代码语言:javascript
复制
var d = new Dictionary<int, int>();
d[0] = 0;
d[0]++;

这段代码执行后,d的值是多少?我认为应该是d == 0,因为Dictionary<>的Item属性返回一个值类型 int,大概是在堆栈上,然后递增。然而,令人惊讶的是,当您实际运行这段代码时,您会发现d == 1。

上面的示例的行为就像索引器返回引用类型一样,但现在考虑以下几点:

代码语言:javascript
复制
var d = new Dictionary<int, int>();
d[0] = 0;
var a = d[0];
a++;

这段代码执行后,d的值是多少?这一次我们得到了预期的d == 0,所以索引器绝对不会返回引用。

有人知道为什么我们会看到这种行为吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-02-02 09:26:35

C#规范7.6.9 Postfix increment and decrement operators

x++或x-形式的后缀递增或递减操作的运行时处理包括以下步骤:

_

  • 如果x被分类为属性或索引器访问:_
    • 与x相关联的实例表达式(如果x不是静态的)和变元列表(如果x是索引器访问)被评估,并将结果用于随后的get和set访问器invocations.
    • The调用x的get访问器,并保存返回值。当使用运算符返回的值调用x的argument.
    • The set访问器时,所选择的运算符argument.
    • The
    • x的argument.
    • The set访问器将成为operation.

的结果

这实际上与值类型与引用类型语义无关,因为--++不应该更改实例,而是返回具有新值的新实例。

代码语言:javascript
复制
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来观察字典中的更新值。

票数 20
EN

Stack Overflow用户

发布于 2015-02-02 09:38:29

第二个示例不会像您想象的那样工作,因为int不是引用类型。当您从字典中取出值时,您就分配了一个新的变量,在本例中为a

第一个示例编译为:

代码语言:javascript
复制
d[0] = d[0] + 1;

但是,将第二个示例编译为:

代码语言:javascript
复制
int a = d[0];
a++;

来详细说明这一点。索引器的操作类似于属性,而属性是getter/setter函数。在这种情况下,d[0] =将调用索引器的setter属性。d[0] + 1将调用索引器的getter属性。

int不是引用对象。第二个例子适用于类,但不适用于整数。属性也不返回对变量的引用。如果修改索引器的返回值,那么修改的是一个全新的变量,而不是实际存储在字典中的变量。这就是为什么第一个示例能按预期工作,而第二个不能。

与第二个示例不同,第一个示例再次更新索引器。

如果你这样做的话,第二个例子和第一个一样。

代码语言:javascript
复制
int a = d[0];
a++;
d[0] = a;

这更多的是一个理解属性和索引器的概念。

代码语言:javascript
复制
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; }
    }
}

现在,对于普通数组,请考虑以下内容

代码语言:javascript
复制
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的以下几点

代码语言:javascript
复制
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的索引器

代码语言:javascript
复制
// 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);
    }
}

我认为现在应该很清楚了,为什么第一个是这样工作的,为什么第二个也是这样。

票数 2
EN

Stack Overflow用户

发布于 2015-02-02 16:42:20

您在示例中使用的Dictionary之类的泛型的伟大之处在于,使用您指定的类型参数生成专门的字典。当你声明一个Dictionary’和‘KeyValuePair’都是字面上的'int‘类型,也就是值类型。“int”值未装箱。所以..。

代码语言:javascript
复制
var d = new Dictionary<int, int>();
d[0] = 0;
d[0]++;

在功能上等同于...

代码语言:javascript
复制
int i = 0;
i++;
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28269825

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档