【问题标题】:Injecting (INotifyPropertyChanged functionality) to an instance of an class注入(INotifyPropertyChanged 功能)到类的实例
【发布时间】:2010-03-18 11:15:36
【问题描述】:

我有一个实现 INotifyPropertyChanged 的​​类。 我在某个 viewModel 中创建了一个类的实例。 是否可以从类中删除此功能并在创建实例后将其注入?我听说 ICustomTypeDescriptor 可以实现,但我不知道如何使用它。

public class C : ICustomNotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public int _id;
    public string _name;

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

            _id = value;
            OnPropertyChanged("Id");
        }
    }

    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value)
            {
                return;
            }

            _name = value;
            OnPropertyChanged("Name");
        }
    }

    public void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }

【问题讨论】:

    标签: c# inotifypropertychanged


    【解决方案1】:

    如果您只是想防止在首次创建对象并设置属性时触发通知,您可以添加布尔标志,该标志为假,直到属性设置一次。仅当标志为真时才执行通知。

    编辑:

    我认为在删除所有 INotifyPropertyChanged 代码后,没有一种干净的方法可以在其中获取功能,但是有很多方法可以从实例外部控制功能。

    请注意,我在文本编辑器中编写了所有这些代码,而不是在 VisualStudio 中;它没有经过任何测试。

    添加启用通知的方法:

    public class OptionalNotification : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        void OnPropertyChanged(string name) ...
    
        bool _shouldNotify;
    
        public void EnableNotifications()
        {
            _shouldNotify = true;
        }
    
        string _someProperty;
        public string SomeProperty
        {
            get { return _someProperty; }
            set 
            {
                if(_someProperty == value) return
    
                _someProperty = value;
    
                if(_shouldNotify) OnPropertyChanged("SomeProperty");
            }
        }
    }
    

    如果您在实例化时知道实例是否应该产生通知,那么您可以在没有该方法的情况下做同样的事情,在这种情况下,您只需要在构造函数中添加一个布尔参数。

    另一种变体是使用工厂模式,您的工厂可以在内部访问布尔标志并在构造时设置它。

    将条件封装在代理中:

    public interface IEntity : INotifyPropertyChanged
    {
        string SomeProperty { get; set; }
    }
    
    public class Entity : IEntity
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        public void OnPropertyChanged(string name) ...
    
        string _someProperty;
        public string SomeProperty
        {
            get { return _someProperty; }
            set 
            {
                if(_someProperty == value) return
    
                _someProperty = value;
                OnPropertyChanged("SomeProperty");
            }
        }
    }
    
    public class EntityNotificationProxy : IEntity
    {
        IEntity _inner;
    
        public EntityNotificationProxy(IEntity entity)
        {
            _inner = entity;
            _inner.PropertyChanged += (o,e) => { if(ShouldNotify) OnPropertyChanged(o,e); }
        }
    
        public bool ShouldNotify { get; set; }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        void OnPropertyChanged(object sender, PropertChangedEventArgs e)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if(handler != null) handler(sender, e);
        }
    
        public string SomeProperty
        {
            get { return _inner.SomeProperty; }
            set 
            {
                if(_inner.SomeProperty == value) return
    
                _inner.SomeProperty = value;
            }
        }
    }
    

    在这里,您的消费类获取实体代理而不是实体本身(但这并不明智,因为当您对接口/抽象进行编程时,它仅引用 IEntity)。代理的包装可以在工厂中进行,也可以通过 IoC 容器/DI 框架进行。

    这种方法的主要优点是您的实体维护一个纯 INotifyPropertyChanged 实现,并且从外部处理条件方面。另一个优点是它有助于强制对抽象和控制反转进行编程。

    主要缺点是您需要为每个想要具有此条件行为的INotifyPropertyChanged 实现创建代理。

    创建一个注册表来跟踪哪些实例应该或不应该发出通知:

    public static class PropertyNotificationRegistry
    {
        static IDictionary<INotifyPropertyChanged, bool> _registeredClasses
            = new Dictionary<INotifyPropertyChanged, bool>;
    
        static void Register(INotifyPropertyChanged o, bool shouldNotify)
        {
            if(!(_registeredClasses.ContainsKey(o)) _registeredClasses.Add(o, shouldNotify);
            // could also implement logic to update an existing class in the dictionary
        }
    
        public static void ShouldNotifyWhenPropertiesChange(this INotifyPropertyChanged o)
        {
            Register(o, true);
        }
    
        public static void ShouldNotNotifyWhenPropertiesChange(this INotifyPropertyChanged o)
        {
            Register(o, false);
        }
    
        public static void NotifyPropertyChanged(this INotifyPropertyChanged o, Action notificationAction)
        {
            if(_registeredClasses.ContainsKey(o))
            {
                bool shouldNotify = _registeredClasses.Where(x => x.Key == o).Single().Value;
    
                if(shouldNotify) notificationAction();
            }
        }
    }
    
    public class EntityUsingNotificationRegistry : INotifyPropertyChanged
    {
        ... // all the standard INotifyPropertyChanged stuff
    
        string _someProperty;
        public string SomeProperty
        {
            get { return _someProperty; }
            set 
            {
                if(_someProperty == value) return;
    
                _someProperty = value;
                this.NotifyPropertyChanged(() => OnPropertyChanged("SomeProperty"));
            }
        }
    }
    
    public class SomethingInstantiatingOurEntity
    {
        public void DoSomething()
        {
            var entity1 = new EntityUsingNotificationRegistry();
            entity1.ShouldNotifyWhenPropertiesChange();
    
            var entity2 = new EntityUsingNotificationRegistry();
            entity2.ShouldNotNotifyWhenPropertiesChange();
    
            entity1.SomeProperty = "arbitrary string"; // raises event
            entity2.SomeProperty = "arbitrary string"; // does not raise event
    
            var entity3 = new EntityUsingNotificationRegistry();
            entity3.SomeProperty = "arbitrary string"; // does not raise event
            entity3.ShouldNotifyWhenPropertiesChange();
            entity3.SomeProperty = "another arbitrary string"; // now raises event
        }
    }
    

    现在,注册表有一个明显的缺点,它保存对每个实例的引用,并且会阻止这些实例被垃圾收集器拾取。可以通过使用WeakReferences 实现注册表来解决此问题,但我不知道它们的用法以推荐特定的实现。

    【讨论】:

    • 不完全是。我想动态控制通知功能。有时,创建实例并且不需要发送通知。这就是为什么我想按需注入属性。
    【解决方案2】:

    这行不通。您可以继承并注入它,但您必须更改字节码以确保正确的方法被调用 - 这是更难的方法。

    【讨论】:

      猜你喜欢
      • 2012-02-06
      • 2011-06-04
      • 2022-12-13
      • 1970-01-01
      • 2012-02-11
      • 1970-01-01
      • 2011-01-01
      • 2020-08-30
      • 2014-03-17
      相关资源
      最近更新 更多