具有 ValueChanged 事件的 c# 对象,该事件在每个属性发生更改时触发



创建具有在其属性之一更改时触发的事件的类的最佳方法是什么? 具体来说,您如何向任何订阅者传达哪个属性发生了变化?

前任:

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
        }
    }
.

NET 提供了INotifyPropertyChanged接口。继承并实现它:

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();
            }
        }
    }
}

这是我为解决我的问题所做的。它有点大,所以可能性能不佳,但对我有用。

编辑

有关性能详细信息,请参阅此问题:C# 将具有值类型的属性与 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);
    }
}

相关内容

  • 没有找到相关文章