c#对象,其中包含ValueChanged事件,该事件在每个属性更改时触发

时间:2013-09-26 17:00:41

标签: c# events event-handling

创建具有在更改其某个属性时触发的事件的类的最佳方法是什么?具体来说,您如何向任何订户传达哪些财产已被更改?

例如:

public class ValueChangedPublisher
{
    private int _prop1;
    private string _prop2;

    public static event ValueChangedHandler(/*some parameters?*/);

    public int Prop1
    {
        get { return _prop1; }
        set
        {
            if (_prop1 != value)
            {
                _prop1 = value;
                ValueChangedHandler(/*parameters?*/);
            }
        }
    }

    public string Prop2
    {
        get { return _prop2; }
        set
        {
            if (_prop2 != value)
            {
                _prop2 = value;
                ValueChangedHandler(/*parameters?*/);
            }
        }
    }
}

public class ValueChangedSubscriber
{
    private int _prop1;
    private string _prop2;

    public ValueChangedSubscriber()
    {
        ValueChangedPublisher.ValueChanged += ValueChanged;
    }

    private void ValueChanged(/*parameters?*/)
    {
        /*how does the subscriber know which property was changed?*/
    }
}

我的目标是尽可能地扩展它(例如,我不想要一堆巨大的if / else if / switch语句笨拙)。有没有人知道要实现我正在寻找的技术?

修改真正寻找的是如何在订阅方使用INotifyPropertyChanged模式。我不想这样做:

private void ValueChanged(string propertyName)
    {
        switch(propertyName)
        {
            case "Prop1":
                _prop1 = _valueChangedPublisher.Prop1;
                break;

            case "Prop2":
                _prop2 = _valueChangedPublisher.Prop2;
                break;

            // the more properties that are added to the publisher, the more cases I
            // have to handle here :/ I don't want to have to do it this way
        }
    }

2 个答案:

答案 0 :(得分:1)

.NET提供INotifyPropertyChanged interface。继承并实现它:

public class ValueChangedPublisher : INotifyPropertyChanged
{
    private int _prop1;
    private string _prop2;

    public event PropertyChangedEventHandler ValueChangedHandler;

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public int Prop1
    {
        get { return _prop1; }
        set
        {
            if (_prop1 != value)
            {
                _prop1 = value;
                NotifyPropertyChanged();
            }
        }
    }

    public string Prop2
    {
        get { return _prop2; }
        set
        {
            if (_prop2 != value)
            {
                _prop2 = value;
                NotifyPropertyChanged();
            }
        }
    }
}

答案 1 :(得分:0)

以下是我为解决问题所做的工作。它有点大,所以可能没有高性能,但对我有用。

修改

有关效果详情,请参阅此问题:C# using properties with value types with Delegate.CreateDelegate

用于发布属性更改内容的基类:

public abstract class PropertyChangePublisherBase : INotifyPropertyChanged
{
    private Dictionary<string, PropertyInfo> _properties;
    private bool _cacheProperties;

    public bool CacheProperties
    {
        get { return _cacheProperties; }
        set
        {
            _cacheProperties = value;

            if (_cacheProperties && _properties == null)
                _properties = new Dictionary<string, PropertyInfo>();
        }
    }

    protected PropertyChangePublisherBase(bool cacheProperties)
    {
        CacheProperties = cacheProperties;
    }

    public bool ContainsBinding(PropertyChangedEventHandler handler)
    {
        if (PropertyChanged == null)
            return false;

        return PropertyChanged.GetInvocationList().Contains(handler);
    }

    public object GetPropertValue(string propertyName)
    {
        if (String.IsNullOrEmpty(propertyName) || String.IsNullOrWhiteSpace(propertyName))
            throw new ArgumentException("Argument must be the name of a property of the current instance.", "propertyName");

        return ProcessGetPropertyValue(propertyName);
    }

    protected virtual object ProcessGetPropertyValue(string propertyName)
    {
        if (_cacheProperties)
        {
            if (_properties.ContainsKey(propertyName))
            {
                return _properties[propertyName].GetValue(this, null);
            }

            else
            {
                var property = GetType().GetProperty(propertyName);
                _properties.Add(propertyName, property);
                return property.GetValue(this, null);
            }
        }

        else
        {
            var property = GetType().GetProperty(propertyName);
            return property.GetValue(this, null);
        }
    }

    #region INotifyPropertyChanged Implementation

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;

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

    #endregion
}

用于接收属性更改内容的基类:

public abstract class PropertyChangeSubscriberBase
{
    protected readonly string _propertyName;
    protected virtual object Value { get; set; }

    protected PropertyChangeSubscriberBase(string propertyName, PropertyChangePublisherBase bindingPublisher)
    {
        _propertyName = propertyName;

        AddBinding(propertyName, this, bindingPublisher);
    }

    ~PropertyChangeSubscriberBase()
    {
        RemoveBinding(_propertyName);
    }

    public void Unbind()
    {
        RemoveBinding(_propertyName);
    }

    #region Static Fields

    private static List<string> _bindingNames = new List<string>();
    private static List<PropertyChangeSubscriberBase> _subscribers = new List<PropertyChangeSubscriberBase>();
    private static List<PropertyChangePublisherBase> _publishers = new List<PropertyChangePublisherBase>();

    #endregion

    #region Static Methods

    private static void PropertyChanged(object sender, PropertyChangedEventArgs args)
    {
        string propertyName = args.PropertyName;

        if (_bindingNames.Contains(propertyName))
        {
            int i = _bindingNames.IndexOf(propertyName);
            var publisher = _publishers[i];
            var subscriber = _subscribers[i];

            subscriber.Value = publisher.GetPropertValue(propertyName);
        }
    }

    public static void AddBinding(string propertyName, PropertyChangeSubscriberBase subscriber, PropertyChangePublisherBase publisher)
    {
        if (!_bindingNames.Contains(propertyName))
        {
            _bindingNames.Add(propertyName);
            _publishers.Add(publisher);
            _subscribers.Add(subscriber);

            if (!publisher.ContainsBinding(PropertyChanged))
                publisher.PropertyChanged += PropertyChanged;
        }
    }

    public static void RemoveBinding(string propertyName)
    {
        if (_bindingNames.Contains(propertyName))
        {
            int i = _bindingNames.IndexOf(propertyName);
            var publisher = _publishers[i];

            _bindingNames.RemoveAt(i);
            _publishers.RemoveAt(i);
            _subscribers.RemoveAt(i);

            if (!_publishers.Contains(publisher))
                publisher.PropertyChanged -= PropertyChanged;
        }
    }

    #endregion
}

用于订阅属性更改内容的实际类:

public sealed class PropertyChangeSubscriber<T> : PropertyChangeSubscriberBase
{
    private PropertyChangePublisherBase _publisher;

    public new T Value
    {
        get
        {
            if (base.Value == null)
                return default(T);

            if (base.Value.GetType() != typeof(T))
                throw new InvalidOperationException(String.Format("Property {0} on object of type {1} does not match the type Generic type specified {2}.", _propertyName, _publisher.GetType(), typeof(T)));

            return (T)base.Value;
        }

        set { base.Value = value; }
    }

    public PropertyChangeSubscriber(string propertyName, PropertyChangePublisherBase bindingPublisher)
        : base(propertyName, bindingPublisher)
    {
        _publisher = bindingPublisher;
    }
}

以下是您希望收到有关通知属性的类的示例:

public class ExamplePublisher: PropertyChangedPublisherBase
{
    private string _id;
    private bool _testBool;

    public string Id
    {
        get { return _id; }
        set
        {
            if (value == _id) return;
            _id = value;

            RaisePropertyChanged("Id");
        }
    }

    public bool TestBool
    {
        get { return _testBool; }
        set
        {
            if (value.Equals(_testBool)) return;
            _testBool = value;

            RaisePropertyChanged("TestBool");
        }
    }
}

以下是当上述类中的属性发生更改时将通知的类的示例:

public class ExampleReceiver
{
    public PropertyChangeSubscriber<string> Id { get; set; }
    public PropertyChangeSubscriber<bool> TestBool { get; set; }

    public MyExampleClass(PropertyChangePublisherBase publisher)
    {
        Id = new PropertyChangeSubscriber<string>("Id", publisher);
        TestBool = new PropertyChangeSubscriber<bool>("TestBool", publisher);
    }
}