Динамические Структуры терпят неудачу - PullRequest
3 голосов
/ 17 ноября 2010

У меня проблема с использованием класса made of конструкций.

Вот базовое определение:

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 работает.
Когда я создаю динамическую запись, настройка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 r = неудачный тест origval r = пройденный тест newval

Я изменяю структуру Real на класс Real, все три случая работают.

Так что происходит?
Спасибо,
Макс

Ответы [ 3 ]

1 голос
/ 17 ноября 2010

dynamic действительно причудливое слово для object, что касается основного CLI, поэтому вы изменяете коробочную копию.Это склонно к сумасшествию.Мутирование структуры , во-первых, действительно, очень подвержено ошибкам.Я бы просто сделал структуру неизменной - иначе вы получите это снова и снова .

0 голосов
/ 19 ноября 2010

Сделайте вашу структуру неизменной, и у вас не будет проблем.

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

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

Изменяемые структуры могут быть полезны в нативном взаимодействии или в некоторых сценариях с высокой производительностью, но тогда вы лучше знаете, что делаете.

0 голосов
/ 19 ноября 2010

Я углубился в эту проблему. Вот ответ от Мэдс Торгерсен из Microsoft.


От Мадс:

Это немного прискорбно, но по замыслу. В

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

Значение dynrecord2.r упаковывается, что означает копирование в его собственный объект кучи. Эта копия модифицируется, а не оригинал, который вы впоследствии тестируете.

Это является следствием самого «локального» поведения C # dynamic. Подумайте об утверждении, подобном приведенному выше - есть два основных способа, которыми мы могли бы атаковать это:

1) Понимать во время компиляции, что происходит что-то динамическое, и по существу перемещать весь оператор, чтобы быть связанным во время выполнения

2) Связывать отдельные операции во время выполнения, когда их составляющие являются динамическими, возвращая что-то динамическое, что, в свою очередь, может привести к тому, что вещи будут связаны во время выполнения

В C # мы пошли с последним, который имеет приятную композицию и позволяет легко описывать динамику в терминах системы типов, но имеет некоторые недостатки - такие как, например, упаковка результирующих типов значений.

Итак, то, что вы видите, является результатом этого выбора дизайна.


Я еще раз взглянул на MSIL. По сути это занимает

dynrecord2.r.real = newval;

и превращает его в:

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

Если dynrecord2.r является классом, он просто копирует дескриптор, чтобы изменение влияло на внутреннее поле. Если dynrecord2.r является структурой, копия создается, и изменение не влияет на оригинал.

Я оставлю это на усмотрение читателя, чтобы решить, является ли это ошибкой или функцией.

Макс

...