9

I'm getting a null exception, but the field was initialized as an empty list. So how could it be null?

The error occurs on the second line in this method (on _hydratedProperties):

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

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

And this is how the field is declared:

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

This is how it's set:

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

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

This is the full class (with the comments and non-relevant parts removed):

[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;
    }
}

Derived class:

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

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

        set
        {
            this._eta = value;
            NotifyPropertyChanged(() => this.Eta);
        }
    }
}
Bob Horn
  • 33,387
  • 34
  • 113
  • 219
  • Is something setting it to `null` somewhere else? Set a breakpoint in the property and make the condition when `value == null` and see if it ever hits. – Ron Beyer Dec 17 '15 at 16:49
  • Could you provide a complete chunk of code to reproduce the issue? – Leonardo Spina Dec 17 '15 at 16:50
  • No, nothing is setting it to null. I just created this new field and haven't set it to null anywhere. – Bob Horn Dec 17 '15 at 16:50
  • 3
    Possible duplicate of [What is a NullReferenceException and how do I fix it?](http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) – Grundy Dec 17 '15 at 16:51
  • 1
    @BobHorn Are you by chance writing to the list from multiple threads? If so you may need to implement locks or use threadsafe collections – Jonathan Carroll Dec 17 '15 at 16:52
  • can you provide sample class with this property and how you use it? – Grundy Dec 17 '15 at 16:53
  • Check if propertyName = null. before the if statement. and also you may put a breakpoint before that and check if any of those variables are null – The Javatar Dec 17 '15 at 16:53
  • @JonathanCarroll, null not a _propertyName_, null a _hydratedProperties_ – Grundy Dec 17 '15 at 16:54
  • 4
    We really need the exact error message (including any variables mentioned) and a representative code sample that reproduces the bug. – willaien Dec 17 '15 at 16:54
  • 1
    I think he knows what a null exception is, and by now he knows what is coming null is _hidratedProperties. He needs to know why. From using notify property changed, might be in a data binding situation. Maybe a thread issue, hard to say. – Ernesto Dec 17 '15 at 16:55
  • 2
    @Grundy this is not a duplicate. This question is asking why is IT throwing a `NullReferenceException` not what IS a `NullReferenceException`... – IronGeek Dec 17 '15 at 16:56
  • 1
    @IronGeek, in duplicated question explain why it can happens and how it can be solved – Grundy Dec 17 '15 at 16:58
  • I added the full class. Does that help? My concern is that the binding is causing a property change notification before the base class is fully initialized, if that's even possible. My other concern is that I could check for null and initialize it and add my value to it, but would that value get wiped out when it's eventually initialized? – Bob Horn Dec 17 '15 at 16:58
  • 1
    can you add also sample derived class, and sample how you create it? – Grundy Dec 17 '15 at 17:00
  • Sure. When I run the code, I hit the NotifyPropertyChanged() method many times before the actual field is initialized. – Bob Horn Dec 17 '15 at 17:02
  • To me it looks like the problem is in the way you initialize the field. Try to initialize it in a constructor and see if it helps – Tanuki Dec 17 '15 at 17:05
  • I actually found another way to solve this issue. See my answer. If anyone has some feedback on it, that would be great. I'm especially curious to know if it should be considered *the* answer to this post. – Bob Horn Dec 17 '15 at 20:52

2 Answers2

12

Here's the clue:

[DataContract]

Yup. DataContractSerializer does not call any constructor. Instead, it uses FormatterServices.GetUninitializedObject to create the object that will be deserialized. This bypasses the constructor call.

Your initializer:

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

is translated to an implicit default constructor by the compiler.

As a workaround, you can use 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
}
Community
  • 1
  • 1
Lucas Trzesniewski
  • 50,214
  • 11
  • 107
  • 158
  • Lucas: What about the answer I just posted? Should that be an acceptable way to solve this as well? – Bob Horn Dec 17 '15 at 20:53
1

I found a simpler answer than what Lucas provided. I'm not sure if this one is actually better, but it's simple and it worked. All I did was add the DataMember attribute to the field. Since that specifies that the field is part of the data contract, it is included with serialization/deserialization and no longer causes a null reference error.

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

    // More code here
}
Bob Horn
  • 33,387
  • 34
  • 113
  • 219
  • That works too, but the property list obviously gets serialized too. I don't know your code to tell if that's good or bad for you, but it may have unintended consequences you should be aware of: if you need to do versioning and you deserialize a previous version of the class, you'll get the old list back - any properties you expect to be in that list and that you added later on won't be there anymore - which *may* break your code, depending on what you're doing with it afterwards. It'll also increase the payload size with redundant data. But if all of this is OK to you, then sure, go ahead. – Lucas Trzesniewski Dec 17 '15 at 21:04
  • Thanks, Lucas. The logic surrounding this code is only in memory and not persisted, so versioning isn't an issue. That's a good point about the payload though. – Bob Horn Dec 17 '15 at 21:10