C# 向类的实例注入(INotifyPropertyChanged功能)

C# 向类的实例注入(INotifyPropertyChanged功能),c#,inotifypropertychanged,C#,Inotifypropertychanged,我有一个实现INotifyPropertyChanged的类。 我在某个viewModel中创建了一个类的实例。 是否可以从类中删除此功能,并在创建实例后将其注入?我听说ICustomTypeDescriptor可以实现这一点,但我不知道如何使用它 public class C : ICustomNotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public int

我有一个实现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));
        }
    }

这是行不通的。您可以对其进行子类化和注入,但必须更改字节码以确保调用正确的方法,而这是一个更难的方法。

如果您只是试图防止在首次创建对象并设置属性时触发通知,则可以添加布尔标志在属性设置一次之前,该值为假。仅当标志为true时才执行通知

编辑: 我认为在删除所有
INotifyPropertyChanged
代码后,没有一种干净的方法可以将功能放在那里,但是有很多方法可以从实例外部控制功能

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

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

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

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

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

主要缺点是,您需要为每个
INotifyPropertyChanged
实现创建代理,您希望实现这种条件行为

创建注册表以跟踪哪些实例应该或不应该引发通知:
公共静态类PropertyNotificationRegistry
{
静态IDictionary注册类
=新字典;
静态无效寄存器(INotifyPropertyChanged o,bool shouldNotify)
{
如果(!(_registeredClasses.ContainsKey(o))\u registeredClasses.Add(o,shouldNotify);
//还可以实现更新字典中现有类的逻辑
}
公共静态void应在属性更改时通知(此InotifyProperty更改为o)
{
登记册(o,真实);
}
当属性更改时,公共静态无效不应通知(此InotifyProperty更改为o)
{
登记册(o,虚假);
}
公共静态无效NotifyPropertyChanged(此INotifyPropertyChanged o,操作notificationAction)
{
如果(_registeredClasses.ContainsKey(o))
{
bool shouldNotify=\u registeredClasses.Where(x=>x.Key==o.Single().Value;
如果(应该通知)notificationAction();
}
}
}
使用NotificationRegistry的公共类实体:INotifyPropertyChanged
{
…//所有标准InotifyProperty更改的内容
字符串_someProperty;
公共字符串属性
{
获取{return\u someProperty;}
设置
{
if(_someProperty==value)返回;
_someProperty=值;
this.NotifyPropertyChanged(()=>OnPropertyChanged(“SomeProperty”);
}
}
}
公共类somethinginstantingourentity
{
公共无效剂量测定法()
{
var entity1=使用NotificationRegistry()的新实体;
entity1.应在属性更改时通知();
var entity2=使用NotificationRegistry()的新实体;
entity2.不应在属性更改时通知();
entity1.SomeProperty=“任意字符串”;//引发事件
entity2.SomeProperty=“任意字符串”;//不引发事件
var entity3=使用NotificationRegistry()的新实体;
entity3.SomeProperty=“任意字符串”;//不引发事件
entity3.应在属性更改时通知();
entity3.SomeProperty=“另一个任意字符串”;//现在引发事件
}
}

现在,注册表有一个明显的缺点,即它持有对每个实例的引用,并将阻止垃圾收集器拾取这些实例。通过使用
WeakReference
s实现注册表,可能会有一个解决方案,但我无法对它们的用法加以限制,以推荐特定的实现。

不完全是。我希望通过友好方式控制通知功能。有时创建实例时不需要发送通知。这就是我希望按需注入属性的原因。
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;
        }
    }
}
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
    }
}