Archive for December, 2008

It’s almost that time of year

More often than not, I hear about people who think so low of their ability to keep new years resolutions, that they refuse to make them.  However, I think that a periodic reflection of your life, your goals, and your priorities is important.  And thus, I will be making my list, and checking it twice next week :-)

Merry Christmas to everyone

Comments

Using the LateBinder

Heh, I seemed to have not realized that I didn’t include any usage information on the LateBinder in the last post.  So here is the how and why you’d want to use this class.

Say you have a need to dynamically get or set the property of some class using the string name of the property.  Normally, the answer would be to use reflection, get the PropertyInfo, extract the MethodInfo of the getter or setter, and then invoke that method using the reflection API.  Hardly very usable … and if you’re not careful, you will end up paying the reflection cost everytime because you have to keep looking up the PropertyInfo and all that.

Enter the LateBinder<T> … using this class, you can do:

MyClass mcInstance = new MyClass();

LateBinder<MyClass> binder = LateBinder<MyClass>.Instance;
binder[mcInstance, "Name"] = “my name”;
string name = (string)binder[mcInstance, "Name"];

The late binder (on windows) uses the System.Reflection.Emit API to generate a DynamicMethod, which is much faster to call than the reflection invoke because it’s pre-compiled using custom built IL instructions.  It also caches them in the late binder instance so that it only has to be compiled once … and furthermore, if you use the singleton .Instance method, you only have to do the compilation once per application run since it keeps the data around in a static variable.

Beneath the hood, the original version of the late binder was using this class.  But I had to change the design of the class to support the cross platform aspect of it and to add in the static support.  It was pretty fun, I ended up using reflector to decompile some sample code I wrote in a console app so I could see what IL instructions I had to use to get/set the statics … so with this version of LateBinder, it supports getting and setting both properties and fields … regardless of whether they are instance or static fields/properties.

Now, on XBox and Zune, the story changes a bit because System.Reflection.Emit does not exist.  So, in this new version I posted, it falls back to simply using reflection.  However, it does keep the propertyinfo in that static cache, so at least you won’t have to pay the penalty of looking for it every time.

So to get back to the original question on usage (ie. when/why would I want to use it) … it probably wouldn’t be a good idea to use this in a tight loop in runtime code.  However, for certain tools and custom serialization code … it could certainly be very useful.  For example, you can relatively easily write code using this that will transfer property values from one class to another … assuming the property names are the same.  The list goes on and on as to how this is useful.

Comments (2)

New LateBinder

A long time ago, I posted a class called LateBinder to this site (the previous blog software).  I went on to use it rather prodigiously at work, and it also turned out to be a boon to others who visited this site (for example, Victor of FlatRedBall fame).  Unfortunately, it did have a few limitations; namely that it didn’t support getting/setting static fields and properties, and it also didn’t work on the xbox360 and zune because the System.Reflection.Emit namespace wasn’t available.

He asked me the other day the feasibility of supporting the static properties, and after a few days of hacking about, I’ve added that, and then some :-)

The version (below) not only supports static fields and properties, but it also supports the xbox360 and zune platforms.  Be warned though, I had to fall back on using simple reflection on the non-windows platforms … so the perf will obviously be slightly slower. 

I am fairly proud of how I was able to refactor the code to encapsulate the cross-platform differences.  If you look at the LateBindFactory abstract class, it uses a factory method to instantiate the platform-specific version.  This way, I was able to keep the pre-processor directives to a minimum, using them only where I needed … and also keeping the same public API for both platforms.

Let me know if this is useful for you  :-)

#region Using Statements

#if !XBOX360 && !ZUNE
#define USE_EMIT
#endif

using System;
using System.Collections.Generic;
using System.Reflection;
#if USE_EMIT
using System.Reflection.Emit;
#endif

#endregion

namespace Scurvy
{
    /// <summary>
    /// Provides a simple interface to late bind a class.
    /// </summary>
    /// <remarks>The first time you attempt to get or set a property, it will dynamically generate the get and/or set
    /// methods and cache them internally.  Subsequent gets uses the dynamic methods without having to query the type’s
    /// meta data.</remarks>
    public sealed class LateBinder<T>
    {
        #region Fields

        private static LateBindFactory factory = LateBindFactory.Create();
        private BindingFlags propertyFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
        private Type mType;
        private Dictionary<string, GetHandler> mPropertyGet;
        private Dictionary<string, SetHandler> mPropertySet;

        private Dictionary<Type, List<string>> mFields;

        private T mTarget = default(T);

        private static LateBinder<T> _instance;

        #endregion

        #region Properties

        public static LateBinder<T> Instance
        {
            get { return _instance; }
        }

        /// <summary>
        /// The instance that this binder operates on by default
        /// </summary>
        /// <remarks>This can be overridden by the caller explicitly passing a target to the indexer</remarks>
        public T Target
        {
            get { return mTarget; }
            set { mTarget = value; }
        }

        /// <summary>
        /// Gets or Sets the supplied property on the contained <seealso cref=”Instance”/>
        /// </summary>
        /// <exception cref=”InvalidOperationException”>Throws if the contained Instance is null.</exception>
        public object this[string propertyName]
        {
            get
            {
                //ValidateInstance();
                return this[mTarget, propertyName];
            }
            set
            {
                //ValidateInstance();
                this[mTarget, propertyName] = value;
            }
        }

        /// <summary>
        /// Gets or Sets the supplied property on the supplied target
        /// </summary>
        public object this[T target, string propertyName]
        {
            get
            {
                ValidateGetter(ref propertyName);
                return mPropertyGet[propertyName](target);
            }
            set
            {
                ValidateSetter(ref propertyName);
                mPropertySet[propertyName](target, value);
            }
        }

        #endregion

        #region Methods

        #region Constructors

        static LateBinder()
        {
            _instance = new LateBinder<T>();
        }

        public LateBinder(T instance)
            : this()
        {
            mTarget = instance;
        }

        public LateBinder()
        {
            mType = typeof(T);
            mPropertyGet = new Dictionary<string, GetHandler>();
            mPropertySet = new Dictionary<string, SetHandler>();

            mFields = new Dictionary<Type, List<string>>();
        }
        #endregion

        #endregion

        #region Public Accessors

        /// <summary>
        /// Sets the supplied property on the supplied target
        /// </summary>
        /// <typeparam name=”K”>the type of the value</typeparam>
        public void SetProperty<K>(T target, string propertyName, K value)
        {
#if !USE_EMIT

            // find out if this is a property or field
            Type type = typeof(T);

            PropertyInfo propertyInfo = type.GetProperty(propertyName);

            if (propertyInfo != null)
            {
                propertyInfo.SetValue(target, value, null);
            }

            else
            {
                FieldInfo fieldInfo = type.GetField(propertyName);

                if (fieldInfo != null)
                {
                    fieldInfo.SetValue(target, value);
                }
                else
                {
                    throw new ArgumentException(“Cannot find property or field with the name ” + propertyName);
                }

            }

#else
            ValidateSetter(ref propertyName);

            if (mPropertySet.ContainsKey(propertyName))
            {
                mPropertySet[propertyName](target, value);
            }
            else
            {
                // This is probably not a property so see if it is a field.

                FieldInfo fieldInfo = mType.GetField(propertyName);

                if (fieldInfo == null)
                {
                    string errorMessage =
                        “LateBinder could not find a field or property by the name of ” + propertyName +
                        “Check the name of the property to verify if it is correct.”;
                    throw new System.MemberAccessException(errorMessage);
                }
                else
                {
                    object[] args = { value };
                    mType.InvokeMember(propertyName, BindingFlags.SetField, null, target, args);
                }
            }
#endif
        }

        public object GetField(T target, ref string propertyName)
        {
            Binder binder = null;
            object[] args = null;

            return mType.InvokeMember(
               propertyName,
               BindingFlags.GetField,
               binder,
               target,
               args
               );
        }

        /// <summary>
        /// Gets  the supplied property on the supplied target
        /// </summary>
        /// <typeparam name=”K”>The type of the property being returned</typeparam>
        public K GetProperty<K>(T target, ref string propertyName)
        {
            ValidateGetter(ref propertyName);
            return (K)mPropertyGet[propertyName](target);
        }

        public object GetProperty(T target, ref string propertyName)
        {
            ValidateGetter(ref propertyName);
            return mPropertyGet[propertyName](target);
        }

        #endregion

        #region Private Helpers

        private void ValidateInstance()
        {
            if (mTarget == null)
            {
                throw new InvalidOperationException(“Instance property must not be null”);
            }
        }

        private void ValidateSetter(ref string propertyName)
        {
            if (!mPropertySet.ContainsKey(propertyName))
            {
                PropertyInfo propertyInfo = mType.GetProperty(propertyName, this.propertyFlags);

                if (propertyInfo != null)
                {
                    mPropertySet.Add(propertyName, factory.CreateSetHandler(mType, propertyInfo));
                }
            }
        }

        private void ValidateGetter(ref string propertyName)
        {
            if (!mPropertyGet.ContainsKey(propertyName))
            {
                mPropertyGet.Add(propertyName, factory.CreateGetHandler(mType, mType.GetProperty(propertyName)));
            }
        }

        #endregion

        #region Contained Classes

        internal delegate object GetHandler(object source);
        internal delegate void SetHandler(object source, object value);
        internal delegate object InstantiateObjectHandler();

        internal abstract class LateBindFactory
        {
            public LateBindFactory()
            {
            }

            public static LateBindFactory Create()
            {
#if USE_EMIT
                return new DynamicMethodFactory();
#else
                return new ReflectionFactory();
#endif
            }

            public abstract InstantiateObjectHandler CreateInstantiateObjectHandler(Type type);
            public abstract GetHandler CreateGetHandler(Type type, PropertyInfo propertyInfo);
            public abstract GetHandler CreateGetHandler(Type type, FieldInfo fieldInfo);
            public abstract SetHandler CreateSetHandler(Type type, PropertyInfo propertyInfo);
            public abstract SetHandler CreateSetHandler(Type type, FieldInfo fieldInfo);
        }

#if USE_EMIT
        internal sealed class DynamicMethodFactory : LateBindFactory
        {
            public DynamicMethodFactory()
            {
            }

            #region Public Methods

            public override InstantiateObjectHandler CreateInstantiateObjectHandler(Type type)
            {
                ConstructorInfo constructorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null);
                if (constructorInfo == null)
                {
                    throw new ApplicationException(string.Format(“The type {0} must declare an empty constructor (the constructor may be private, internal, protected, protected internal, or public).”, type));
                }

                DynamicMethod dynamicMethod = new DynamicMethod(“InstantiateObject”, MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(object), null, type, true);
                ILGenerator generator = dynamicMethod.GetILGenerator();
                generator.Emit(OpCodes.Newobj, constructorInfo);
                generator.Emit(OpCodes.Ret);
                return (InstantiateObjectHandler)dynamicMethod.CreateDelegate(typeof(InstantiateObjectHandler));
            }

            public override GetHandler CreateGetHandler(Type type, PropertyInfo propertyInfo)
            {
                MethodInfo getMethodInfo = propertyInfo.GetGetMethod(true);

                DynamicMethod dynamicGet = CreateGetDynamicMethod(type);
                ILGenerator getGenerator = dynamicGet.GetILGenerator();

                if (!getMethodInfo.IsStatic)
                {
                    getGenerator.Emit(OpCodes.Ldarg_0);
                }
                getGenerator.Emit(OpCodes.Call, getMethodInfo);
                BoxIfNeeded(getMethodInfo.ReturnType, getGenerator);
                getGenerator.Emit(OpCodes.Ret);

                return (GetHandler)dynamicGet.CreateDelegate(typeof(GetHandler));
            }

            public override GetHandler CreateGetHandler(Type type, FieldInfo fieldInfo)
            {

                DynamicMethod dynamicGet = CreateGetDynamicMethod(type);
                ILGenerator getGenerator = dynamicGet.GetILGenerator();

                if (!fieldInfo.IsStatic)
                {
                    getGenerator.Emit(OpCodes.Ldarg_0);
                }
                getGenerator.Emit(OpCodes.Ldfld, fieldInfo);
                BoxIfNeeded(fieldInfo.FieldType, getGenerator);
                getGenerator.Emit(OpCodes.Ret);

                return (GetHandler)dynamicGet.CreateDelegate(typeof(GetHandler));
            }

            public override SetHandler CreateSetHandler(Type type, PropertyInfo propertyInfo)
            {
                MethodInfo setMethodInfo = propertyInfo.GetSetMethod(true);

                DynamicMethod dynamicSet = CreateSetDynamicMethod(type);
                ILGenerator setGenerator = dynamicSet.GetILGenerator();

                if (!setMethodInfo.IsStatic)
                {
                    setGenerator.Emit(OpCodes.Ldarg_0);
                }
                setGenerator.Emit(OpCodes.Ldarg_1);
                UnboxIfNeeded(setMethodInfo.GetParameters()[0].ParameterType, setGenerator);
                setGenerator.Emit(OpCodes.Call, setMethodInfo);
                setGenerator.Emit(OpCodes.Ret);

                return (SetHandler)dynamicSet.CreateDelegate(typeof(SetHandler));
            }

            public override SetHandler CreateSetHandler(Type type, FieldInfo fieldInfo)
            {
                DynamicMethod dynamicSet = CreateSetDynamicMethod(type);
                ILGenerator setGenerator = dynamicSet.GetILGenerator();

                if (!fieldInfo.IsStatic)
                {
                    setGenerator.Emit(OpCodes.Ldarg_0);
                }
                setGenerator.Emit(OpCodes.Ldarg_1);
                UnboxIfNeeded(fieldInfo.FieldType, setGenerator);

                if (!fieldInfo.IsStatic)
                {
                    setGenerator.Emit(OpCodes.Stfld, fieldInfo);
                }
                else
                {
                    setGenerator.Emit(OpCodes.Stsfld, fieldInfo);
                }

                setGenerator.Emit(OpCodes.Ret);

                return (SetHandler)dynamicSet.CreateDelegate(typeof(SetHandler));
            }

            #endregion

            #region Private Methods

            private static DynamicMethod CreateGetDynamicMethod(Type type)
            {
                return new DynamicMethod(“DynamicGet”, typeof(object), new Type[] { typeof(object) }, type, true);
            }

            // CreateSetDynamicMethod
            private static DynamicMethod CreateSetDynamicMethod(Type type)
            {
                return new DynamicMethod(“DynamicSet”, typeof(void), new Type[] { typeof(object), typeof(object) }, type, true);
            }

            // BoxIfNeeded
            private static void BoxIfNeeded(Type type, ILGenerator generator)
            {
                if (type.IsValueType)
                {
                    generator.Emit(OpCodes.Box, type);
                }
            }

            // UnboxIfNeeded
            private static void UnboxIfNeeded(Type type, ILGenerator generator)
            {
                if (type.IsValueType)
                {
                    generator.Emit(OpCodes.Unbox_Any, type);
                }
            }

            #endregion
        }
#endif

#if !USE_EMIT
        internal sealed class ReflectionFactory : LateBindFactory
        {
            public ReflectionFactory()
            {
            }

            public override InstantiateObjectHandler CreateInstantiateObjectHandler(Type type)
            {
                return delegate()
                {
                    return Activator.CreateInstance(type);
                };
            }

            public override GetHandler CreateGetHandler(Type type, PropertyInfo propertyInfo)
            {
                MethodInfo method = propertyInfo.GetGetMethod(true);

                return delegate(object source)
                {
                    return method.Invoke(method.IsStatic ? null : source, null);
                };
            }

            public override GetHandler CreateGetHandler(Type type, FieldInfo fieldInfo)
            {
                return delegate(object source)
                {
                    return fieldInfo.GetValue(fieldInfo.IsStatic ? null : source);
                };
            }

            public override SetHandler CreateSetHandler(Type type, PropertyInfo propertyInfo)
            {
                MethodInfo method = propertyInfo.GetSetMethod();

                return delegate(object source, object value)
                {
                    method.Invoke(method.IsStatic ? null : source, new object[] { value });
                };
            }

            public override SetHandler CreateSetHandler(Type type, FieldInfo fieldInfo)
            {
                return delegate(object source, object value)
                {
                    fieldInfo.SetValue(fieldInfo.IsStatic ? null : source, value);
                };
            }
        }
#endif
        #endregion
    }
}

Comments

Handy LinQ Extension Methods

Isn’t it annoying how an IEnumerable<SomeClassThatImplementsISomeInterface>, is not castable to IEnumerable<ISomeInterface> ?  The reasoning for this makes sense once you get into it … because of generics, those two are functionally different classes.

So, I made a handy-dandy extension method that does the job for me (at the cost of a few more allocations on the heap)

public static IEnumerable<KTo> Convert<TFrom, KTo>(this IEnumerable<TFrom> enumerable)
    where TFrom : KTo
{           
    return enumerable.Select((TFrom s) => (KTo)s);
}

The usage is very simple: return list.Convert<SomeClassThatImplementsISomeInterface, ISomeInterface>();

If you need to do this with an Entity Framework query, where your entities are partial classed to implement an interface … you need to use this version though:

public static IEnumerable<KTo> ConvertToList<TFrom, KTo>(this IEnumerable<TFrom> enumerable)
    where TFrom : KTo
{           
    return enumerable.ToList().Select((TFrom s) => (KTo)s);
}

note the .ToList() that’s injected in there … the reason for this is that the enumerable needs to be fully enumerated so that the EF executes the query and moves all the results into memory, before doing the cast in the select.

hope that helps :-)

Comments (3)