Hello World - Yappi style

Almost any C# programmer sometimes wrote code like this:

    public class Animal:INotifyPropertyChanged
    {
        private string _name;
        private int _age;
        
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name == value) return;
                _name = value;
                OnPropertyChanged("Name");
            }
        }
 
        public int Age
        {
            get { return _age; }
            set
            {
                if (_age == value) return;
                _age = value;
                OnPropertyChanged("Age");
            }
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
        
        protected void OnPropertyChanged(string name)
        {
            var caller = PropertyChanged;
            if(caller!=null)
            {
                caller(this, new PropertyChangedEventArgs(name));
            }
        }
    }

Is it annoying? Is it hell for refactoring? The answer is Yes in both cases.

There is lot of solutions for that problem. Here is some of them:

and Yappi is another one.

Solution in Yappi-style looks like this:

    public class Animal:INotifyPropertyChanged
    {
        //Hide constructor
        protected Animal(){}
        //Construct derived Type
        public static readonly Type Type = PropertyProxy.ConstructType<Animal, Implementation>(new Type[0], true);
        //Create constructing delegate
        public static readonly Func<Animal> New = Constructor.Compile<Func<Animal>>(Type);
 
        public virtual string Name { get; set; }
 
        public virtual int Age { get; set; }
 
        public event PropertyChangedEventHandler PropertyChanged;
        
        protected void OnPropertyChanged(string name)
        {
            var caller = PropertyChanged;
            if(caller!=null)
            {
                caller(this, new PropertyChangedEventArgs(name));
            }
        }
 
        //define implementation
        public class Implementation : DefaultImplementation<Animal>
        {
            public override Func<TBaseType, TResult> OverrideGetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                return PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(property.Name);
            }
 
            public override Action<TBaseType, TResult> OverrideSetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                var propertyName = property.Name;
                var setter = PropertyImplementation<TBaseType, TDeclaringType>.GetSetter<TResult>(propertyName);
                var getter = PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(propertyName);
                var comparer = EqualityComparer<TResult>.Default;
 
                return (pthis, value) =>
                {
                    if (comparer.Equals(value, getter(pthis))) return;
                    setter(pthis, value);
                    pthis.OnPropertyChanged(propertyName);
                };
            }
        }
    }

Looks complex, but really it’s simple.

Implementation consists of simple steps:

  1. Make properties virtual
  2. Make constructors protected
  3. Define Implementation class, derived from DefaultImplementation
  4. Create derived proxy type and store it in public static readonly field
  5. Create constructor-replacing delegate and store into public static readonly field

Using Yappi, you have no need to write additional code on each property. Just declare some virtual auto properties.

Does it looks too complex? Yes, in some cases, but you may implement INotifyPropertyChanged in base class like this:

    public class Concept:INotifyPropertyChanged
    {
        //Hide constructor
        protected Concept(){}
 
 
        public static class Create<TConcept> where TConcept:Concept
        {
            //Construct derived Type
            public static readonly Type Type = PropertyProxy.ConstructType<TConcept, Implementation<TConcept>>(new Type[0], true);
            //Create constructing delegate
            public static Func<TConcept> New = Constructor.Compile<Func<TConcept>>(Type);
        }
 
 
        public event PropertyChangedEventHandler PropertyChanged;
        
        protected void OnPropertyChanged(string name)
        {
            var caller = PropertyChanged;
            if(caller!=null)
            {
                caller(this, new PropertyChangedEventArgs(name));
            }
        }
 
        //define implementation
        public class Implementation<TConcept> : DefaultImplementation<TConcept> where TConcept:Concept
        {
            public override Func<TBaseType, TResult> OverrideGetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                return PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(property.Name);
            }
 
            public override Action<TBaseType, TResult> OverrideSetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                var propertyName = property.Name;
                var setter = PropertyImplementation<TBaseType, TDeclaringType>.GetSetter<TResult>(propertyName);
                var getter = PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(propertyName);
                var comparer = EqualityComparer<TResult>.Default;
 
                return (pthis, value) =>
                {
                    if (comparer.Equals(value, getter(pthis))) return;
                    setter(pthis, value);
                    pthis.OnPropertyChanged(propertyName);
                };
            }
        }
    }

Moving this way will allow you to create any number of derived concepts in pure declarative style, like:

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

and only thing you should remember then, is way of constructing new instances:

Concept.Create<Animal>.New();

Also you can specify concepts as interfaces instead of classes.

Why is Yappi better?

It’s the main question. The answers is:

  • Yappi supports any automatic refactoring (renaming of methods, adding|removing properties)
  • Yappi-implemented properties almost as fast as handwritten code
  • Yappi allows to implement property accessors without any reflection, by providing useful generic parameters
  • Yappi can help you to create any custom property implementation, not just INPC (like in MS Entity Framework 4)
  • All done at run time (if you need it).

 

You interested?

Try latest Yappi release or read some more about Yappi.

Last edited Jul 12, 2011 at 1:52 PM by Kelqualyn, version 21

Comments

No comments yet.