简体   繁体   中英

INotifyPropertyChanged and Deserialization

Given:

public class MyClass : INotifyPropertyChanged
{
    public List<string> _TestFire = new List<string>();

    string _StringProp;
    public string StringProp
    {
        get
        {
            return _StringProp;
        }
        set
        {
            if (_StringProp != value)
            {
                _StringProp = value;
                RaisePropertyChanged("StringProp");
                _TestFire.Add("fired " + DateTime.Now);
            }
        }
    }
}

When you serialize and then deserialize this class, it fires the RaisePropertyChanged event, which is undesirable. Is it possible to prevent this event from being fired when the class gets deserialized?

var MyclassInstance = new MyClass() { StringProp = "None" };

MyclassInstance._TestFire.Clear(); // Clear property change history

var serobj = JsonConvert.SerializeObject();

var newitem = JsonConvert.DeserializeObject<MyClass>(serobj);

// newitem._TestFire.Count == 1, the set method was executed

Is there a way to get a bool value if the class is being deserialized? So then I could do:

        set
        {
            if (_StringProp != value)
            {
                _StringProp = value;

                if (!Deserializing)
                {
                    RaisePropertyChanged("StringProp");
                    _TestFire.Add("fired " + DateTime.Now);
                }
            }
        }

Yes, you can do what you want by implementing the OnDeserializing and OnDeserialized serialization callback methods in your class. In the OnDeserializing method, set your private _isDeserializing variable to true, and in OnDeserialized set it back to false. I would recommend doing the _isDeserializing check inside the RaisePropertyChanged method so you don't have duplicate code inside every property.

So you would end up with something like this:

public class MyClass : INotifyPropertyChanged
{
    public List<string> _TestFire = new List<string>();

    string _StringProp;
    public string StringProp
    {
        get
        {
            return _StringProp;
        }
        set
        {
            if (_StringProp != value)
            {
                _StringProp = value;
                RaisePropertyChanged("StringProp");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged(string propertyName)
    {
        // don't raise the event if the property is being changed due to deserialization    
        if (_isDeserializing) return;

        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        _TestFire.Add(propertyName + " was fired " + DateTime.Now);
    }

    bool _isDeserializing = false;

    [OnDeserializing]
    internal void OnDeserializingMethod(StreamingContext context)
    {
        _isDeserializing = true;
    }

    [OnDeserialized]
    internal void OnDeserializedMethod(StreamingContext context)
    {
        _isDeserializing = false;
    }
}

Working demo: https://dotnetfiddle.net/QkOcF4


Another way you could solve this problem is to mark your public properties with [JsonIgnore] and then mark the corresponding backing fields with [JsonProperty] , specifying the property name to use in the JSON. This would allow Json.Net to set the backing fields directly and not execute any of the mutator logic.

public class MyClass : INotifyPropertyChanged
{
    public List<string> _TestFire = new List<string>();

    [JsonProperty("StringProp")]
    string _StringProp;

    [JsonIgnore]
    public string StringProp
    {
        get
        {
            return _StringProp;
        }
        set
        {
            if (_StringProp != value)
            {
                _StringProp = value;
                RaisePropertyChanged("StringProp");
                _TestFire.Add("StringProp was fired " + DateTime.Now);
            }
        }
    }

    ...
}

Working demo: https://dotnetfiddle.net/jc7wDu

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM