Implementing INotifyPropertyChanged - does a better way exist?

前端 未结 30 3192
感情败类
感情败类 2020-11-21 05:23

Microsoft should have implemented something snappy for INotifyPropertyChanged, like in the automatic properties, just specify {get; set; notify;} I

30条回答
  •  花落未央
    2020-11-21 05:52

    Let me introduce my own approach called Yappi. It belongs to Runtime proxy|derived class generators, adding new functionality to an existing object or type, like Caste Project's Dynamic Proxy.

    It allows to implement INotifyPropertyChanged once in base class, and then declare derived classes in following style, still supporting INotifyPropertyChanged for new properties:

    public class Animal:Concept
    {
        protected Animal(){}
        public virtual string Name { get; set; }
        public virtual int Age { get; set; }
    }
    

    Complexity of derived class or proxy construction can be hidden behind the following line:

    var animal = Concept.Create.New();
    

    And all INotifyPropertyChanged implementation work can be done like this:

    public class Concept:INotifyPropertyChanged
    {
        //Hide constructor
        protected Concept(){}
    
        public static class Create where TConcept:Concept
        {
            //Construct derived Type calling PropertyProxy.ConstructType
            public static readonly Type Type = PropertyProxy.ConstructType>(new Type[0], true);
            //Create constructing delegate calling Constructor.Compile
            public static Func New = Constructor.Compile>(Type);
        }
    
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
        {
            var caller = PropertyChanged;
            if(caller!=null)
            {
                caller(this, eventArgs);
            }
        }
    
        //define implementation
        public class Implementation : DefaultImplementation where TConcept:Concept
        {
            public override Func OverrideGetter(PropertyInfo property)
            {
                return PropertyImplementation.GetGetter(property.Name);
            }
            /// 
            /// Overriding property setter implementation.
            /// 
            /// Base type for implementation. TBaseType must be TConcept, and inherits all its constraints. Also TBaseType is TDeclaringType.
            /// Type, declaring property.
            /// Constructed type. TConstructedType is TDeclaringType and TBaseType.
            /// Type of property.
            /// PropertyInfo of property.
            /// Delegate, corresponding to property setter implementation.
            public override Action OverrideSetter(PropertyInfo property)
            {
                //This code called once for each declared property on derived type's initialization.
                //EventArgs instance is shared between all events for each concrete property.
                var eventArgs = new PropertyChangedEventArgs(property.Name);
                //get delegates for base calls.
                Action setter = PropertyImplementation.GetSetter(property.Name);
                Func getter = PropertyImplementation.GetGetter(property.Name);
    
                var comparer = EqualityComparer.Default;
    
                return (pthis, value) =>
                {//This code executes each time property setter is called.
                    if (comparer.Equals(value, getter(pthis))) return;
                    //base. call
                    setter(pthis, value);
                    //Directly accessing Concept's protected method.
                    pthis.OnPropertyChanged(eventArgs);
                };
            }
        }
    }
    

    It is fully safe for refactoring, uses no reflection after type construction and fast enough.

提交回复
热议问题