2015-12-17 3 views
9

Я получаю исключение null, но поле было инициализировано как пустой список. Итак, как это могло быть нулевым?Как это поле нулевое?

ошибка происходит на второй строке этого метода (на _hydratedProperties):

protected virtual void NotifyPropertyChanged<T>(Expression<Func<T>> expression) 
{ 
    string propertyName = GetPropertyName(expression); 

    if (!this._hydratedProperties.Contains(propertyName)) { this._hydratedProperties.Add(propertyName); } 
} 

И это, как декларируется поле:

public abstract class EntityBase<TSubclass> : INotifyPropertyChanged where TSubclass : class 
{ 
    private List<string> _hydratedProperties = new List<string>(); 

Это, как это установлено:

public Eta Eta 
    { 
     get { return this._eta; } 

     set 
     { 
      this._eta = value; 
      NotifyPropertyChanged(() => this.Eta); 
     } 
    } 

Это полный класс (с комментариями и отсутствующими частями):

[DataContract] 
public abstract class EntityBase<TSubclass> : INotifyPropertyChanged where TSubclass : class 
{ 
    private List<string> _hydratedProperties = new List<string>(); 

    public bool IsPropertyHydrated(string propertyName) 
    { 
     return this._hydratedProperties.Contains(propertyName); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void NotifyPropertyChanged<T>(Expression<Func<T>> expression) 
    { 
     string propertyName = GetPropertyName(expression); 

     if (!this._hydratedProperties.Contains(propertyName)) { this._hydratedProperties.Add(propertyName); } 

     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

    public string GetPropertyName<T>(Expression<Func<T>> expression) 
    { 
     MemberExpression memberExpression = (MemberExpression)expression.Body; 
     return memberExpression.Member.Name; 
    } 
} 

производный класс:

[DataContract] 
public class Bin : EntityBase<Bin> 
{ 
    private Eta _eta; 

    [DataMember] 
    public Eta Eta 
    { 
     get { return this._eta; } 

     set 
     { 
      this._eta = value; 
      NotifyPropertyChanged(() => this.Eta); 
     } 
    } 
} 
+0

Есть ли что-то, устанавливающее его в «null» в другом месте? Задайте точку останова в свойстве и сделайте условие, когда 'value == null', и посмотрите, не попал ли он когда-либо. –

+0

Не могли бы вы предоставить полный фрагмент кода для воспроизведения проблемы? –

+0

Нет, ничто не устанавливает значение null. Я просто создал это новое поле и нигде не установил его на нуль. –

ответ

12

Вот подсказка:

[DataContract] 

Угу. DataContractSerializer does not call any constructor. Вместо этого он использует FormatterServices.GetUninitializedObject для создания объекта, который будет десериализован. Это обходит вызов конструктора.

Ваш инициализатор:

private List<string> _hydratedProperties = new List<string>(); 

переводится в неявном конструктор по умолчанию компилятором.

В качестве обходного пути, вы можете использовать a deserialization callback with OnDeserializingAttribute:

[DataContract] 
public abstract class EntityBase<TSubclass> : INotifyPropertyChanged 
    where TSubclass : class 
{ 
    private List<string> _hydratedProperties; 

    protected EntityBase() 
    { 
     Init(); 
    } 

    private void Init() 
    { 
     _hydratedProperties = new List<string>() 
    } 

    [OnDeserializing] 
    private void OnDeserializing(StreamingContext context) 
    { 
     Init(); 
    } 

    // ... rest of code here 
} 
+0

Лукас: А как насчет ответа, который я только что опубликовал? Должен ли это быть приемлемым способом решения этого? –

1

Я нашел простой ответ, чем то, что при условии Лукас. Я не уверен, что на самом деле это лучше, но это просто, и это сработало. Все, что я сделал, это добавить в поле атрибут DataMember. Поскольку это указывает, что поле является частью контракта данных, оно включается в сериализацию/десериализацию и больше не вызывает ошибку нулевой ссылки.

[DataContract] 
public abstract class EntityBase<TSubclass> : INotifyPropertyChanged where TSubclass : class 
{ 
    [DataMember] 
    private List<string> _hydratedProperties = new List<string>(); 

    // More code here 
} 
+0

Это тоже работает, но список свойств, очевидно, также сериализуется. Я не знаю вашего кода, чтобы сказать, хорошо это или плохо для вас, но у него могут быть непредвиденные последствия, о которых вам следует знать: если вам нужно выполнить управление версиями и десериализовать предыдущую версию класса, вы получите старый список назад - любые свойства, которые вы ожидаете в этом списке, и которые вы добавили позже, больше не будут там, - которые * могут * сломать ваш код, в зависимости от того, что вы делаете с ним впоследствии. Это также увеличит размер полезной нагрузки с избыточными данными. Но если все в порядке с тобой, тогда обязательно, вперед. –

+0

Спасибо, Лукас. Логика, окружающая этот код, только в памяти и не сохраняется, поэтому управление версиями не является проблемой. Однако это хороший момент в отношении полезной нагрузки. –

Смежные вопросы