понедельник, 18 февраля 2013 г.

Упаковка значения в C#

Обнаружил забавный эффект при упаковке значения. Берём пример из книги "C# 2010: ускоренный курс для профессионалов" Трея Нэша:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace test03
{
    public interface IPrint
    {
        void Print();
    }

    public struct MyValue : IPrint
    {
        public int x;
        public void Print()
        {
            Console.WriteLine("{0}", x);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyValue myval = new MyValue();
            myval.x = 123;
            myval.Print();     // (1)

            IPrint printer = myval;
            myval.x = 456;
            myval.Print();     // (2)

            printer.Print();   // (3)
        }
    }
}


и вставляем в него две строчки, выделенные жирным шрифтом. Что получаем?

123
456
123

Выражения (1) и (2) работают как и ожидается. Выражение (3) вроде должно было бы также вывести 456, но выводит 123.

На мой взгляд, имеется в наличии источник трудноотлавливаемых ошибок. Значение в поле структуры было изменено, а интерфейс printer помнит старое значение, поскольку именно оно и было неявно упаковано при создании объекта-интерфейса. Теперь в программе значение (в смысле типа, т.е. struct) myval существует в двух ипостасях: со значением (в смысле содержимого) поля x, равным 123, и со значением 456. Что и подтверждает явная распаковка, которую можно добавить после (3):

            MyValue unpacked_myval = (MyValue)printer;
            unpacked_myval.Print();


Вот.

Тестировалось на MS VS 2008.