Как переменная может иметь два значения одновременно?

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

Я использую С#, .Net 4.0. Код большой, слишком большой, чтобы вставить все это здесь, но я пытаюсь объяснить, что происходит, в надежде, что найдется кто-нибудь знающий, у кого будут какие-то мысли.

В моем стеке вызовов у меня есть ряд универсальных методов, и я заметил, что, хотя значение должно быть просто передано от одного к другому, оно меняется. Хорошо, это было мое первое впечатление. Позже мне удалось выделить один проблематичный метод, в котором при остановке я вижу два разных значения одного и того же свойства объекта.

public class Sample : BaseClass, ISomeInterface
{
    [XmlIgnore]
    public new Guid Id { get; set; }
} 

Что может быть важно, что и BaseClass, и ISomeINterface определяют

public Guid Id

так что теперь, когда я останавливаюсь в этом универсальном методе и смотрю переменную data типа Sample, я могу развернуть ее свойства и увидеть первое значение Id . Но когда я смотрю data.Id, он показывает другое значение. Посмотрите сами.

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

Редактировать: я отправил его сюда http://picturepush.com/public/7307446

Кто-нибудь знает, в чем разница в том, как получаются эти значения в окне Watch? В чем разница? Я пробовал много разных подходов, приведение, использование отражения, но всегда получаю то же значение, что и при просмотре data.Id, и по иронии судьбы правильное значение, которое я ожидаю, является другим, неуловимым.

О, и нет, это не домашнее задание ;)


person michal    schedule 09.01.2012    source источник
comment
Загрузите картинку куда-нибудь, пожалуйста.   -  person gdoron is supporting Monica    schedule 09.01.2012
comment
Как выглядит базовый класс? Когда вы проверяете значение, является ли экземпляр класса Sample приведенным как Sample Class или BaseClass?   -  person Jesper Fyhr Knudsen    schedule 09.01.2012
comment
Украшено ли свойство атрибутом DebuggerDisplayAttribute? Вы можете отобразить значение, отличное от того, что вы видите, когда расширяете объект в отладчике.   -  person Razor    schedule 09.01.2012
comment
Изображение загружено ссылка. Я безуспешно пытался выполнить приведение ко всем возможным типам, значение всегда одно и то же. Свойство не украшено атрибутом DebuggigerDisplayAttribute. Но еще раз просмотрев код, я заметил, что ValueInjector использовался ранее для копирования значений из другого объекта. Может ли это быть побочным эффектом?   -  person michal    schedule 09.01.2012


Ответы (3)


Ключевое слово new скрывает член базового класса.

Установите свойство Id в BaseClass на виртуальное:

public class BaseClass : ISomeInterface
{
    public virtual Guid Id { get; set; }
}

И переопределите его в Sample:

public class Sample : BaseClass, ISomeInterface
{
    public override Guid Id { get; set; }
}
person lukiffer    schedule 09.01.2012
comment
Это решило это. Я могу понять, как скрытие может сбивать с толку разработчиков, но оно существует по какой-то причине, иногда вам нужно определить (или переопределить) свойство. Но здесь похоже, что отладчик/компилятор/.Net_framework тоже перепутали, но «особенность» языка;) - person michal; 09.01.2012

Когда вы определяете Id как в базовом, так и в реальном классе, второе определение не переопределяет первое, а скрывает его. Это означает, что класс Sample фактически содержит оба определения и зависит от статического типа переменной, к которой вы обращаетесь.

Следовательно:

Sample s = new Sample();
BaseClass b = (BaseClass)s;
s.Id != b.Id;

Как правило, это не то, что вам нужно, и может привести к довольно странному поведению, как вы уже выяснили :)

Простой компилируемый пример, демонстрирующий это:

class A {
    public int val = 5;

    public static void Main(string[] args) {
        B b = new B();
        A a = (A)b;
        Console.WriteLine(a.val); // 5
        Console.WriteLine(b.val); // 10
    }
}

class B : A {
    public int val = 10;
}

Если у вас уже есть этот беспорядок и вы не можете его правильно исправить, вы можете привести статический тип к любой версии, к которой вы хотите получить доступ.

person Voo    schedule 09.01.2012
comment
Я думал об этом и безрезультатно пытался использовать все типы в иерархии. В вашем примере две переменные явно относятся к двум разным типам. Пожалуйста, посмотрите на изображение, которое я загрузил, я говорю об одной переменной одного типа в один и тот же момент времени. - person michal; 09.01.2012

Не определяйте свойство Id в классе Sample. Просто унаследуйте его от BaseClass. Приведенный выше код должен выдавать предупреждения компилятора о том, что Sample.Id скрывает BaseClass.Id.

person Andrew Cooper    schedule 09.01.2012
comment
Перед этим идентификатором есть новый. Мне нужно, чтобы он был там, чтобы иметь возможность украсить его атрибутами, которых нет в базовом классе. - person michal; 09.01.2012
comment
Ты прав. Я пропустил ключевое слово new. Однако это источник вашей проблемы. Если ваш код имеет ссылку типа BaseClass, вы увидите идентификатор, определенный в BaseClass. Если ссылка имеет тип Sample, вы увидите новую. Если вы не можете изменить базовый класс, вам нужно убедиться, что вы имеете дело со ссылкой типа Sample всякий раз, когда вы его используете. - person Andrew Cooper; 09.01.2012
comment
Спасибо за ваш вклад. Конечно, сокрытие было источником проблемы, но концепция как таковая хорошо известна (мне). Настоящая проблема и источник путаницы заключались в том, почему это происходило только в одном методе и почему отладчик показывал два значения одновременно. Во всяком случае, теперь это решено. Помогло переопределение, а не сокрытие. - person michal; 09.01.2012