0

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

前任:

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

2 回答 2

1

.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();
            }
        }
    }
}
于 2013-09-26T17:05:01.040 回答
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);
    }
}
于 2013-12-09T21:43:51.037 回答