How Yappi Works

Inside Yappi there is everything around Reflection.Emit. New types are emitted at runtime on call to one of PropertyProxy.ConstructType overloads using given base type and implementation. Constructed new type overrides existing virtual properties and implements specified interfaces and its properties using given implementation. For each property accessor, Implementation class defines corresponding delegate,to use every time, accessing property.

Accessor-like delegates are defined inside methods with generic  arguments that completely defines property’s structure, including , base type, type declaring property, type declaring overridden implementation, constructed new type (yep, implementation is defined after type construction, in type’s initializer) and property type. Base implementation class contains one generic parameter - common base class for implementation. In deriver implementation you can add generic constrain on base type. this constraint automatically will be applied to parameters of accessor-defining methods allowing you to access common base type’s methods directly without reflection. As a result, overridden implementation can be defined as anonymous delegate or lambda expression, and this is core benefit. Lambdas are written in pure С# with full language features including refactoring and debugging support.

Common practice in overrides is non-virtual and virtual calls to base class methods. For example “base.” calls. There is helper classes for that:

  • Property<TType> helps to make virtual call to property accessor by its name and type (passed as generic parameter)
  • PropertyImplementation<TImplementingType, TDeclaringType> helps to make non-virtual call to property accessor implementation by its name , declaring and implementing types

Types, defined at runtime can’t be constructed by direct constructor call from precompiled code.

Common practices are:

  • Activator.CreateInstance
  • ConstrictorInfo.Invoke

Both are hundreds times slower then direct call. Yappi introduces Constructor helper class. It contains one method with signature:

Compile<TDelegate>(Type instanceType)

This method takes given delegate type’s signature, searches constructor in given instance type with corresponding signature and makes delegate, that directly calls found constructor. Constructed delegate almost as fast as direct constructor call. It can be constructed once in type initializer and stored in static readonly field.

All mentioned features are used in this code sample:

    public class Concept:INotifyPropertyChanged
    {
        //Hide constructor
        protected Concept(){}
        
        public static class Create<TConcept> where TConcept:Concept
        {
            //Construct derived Type calling PropertyProxy.ConstructType
            public static readonly Type Type = PropertyProxy.ConstructType<TConcept, Implementation<TConcept>>(new Type[0], true);
            //Create constructing delegate calling Constructor.Compile
            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);
            }
            /// <summary>
            /// Overriding property setter implementation.
            /// </summary>
            /// <typeparam name="TBaseType">Base type for implementation. TBaseType must be TConcept, and inherits all its constraints. Also TBaseType is TDeclaringType.</typeparam>
            /// <typeparam name="TDeclaringType">Type, declaring property.</typeparam>
            /// <typeparam name="TConstructedType">Constructed type. TConstructedType is TDeclaringType and TBaseType.</typeparam>
            /// <typeparam name="TResult">Type of property.</typeparam>
            /// <param name="property">PropertyInfo of property.</param>
            /// <returns>Delegate, corresponding to property setter implementation.</returns>
            public override Action<TBaseType, TResult> OverrideSetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                var propertyName = property.Name;
                //get delegates for base calls.
                Action<TBaseType, TResult> setter = PropertyImplementation<TBaseType, TDeclaringType>.GetSetter<TResult>(propertyName);
                Func<TBaseType, TResult> getter = PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(propertyName);
 
                var comparer = EqualityComparer<TResult>.Default;
 
                return (pthis, value) =>
                {
                    if (comparer.Equals(value, getter(pthis))) return;
                    //base. call
                    setter(pthis, value);
                    //Directly accessing Concept's protected method.
                    pthis.OnPropertyChanged(propertyName);
                };
            }
        }
    }
    public class Animal:Concept
    {
        protected Animal(){}
        public virtual string Name { get; set; }
        public virtual int Age { get; set; }
    }

You interested?

Try latest release or read more about Yappi.

Last edited Jul 20, 2011 at 5:23 PM by Kelqualyn, version 8

Comments

No comments yet.