动态结构失败

时间:2010-11-17 05:07:23

标签: c# dynamic struct

我在使用一类结构时遇到了问题。

这是基本定义:

using System;

struct Real
{
    public double real;

    public Real(double real)
    { 
        this.real = real;
    }
}

class Record
{
    public Real r;
    public Record(double r)
    {
        this.r = new Real(r);
    }
    public void Test(double origval, double newval)
    {
        if (this.r.real == newval)
            Console.WriteLine("r = newval-test passed\n");
        else if (this.r.real == origval)
            Console.WriteLine("r = origval-test failed\n");
        else
            Console.WriteLine("r = neither-test failed\n");
    }
}

当我创建一个非动态(静态?)记录时,设置Real工作 当我创建动态记录时,设置真实不起作用 当我创建一个动态记录,取代真实的作品。

这是测试程序

class Program
{
    static void Main(string[] args)
    {
        double origval = 8.0;
        double newval = 5.0;

        // THIS WORKS - create fixed type Record, print, change value, print
        Record record1 = new Record(origval);
        record1.r.real = newval;        // change value  ***
        record1.Test(origval, newval);

        // THIS DOESN'T WORK.  change value is not making any change!
        dynamic dynrecord2 = new Record(origval);
        dynrecord2.r.real = newval;     // change value
        dynrecord2.Test(origval, newval);

        // THIS WORKS - create dynamic type Record, print, change value, print
        dynamic dynrecord3 = new Record(origval);
        dynamic r = dynrecord3.r;       // copy out value
        r.real = newval;                // change copy
        dynrecord3.r = r;               // copy in modified value
        dynrecord3.Test(origval, newval);

    }
}

这是输出: r = newval-test通过 r = origval-test失败 r = newval-test传递

当我将struct Real更改为Real类时,所有三种情况都有效。

那是怎么回事?
谢谢,
最大

3 个答案:

答案 0 :(得分:1)

就核心CLI而言,

dynamic对于object来说确实是一个奇特的词,因此您正在改变一个盒装副本。这很容易发疯。首先突变结构 确实非常容易出错。我只是简单地使结构不可变 - 否则你将一遍又一遍地得到

答案 1 :(得分:0)

我深入研究了这个问题。以下是微软Mads Torgersen的回答。


来自Mads:

这有点不幸但是设计不合适。在

dynrecord2.r.real = newval; // change value

dynrecord2.r的值被装箱,这意味着复制到它自己的堆对象中。该副本是经过修改的副本,而不是您随后测试的副本。

这是C#动态工作的非常“本地”方式的结果。想想上面的陈述 - 我们可以通过两种基本方式来攻击它:

1)在编译时实现动态正在发生的事情,并且基本上将整个语句移动到运行时绑定

2)当它们的成分是动态的时,在运行时绑定各个操作,返回动态的东西,这可能反过来导致事物在运行时绑定

在C#中,我们使用了后者,这非常合成,并且可以很容易地根据类型系统来描述动态,但是有一些缺点 - 例如拳击结果值类型。

所以你所看到的是这种设计选择的结果。


我又看了一眼MSIL。它基本上需要

dynrecord2.r.real = newval;

并将其变为:

Real temp = dynrecord2.r;
temp.real = newval;

如果dynrecord2.r是一个类,它只是复制句柄,因此更改会影响内部字段。如果dynrecord2.r是结构,则会生成副本,并且更改不会影响原始结构。

我将由读者决定这是一个错误还是一个功能。

最高

答案 2 :(得分:0)

让你的结构不可变,你就不会有问题。

struct Real
{
    private double real;
    public double Real{get{return real;}}

    public Real(double real)
    { 
        this.real = real;
    }
}

可变结构在本机互操作或某些高性能场景中非常有用,但是您可以更好地了解自己在做什么。