Creating a Decorator Using System.Reflection.Emit


J

jehugaleahsa

Hello:

I want to play around with a class that can generate types dynamically
at runtime. This class will be given an instance of an interface and
automatically implement a dynamic decorator class that implements the
same interface. All the decorator will do is delegate all calls to the
given instance.

The big idea is to give me another level indirection. Below is what I
have written so far. However, it keeps telling me that my properties
and methods aren't implemented. If someone could please fix my
mistakes and perhaps add additional logic for delegating to any other
language constructs, such as events, etc.

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace Lookup
{
/// <summary>
/// Generates decorators for instances of the specified interface.
/// </summary>
/// <typeparam name="TInterface">The interface type.</typeparam>
public class DecoratorFactory<TInterface>
where TInterface : class
{
private static readonly Type _dynamicType;

/// <summary>
/// Creates a dynamic type that implement the generic type
interface.
/// </summary>
static DecoratorFactory()
{
Type type = typeof(TInterface);
if (!type.IsInterface)
{
throw new InvalidOperationException("The type argument
to DecoratorFactory must be an interface.");
}
AssemblyName assemblyName = new AssemblyName
("LookupDecorators");
AssemblyBuilder assemblyBuilder
= AppDomain.CurrentDomain.DefineDynamicAssembly
(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder =
assemblyBuilder.DefineDynamicModule(assemblyName.Name);
string typeName = type.Name + "LookupDecorator";
TypeBuilder typeBuilder = moduleBuilder.DefineType
(typeName,
TypeAttributes.Class | TypeAttributes.Public |
TypeAttributes.Sealed,
typeof(Object),
new Type[] { type });
FieldBuilder fieldBuilder = typeBuilder.DefineField
("_instance", type, FieldAttributes.Private);
emitConstructor(type, typeBuilder, fieldBuilder);
emitPropertiesAndMethods(type, typeBuilder, fieldBuilder);
_dynamicType = typeBuilder.CreateType();
}

/// <summary>
/// Creates a constructor that takes an instance of the
interface type and stores it
/// in a backing field.
/// </summary>
/// <param name="type">The interface type.</param>
/// <param name="typeBuilder">The decorator type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
private static void emitConstructor(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo)
{
ConstructorBuilder ctorBuilder =
typeBuilder.DefineConstructor(MethodAttributes.Public,
CallingConventions.Standard,
new Type[] { type });
ILGenerator ctorGenerator = ctorBuilder.GetILGenerator();
ctorGenerator.Emit(OpCodes.Ldarg_0);
ctorGenerator.Emit(OpCodes.Call, typeof
(object).GetConstructor(Type.EmptyTypes));
ctorGenerator.Emit(OpCodes.Ldarg_0);
ctorGenerator.Emit(OpCodes.Ldarg_1);
ctorGenerator.Emit(OpCodes.Stfld, fieldInfo);
ctorGenerator.Emit(OpCodes.Ret);
}

/// <summary>
/// Emits properties and methods for the interface type (and
its interfaces)
/// such that they delegate to the underlying field's
properties and methods.
/// </summary>
/// <param name="type">The interface type.</param>
/// <param name="typeBuilder">The decorator type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
private static void emitPropertiesAndMethods(Type type,
TypeBuilder typeBuilder, FieldInfo fieldInfo)
{
emitProperties(type, typeBuilder, fieldInfo);
emitMethods(type, typeBuilder, fieldInfo);
foreach (Type interfaceType in type.GetInterfaces())
{
emitPropertiesAndMethods(interfaceType, typeBuilder,
fieldInfo);
}
}

/// <summary>
/// Emits properties for the interface type such that they
/// delegate to the underlying field.
/// </summary>
/// <param name="type">The interface type.</param>
/// <param name="typeBuilder">The decorator type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
private static void emitProperties(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo)
{
foreach (PropertyInfo propertyInfo in type.GetProperties
())
{
emitProperty(type, typeBuilder, fieldInfo,
propertyInfo);
}
}

/// <summary>
/// Emits a property for the interface type such that it
/// delegates to the corresponding property of the underlying
field.
/// </summary>
/// <param name="type">The interface type.</param>
/// <param name="typeBuilder">The decorator type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
/// <param name="propertyInfo">The property being delegated
to.</param>
private static void emitProperty(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo, PropertyInfo propertyInfo)
{
PropertyBuilder propertyBuilder =
typeBuilder.DefineProperty(propertyInfo.Name,
propertyInfo.Attributes,
propertyInfo.PropertyType,
null);
if (propertyInfo.CanRead)
{
MethodInfo methodInfo = propertyInfo.GetGetMethod();
MethodBuilder getterBuilder = typeBuilder.DefineMethod
("get_" + propertyInfo.Name,
methodInfo.Attributes,
methodInfo.CallingConvention,
methodInfo.ReturnType,
Type.EmptyTypes);
ILGenerator getterGenerator =
getterBuilder.GetILGenerator();
getterGenerator.Emit(OpCodes.Ldarg_0);
getterGenerator.Emit(OpCodes.Ldfld, fieldInfo);
getterGenerator.Emit(OpCodes.Call, methodInfo);
getterGenerator.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getterBuilder);
}
if (propertyInfo.CanWrite)
{
MethodInfo methodInfo = propertyInfo.GetSetMethod();
MethodBuilder setterBuilder = typeBuilder.DefineMethod
("set_" + propertyInfo.Name,
methodInfo.Attributes,
methodInfo.CallingConvention,
methodInfo.ReturnType,
new Type[] { propertyInfo.PropertyType });
ILGenerator setterGenerator =
setterBuilder.GetILGenerator();
setterGenerator.Emit(OpCodes.Ldarg_0);
setterGenerator.Emit(OpCodes.Ldfld, fieldInfo);
setterGenerator.Emit(OpCodes.Ldarg_1);
setterGenerator.Emit(OpCodes.Call, methodInfo);
setterGenerator.Emit(OpCodes.Ret);
propertyBuilder.SetSetMethod(setterBuilder);
}
}

/// <summary>
/// Emits methods for the interface type such that they
/// delegate to the underlying field.
/// </summary>
/// <param name="type">The interface type.</param>
/// <param name="typeBuilder">The decorated type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
private static void emitMethods(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo)
{
foreach (MethodInfo methodInfo in type.GetMethods())
{
if ((methodInfo.Attributes &
MethodAttributes.SpecialName) == 0)
{
emitMethod(type, typeBuilder, fieldInfo,
methodInfo);
}
}
}

/// <summary>
/// Emits a method for the interface type such that it
/// delegates to the corresponding method of the underlying
field.
/// </summary>
/// <param name="type">The interface type.</param>
/// <param name="typeBuilder">The decorator type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
/// <param name="methodInfo">The method being delegated to.</
param>
private static void emitMethod(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo, MethodInfo methodInfo)
{
ParameterInfo[] parameters = methodInfo.GetParameters();
List<Type> parameterTypes = new List<Type>();
foreach (ParameterInfo parameterInfo in parameters)
{
parameterTypes.Add(parameterInfo.ParameterType);
}
MethodBuilder methodBuilder = typeBuilder.DefineMethod
(methodInfo.Name,
methodInfo.Attributes,
methodInfo.CallingConvention,
methodInfo.ReturnType,
parameterTypes.ToArray());
ILGenerator bodyGenerator = methodBuilder.GetILGenerator
();
bodyGenerator.Emit(OpCodes.Ldarg_0);
bodyGenerator.Emit(OpCodes.Ldfld, fieldInfo);
for (int parameterIndex = 1; parameterIndex <
parameters.Length; ++parameterIndex)
{
bodyGenerator.Emit(OpCodes.Ldarg, parameterIndex);
}
bodyGenerator.Emit(OpCodes.Call, methodInfo);
bodyGenerator.Emit(OpCodes.Ret);
}

/// <summary>
/// Creates a decorator implementing the interface.
/// </summary>
/// <typeparam name="TInterface">The type of the interface.</
typeparam>
/// <returns>A decorator.</returns>
public TInterface CreateDecorator(TInterface instance)
{
ConstructorInfo constructor = _dynamicType.GetConstructor
(new Type[] { typeof(TInterface) });
return (TInterface)constructor.Invoke(new object[]
{ instance });
}
}
}
 
Ad

Advertisements

J

jehugaleahsa

Hello:

I want to play around with a class that can generate types dynamically
at runtime. This class will be given an instance of an interface and
automatically implement a dynamic decorator class that implements the
same interface. All the decorator will do is delegate all calls to the
given instance.

The big idea is to give me another level indirection. Below is what I
have written so far. However, it keeps telling me that my properties
and methods aren't implemented. If someone could please fix my
mistakes and perhaps add additional logic for delegating to any other
language constructs, such as events, etc.

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace Lookup
{
    /// <summary>
    /// Generates decorators for instances of the specified interface..
    /// </summary>
    /// <typeparam name="TInterface">The interface type.</typeparam>
    public class DecoratorFactory<TInterface>
        where TInterface : class
    {
        private static readonly Type _dynamicType;

        /// <summary>
        /// Creates a dynamic type that implement the generic type
interface.
        /// </summary>
        static DecoratorFactory()
        {
            Type type = typeof(TInterface);
            if (!type.IsInterface)
            {
                throw new InvalidOperationException("The type argument
to DecoratorFactory must be an interface.");
            }
            AssemblyName assemblyName = new AssemblyName
("LookupDecorators");
            AssemblyBuilder assemblyBuilder
                = AppDomain.CurrentDomain.DefineDynamicAssembly
(assemblyName, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder =
assemblyBuilder.DefineDynamicModule(assemblyName.Name);
            string typeName = type.Name + "LookupDecorator";
            TypeBuilder typeBuilder = moduleBuilder.DefineType
(typeName,
                TypeAttributes.Class | TypeAttributes.Public |
TypeAttributes.Sealed,
                typeof(Object),
                new Type[] { type });
            FieldBuilder fieldBuilder = typeBuilder.DefineField
("_instance", type, FieldAttributes.Private);
            emitConstructor(type, typeBuilder, fieldBuilder);
            emitPropertiesAndMethods(type, typeBuilder, fieldBuilder);
            _dynamicType = typeBuilder.CreateType();
        }

        /// <summary>
        /// Creates a constructor that takes an instance of the
interface type and stores it
        /// in a backing field.
        /// </summary>
        /// <param name="type">The interface type.</param>
        /// <param name="typeBuilder">The decorator type builder.</
param>
        /// <param name="fieldInfo">The backing field builder.</param>
        private static void emitConstructor(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo)
        {
            ConstructorBuilder ctorBuilder =
typeBuilder.DefineConstructor(MethodAttributes.Public,
                CallingConventions.Standard,
                new Type[] { type });
            ILGenerator ctorGenerator = ctorBuilder.GetILGenerator();
            ctorGenerator.Emit(OpCodes.Ldarg_0);
            ctorGenerator.Emit(OpCodes.Call, typeof
(object).GetConstructor(Type.EmptyTypes));
            ctorGenerator.Emit(OpCodes.Ldarg_0);
            ctorGenerator.Emit(OpCodes.Ldarg_1);
            ctorGenerator.Emit(OpCodes.Stfld, fieldInfo);
            ctorGenerator.Emit(OpCodes.Ret);
        }

        /// <summary>
        /// Emits properties and methods for the interface type (and
its interfaces)
        /// such that they delegate to the underlying field's
properties and methods.
        /// </summary>
        /// <param name="type">The interface type.</param>
        /// <param name="typeBuilder">The decorator type builder.</
param>
        /// <param name="fieldInfo">The backing field builder.</param>
        private static void emitPropertiesAndMethods(Type type,
TypeBuilder typeBuilder, FieldInfo fieldInfo)
        {
            emitProperties(type, typeBuilder, fieldInfo);
            emitMethods(type, typeBuilder, fieldInfo);
            foreach (Type interfaceType in type.GetInterfaces())
            {
                emitPropertiesAndMethods(interfaceType, typeBuilder,
fieldInfo);
            }
        }

        /// <summary>
        /// Emits properties for the interface type such that they
        /// delegate to the underlying field.
        /// </summary>
        /// <param name="type">The interface type.</param>
        /// <param name="typeBuilder">The decorator type builder.</
param>
        /// <param name="fieldInfo">The backing field builder.</param>
        private static void emitProperties(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo)
        {
            foreach (PropertyInfo propertyInfo in type.GetProperties
())
            {
                emitProperty(type, typeBuilder, fieldInfo,
propertyInfo);
            }
        }

        /// <summary>
        /// Emits a property for the interface type such that it
        /// delegates to the corresponding property of the underlying
field.
        /// </summary>
        /// <param name="type">The interface type.</param>
        /// <param name="typeBuilder">The decorator type builder.</
param>
        /// <param name="fieldInfo">The backing field builder.</param>
        /// <param name="propertyInfo">The property being delegated
to.</param>
        private static void emitProperty(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo, PropertyInfo propertyInfo)
        {
            PropertyBuilder propertyBuilder =
typeBuilder.DefineProperty(propertyInfo.Name,
                propertyInfo.Attributes,
                propertyInfo.PropertyType,
                null);
            if (propertyInfo.CanRead)
            {
                MethodInfo methodInfo = propertyInfo.GetGetMethod();
                MethodBuilder getterBuilder = typeBuilder.DefineMethod
("get_" + propertyInfo.Name,
                    methodInfo.Attributes,
                    methodInfo.CallingConvention,
                    methodInfo.ReturnType,
                    Type.EmptyTypes);
                ILGenerator getterGenerator =
getterBuilder.GetILGenerator();
                getterGenerator.Emit(OpCodes.Ldarg_0);
                getterGenerator.Emit(OpCodes.Ldfld, fieldInfo);
                getterGenerator.Emit(OpCodes.Call, methodInfo);
                getterGenerator.Emit(OpCodes.Ret);
                propertyBuilder.SetGetMethod(getterBuilder);
            }
            if (propertyInfo.CanWrite)
            {
                MethodInfo methodInfo = propertyInfo.GetSetMethod();
                MethodBuilder setterBuilder = typeBuilder.DefineMethod
("set_" + propertyInfo.Name,
                    methodInfo.Attributes,
                    methodInfo.CallingConvention,
                    methodInfo.ReturnType,
                    new Type[] { propertyInfo.PropertyType });
                ILGenerator setterGenerator =
setterBuilder.GetILGenerator();
                setterGenerator.Emit(OpCodes.Ldarg_0);
                setterGenerator.Emit(OpCodes.Ldfld, fieldInfo);
                setterGenerator.Emit(OpCodes.Ldarg_1);
                setterGenerator.Emit(OpCodes.Call, methodInfo);
                setterGenerator.Emit(OpCodes.Ret);
                propertyBuilder.SetSetMethod(setterBuilder);
            }
        }

        /// <summary>
        /// Emits methods for the interface type such that they
        /// delegate to the underlying field.
        /// </summary>
        /// <param name="type">The interface type.</param>
        /// <param name="typeBuilder">The decorated type builder.</
param>
        /// <param name="fieldInfo">The backing field builder.</param>
        private static void emitMethods(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo)
        {
            foreach (MethodInfo methodInfo in type.GetMethods())
            {
                if ((methodInfo.Attributes &
MethodAttributes.SpecialName) == 0)
                {
                    emitMethod(type, typeBuilder, fieldInfo,
methodInfo);
                }
            }
        }

        /// <summary>
        /// Emits a method for the interface type such that it
        /// delegates to the corresponding method of the underlying
field.
        /// </summary>
        /// <param name="type">The interface type.</param>
        /// <param name="typeBuilder">The decorator type builder.</
param>
        /// <param name="fieldInfo">The backing field builder.</param>
        /// <param name="methodInfo">The method being delegatedto.</
param>
        private static void emitMethod(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo, MethodInfo methodInfo)
        {
            ParameterInfo[] parameters = methodInfo.GetParameters();
            List<Type> parameterTypes = new List<Type>();
            foreach (ParameterInfo parameterInfo in parameters)
            {
                parameterTypes.Add(parameterInfo.ParameterType);
            }
            MethodBuilder methodBuilder = typeBuilder.DefineMethod
(methodInfo.Name,
                methodInfo.Attributes,
                methodInfo.CallingConvention,
                methodInfo.ReturnType,
                parameterTypes.ToArray());
            ILGenerator bodyGenerator = methodBuilder.GetILGenerator
();
            bodyGenerator.Emit(OpCodes.Ldarg_0);
            bodyGenerator.Emit(OpCodes.Ldfld, fieldInfo);
            for (int parameterIndex = 1; parameterIndex <
parameters.Length; ++parameterIndex)
            {
                bodyGenerator.Emit(OpCodes.Ldarg, parameterIndex);
            }
            bodyGenerator.Emit(OpCodes.Call, methodInfo);
            bodyGenerator.Emit(OpCodes.Ret);
        }

        /// <summary>
        /// Creates a decorator implementing the interface.
        /// </summary>
        /// <typeparam name="TInterface">The type of the interface.</
typeparam>
        /// <returns>A decorator.</returns>
        public TInterface CreateDecorator(TInterface instance)
        {
            ConstructorInfo constructor = _dynamicType.GetConstructor
(new Type[] { typeof(TInterface) });
            return (TInterface)constructor.Invoke(new object[]
{ instance });
        }
    }



}- Hide quoted text -

- Show quoted text -

I found out that one of my problems was that I was using the
MethodAttributes of the interface's getter and setter methods, which
essentially are Abstract in the interface.

The next problem was that I needed to add Virtual as one of the
MethodAttributes. That makes sense as well.

Once I did those two things my decorator was working pretty well. I
added a property to the decorator class to allow direct access to the
underlying instance.

Cool, that should get me working for now. I'd still like to see where
I could improve and see code for handling other language constructs,
such as events, etc.
 
P

Peter Duniho

Hello:

I want to play around with a class that can generate types dynamically
at runtime. This class will be given an instance of an interface and
automatically implement a dynamic decorator class that implements the
same interface. All the decorator will do is delegate all calls to the
given instance.

The big idea is to give me another level indirection. Below is what I
have written so far. However, it keeps telling me that my properties
and methods aren't implemented.

Since you aren't specific about the error and where it occurs, and you
didn't provide a concise-but-complete code example that reliably
demonstrates the problem, the chances of someone finding your error and
providing a solution are low. Not impossible, but not that likely either.

I scanned through the code, and didn't find anything obvious. But then,
what you're doing is a bit "out there"...it's harder to notice subtle
problems when the code is doing unusual things.

I'm intrigued by the idea of a decorator that, as near as I can tell, only
delegates and doesn't actually add any functionality. I assume the second
step will follow the first. :) I will be interested to see if, in the
end, you have really saved that much effort by having such a decorator
framework; intuitively, it seems to me that you'd have to have an awful
lot of disparate types that all have to be decorated in the same way for
this to be worth all the trouble.

But, it certainly looks cool and is a fun application of run-time type
generation. So I give you kudos for that! :)
If someone could please fix my
mistakes and perhaps add additional logic for delegating to any other
language constructs, such as events, etc.

I'm not sure what, other than events, you would want to delegate. Types
can have attributes, nested types, other interface implementations, etc.
but from the code you posted, I have the impression these are things you
wouldn't want or need to worry about.

As far as handling an event, you can easily do that yourself, as the event
implementation looks a lot like a property. Other than the actual method
names, you'll deal with it pretty much the same way you deal with
properties.

I'd love to see a fully working concise-but-complete code example. If you
post one, I'd be happy to try to find some time and look at your error.

Pete
 
J

jehugaleahsa

Since you aren't specific about the error and where it occurs, and you  
didn't provide a concise-but-complete code example that reliably  
demonstrates the problem, the chances of someone finding your error and  
providing a solution are low.  Not impossible, but not that likely either.

I scanned through the code, and didn't find anything obvious.  But then,  
what you're doing is a bit "out there"...it's harder to notice subtle  
problems when the code is doing unusual things.

I'm intrigued by the idea of a decorator that, as near as I can tell, only  
delegates and doesn't actually add any functionality.  I assume the second  
step will follow the first.  :)  I will be interested to see if, in the  
end, you have really saved that much effort by having such a decorator  
framework; intuitively, it seems to me that you'd have to have an awful  
lot of disparate types that all have to be decorated in the same way for  
this to be worth all the trouble.

But, it certainly looks cool and is a fun application of run-time type  
generation.  So I give you kudos for that!  :)


I'm not sure what, other than events, you would want to delegate.  Types  
can have attributes, nested types, other interface implementations, etc.  
but from the code you posted, I have the impression these are things you  
wouldn't want or need to worry about.

As far as handling an event, you can easily do that yourself, as the event  
implementation looks a lot like a property.  Other than the actual method  
names, you'll deal with it pretty much the same way you deal with  
properties.

I'd love to see a fully working concise-but-complete code example.  If you  
post one, I'd be happy to try to find some time and look at your error.

Pete

I did get this working last night. Below is the working code. Then
below that is a unit test that exercised it.

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace Decorators
{
/// <summary>
/// Generates decorators for instances of the specified interface.
/// </summary>
/// <typeparam name="TInterface">The interface type.</typeparam>
public class DecoratorFactory<TInterface>
where TInterface : class
{
private static readonly Type _dynamicType = emitType();
private const string _fieldName = "_instance";

/// <summary>
/// Creates a dynamic type that implement the generic type
interface.
/// </summary>
private static Type emitType()
{
Type type = typeof(TInterface);
if (!type.IsInterface)
{
throw new InvalidOperationException("The type argument
to DecoratorFactory must be an interface.");
}
AssemblyName assemblyName = new AssemblyName
("__Decorators");
AssemblyBuilder assemblyBuilder
= AppDomain.CurrentDomain.DefineDynamicAssembly
(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder =
assemblyBuilder.DefineDynamicModule(assemblyName.Name);
string typeName = "__" + type.Name + "_Decorator";
TypeBuilder typeBuilder = moduleBuilder.DefineType
(typeName,
TypeAttributes.Class | TypeAttributes.Public |
TypeAttributes.Sealed,
typeof(Object),
new Type[] { type });
FieldBuilder fieldBuilder = typeBuilder.DefineField
(_fieldName, type, FieldAttributes.Private);
emitConstructor(typeBuilder);
emitPropertiesAndMethods(type, typeBuilder, fieldBuilder);
return typeBuilder.CreateType();
}

/// <summary>
/// Creates a constructor that takes an instance of the
interface type and stores it
/// in a backing field.
/// </summary>
///
/// <param name="typeBuilder">The decorator type builder.</
param>
///
private static void emitConstructor(TypeBuilder typeBuilder)
{
ConstructorBuilder ctorBuilder =
typeBuilder.DefineConstructor(MethodAttributes.Public,
CallingConventions.Standard,
Type.EmptyTypes);
ILGenerator ctorGenerator = ctorBuilder.GetILGenerator();
ctorGenerator.Emit(OpCodes.Ldarg_0);
ctorGenerator.Emit(OpCodes.Call, typeof
(object).GetConstructor(Type.EmptyTypes));
ctorGenerator.Emit(OpCodes.Ret);
}

/// <summary>
/// Emits properties and methods for the interface type (and
its interfaces)
/// such that they delegate to the underlying field's
properties and methods.
/// </summary>
/// <param name="type">The interface type.</param>
/// <param name="typeBuilder">The decorator type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
private static void emitPropertiesAndMethods(Type type,
TypeBuilder typeBuilder, FieldInfo fieldInfo)
{
emitProperties(type, typeBuilder, fieldInfo);
emitMethods(type, typeBuilder, fieldInfo);
foreach (Type interfaceType in type.GetInterfaces())
{
emitPropertiesAndMethods(interfaceType, typeBuilder,
fieldInfo);
}
}

/// <summary>
/// Emits properties for the interface type such that they
/// delegate to the underlying field.
/// </summary>
/// <param name="type">The interface type.</param>
/// <param name="typeBuilder">The decorator type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
private static void emitProperties(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo)
{
foreach (PropertyInfo propertyInfo in type.GetProperties
())
{
emitProperty(typeBuilder, fieldInfo, propertyInfo);
}
}

/// <summary>
/// Emits a property for the interface type such that it
/// delegates to the corresponding property of the underlying
field.
/// </summary>
///
/// <param name="typeBuilder">The decorator type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
/// <param name="propertyInfo">The property being delegated
to.</param>
private static void emitProperty(TypeBuilder typeBuilder,
FieldInfo fieldInfo, PropertyInfo propertyInfo)
{
PropertyBuilder propertyBuilder =
typeBuilder.DefineProperty(propertyInfo.Name,
propertyInfo.Attributes,
propertyInfo.PropertyType,
null);
if (propertyInfo.CanRead)
{
MethodInfo methodInfo = propertyInfo.GetGetMethod();
MethodBuilder getterBuilder = typeBuilder.DefineMethod
("get_" + propertyInfo.Name,
MethodAttributes.Public |
MethodAttributes.SpecialName | MethodAttributes.HideBySig |
MethodAttributes.Virtual,
methodInfo.CallingConvention,
methodInfo.ReturnType,
Type.EmptyTypes);
ILGenerator getterGenerator =
getterBuilder.GetILGenerator();
getterGenerator.Emit(OpCodes.Ldarg_0);
getterGenerator.Emit(OpCodes.Ldfld, fieldInfo);
getterGenerator.Emit(OpCodes.Call, methodInfo);
getterGenerator.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getterBuilder);
}
if (propertyInfo.CanWrite)
{
MethodInfo methodInfo = propertyInfo.GetSetMethod();
MethodBuilder setterBuilder = typeBuilder.DefineMethod
("set_" + propertyInfo.Name,
MethodAttributes.Public |
MethodAttributes.SpecialName | MethodAttributes.HideBySig |
MethodAttributes.Virtual,
methodInfo.CallingConvention,
methodInfo.ReturnType,
new Type[] { propertyInfo.PropertyType });
ILGenerator setterGenerator =
setterBuilder.GetILGenerator();
setterGenerator.Emit(OpCodes.Ldarg_0);
setterGenerator.Emit(OpCodes.Ldfld, fieldInfo);
setterGenerator.Emit(OpCodes.Ldarg_1);
setterGenerator.Emit(OpCodes.Call, methodInfo);
setterGenerator.Emit(OpCodes.Ret);
propertyBuilder.SetSetMethod(setterBuilder);
}
}

/// <summary>
/// Emits methods for the interface type such that they
/// delegate to the underlying field.
/// </summary>
/// <param name="type">The interface type.</param>
/// <param name="typeBuilder">The decorated type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
private static void emitMethods(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo)
{
foreach (MethodInfo methodInfo in type.GetMethods())
{
if ((methodInfo.Attributes &
MethodAttributes.SpecialName) == 0)
{
emitMethod(typeBuilder, fieldInfo, methodInfo);
}
}
}

/// <summary>
/// Emits a method for the interface type such that it
/// delegates to the corresponding method of the underlying
field.
/// </summary>
///
/// <param name="typeBuilder">The decorator type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
/// <param name="methodInfo">The method being delegated to.</
param>
private static void emitMethod(TypeBuilder typeBuilder,
FieldInfo fieldInfo, MethodInfo methodInfo)
{
ParameterInfo[] parameters = methodInfo.GetParameters();
List<Type> parameterTypes = new List<Type>();
foreach (ParameterInfo parameterInfo in parameters)
{
parameterTypes.Add(parameterInfo.ParameterType);
}
MethodBuilder methodBuilder = typeBuilder.DefineMethod
(methodInfo.Name,
MethodAttributes.Public | MethodAttributes.Virtual,
methodInfo.CallingConvention,
methodInfo.ReturnType,
parameterTypes.ToArray());
ILGenerator bodyGenerator = methodBuilder.GetILGenerator
();
bodyGenerator.Emit(OpCodes.Ldarg_0);
bodyGenerator.Emit(OpCodes.Ldfld, fieldInfo);
for (int parameterIndex = 1; parameterIndex <
parameters.Length; ++parameterIndex)
{
bodyGenerator.Emit(OpCodes.Ldarg, parameterIndex);
}
bodyGenerator.Emit(OpCodes.Call, methodInfo);
bodyGenerator.Emit(OpCodes.Ret);
}

/// <summary>
/// Creates a decorator implementing the interface.
/// </summary>
/// <typeparam name="TInterface">The type of the interface.</
typeparam>
/// <returns>A decorator.</returns>
public TInterface CreateDecorator(TInterface instance)
{
ConstructorInfo constructor = _dynamicType.GetConstructor
(Type.EmptyTypes);
TInterface decorator = (TInterface)constructor.Invoke(new
Object[0]);
SetInstance(decorator, instance);
return decorator;
}

/// <summary>
/// Gets the given decorators underlying instance.
/// </summary>
/// <param name="decorator">The decorator to get the instance
for.</param>
/// <returns>The underlying instance.</returns>
public TInterface GetInstance(TInterface decorator)
{
if (decorator == null)
{
throw new ArgumentNullException("decorator", "The
decorator must not be null.");
}
Type decoratorType = decorator.GetType();
if (decoratorType != _dynamicType)
{
throw new ArgumentException("The given decorator was
not the decorator type.", "decorator");
}
FieldInfo fieldInfo = decoratorType.GetField(_fieldName,
BindingFlags.Instance | BindingFlags.NonPublic);
return (TInterface)fieldInfo.GetValue(decorator);
}

/// <summary>
/// Sets the given decorator's underlying instance.
/// </summary>
/// <param name="decorator">An instance of the decorator.</
param>
/// <param name="instance">The instance to place in the
decorator.</param>
public void SetInstance(TInterface decorator, TInterface
instance)
{
if (decorator == null)
{
throw new ArgumentNullException("decorator", "The
decorator must not be null.");
}
Type decoratorType = decorator.GetType();
if (decoratorType != _dynamicType)
{
throw new ArgumentException("The given decorator was
not the decorator type.", "decorator");
}
FieldInfo fieldInfo = decoratorType.GetField(_fieldName,
BindingFlags.Instance | BindingFlags.NonPublic);
fieldInfo.SetValue(decorator, instance);
}
}
}

[TestMethod]
public void TestDecoratorFactory()
{
DecoratorFactory<IDataObject> factory = new
DecoratorFactory<IDataObject>();

// Assert decorator wraps instance
DataObject instance = new DataObject() { Age = 24, Name =
"Travis" };
IDataObject decorator = factory.CreateDecorator(instance);
Assert.AreSame(instance, factory.GetInstance(decorator));

// Assert setter modify instance.
decorator.Age = 25;
decorator.Name = "John";
Assert.AreEqual<int>(25, instance.Age);
Assert.AreEqual<string>("John", instance.Name);

// Assert clone returns DataObject and not decorator.
IDataObject clone = (IDataObject)decorator.Clone();
Assert.IsInstanceOfType(clone, typeof(DataObject));

IDataObject tom = new DataObject() { Name = "Tom", Age =
23 };
factory.SetInstance(decorator, tom);
Assert.AreEqual<IDataObject>(tom, factory.GetInstance
(decorator));
}
 
J

jehugaleahsa

The main purpose of this code set was to save myself the time it would
have taken to individually create a decorator for every single data
object interface in my system (over 50). They weren't very significant
outside of a small part of the system so I couldn't justify creating
that much work for myself.

From a high level, I had a situation where I had several instances
pointing to a common object.

a b c d
| | | |
----------------
|
e

The problem was that I was creating a copy of e, call it f. After a
while, I would want to get rid of e altogether and just use the
updated f instance. Unfortunately, I couldn't find all the references
and change them. So, since I couldn't change all of the references, I
decided to just pull the carpet from underneathe their feet!

a b c d
| | | |
----------------
|
decorator
|
e

Then all I had to do was replace which instance the decorator referred
to and every reference would be instantly updated.

Of course this means that every access to the instance requires an
additional indirection. Performance isn't a big deal in my situation.
 
Ad

Advertisements

J

jehugaleahsa

Here is an implementation that also handles events and indexers (which
I totally forgot about). This class will now work with any C#
interface you throw at it. Sorry about my example unit test above; I
total forgot that you don't have IDataObject or DataObject available.
They're nothing special, just simple primitives.

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace Decorators
{
/// <summary>
/// Generates decorators for instances of the specified interface.
/// </summary>
/// <typeparam name="TInterface">The interface type.</typeparam>
public class DecoratorFactory<TInterface>
where TInterface : class
{
private static readonly Type _dynamicType = emitType();
private const string _fieldName = "_instance";

/// <summary>
/// Creates a dynamic type that implement the generic type
interface.
/// </summary>
/// <returns>The generated type.</returns>
private static Type emitType()
{
Type type = typeof(TInterface);
if (!type.IsInterface)
{
throw new InvalidOperationException("The type argument
to DecoratorFactory must be an interface.");
}
AssemblyName assemblyName = new AssemblyName
("__Decorators");
AssemblyBuilder assemblyBuilder
= AppDomain.CurrentDomain.DefineDynamicAssembly
(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder =
assemblyBuilder.DefineDynamicModule(assemblyName.Name);
string typeName = "__" + type.Name + "_Decorator";
TypeBuilder typeBuilder = moduleBuilder.DefineType
(typeName,
TypeAttributes.Class | TypeAttributes.Public |
TypeAttributes.Sealed,
typeof(Object),
new Type[] { type });
FieldBuilder fieldBuilder = typeBuilder.DefineField
(_fieldName, type, FieldAttributes.Private);
emitConstructor(typeBuilder);
emitInterface(type, typeBuilder, fieldBuilder);
return typeBuilder.CreateType();
}

/// <summary>
/// Creates a constructor that takes an instance of the
interface type and stores it
/// in a backing field.
/// </summary>
/// <param name="typeBuilder">The decorator type builder.</
param>
private static void emitConstructor(TypeBuilder typeBuilder)
{
ConstructorBuilder ctorBuilder =
typeBuilder.DefineConstructor(MethodAttributes.Public,
CallingConventions.Standard,
Type.EmptyTypes);
ILGenerator ctorGenerator = ctorBuilder.GetILGenerator();
ctorGenerator.Emit(OpCodes.Ldarg_0);
ctorGenerator.Emit(OpCodes.Call, typeof
(object).GetConstructor(Type.EmptyTypes));
ctorGenerator.Emit(OpCodes.Ret);
}

/// <summary>
/// Emits properties and methods for the interface type (and
its interfaces)
/// such that they delegate to the underlying field's
properties and methods.
/// </summary>
/// <param name="type">The interface type.</param>
/// <param name="typeBuilder">The decorator type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
private static void emitInterface(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo)
{
emitEvents(type, typeBuilder, fieldInfo);
emitProperties(type, typeBuilder, fieldInfo);
emitMethods(type, typeBuilder, fieldInfo);
foreach (Type interfaceType in type.GetInterfaces())
{
emitInterface(interfaceType, typeBuilder, fieldInfo);
}
}

/// <summary>
/// Emits events for the interface type such that they
delegate to the underlying field.
/// </summary>
/// <param name="type">The interface type.</param>
/// <param name="typeBuilder">The decorator type.</param>
/// <param name="fieldInfo">The backing field builder.</param>
private static void emitEvents(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo)
{
foreach (EventInfo eventInfo in type.GetEvents())
{
emitEvent(typeBuilder, fieldInfo, eventInfo);
}
}

/// <summary>
/// Emits an events for the interface type such that it
delegates
/// to the corresponding event of the underlying field.
/// </summary>
/// <param name="typeBuilder">The decorator type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
/// <param name="eventInfo">The event being delegated to.</
param>
private static void emitEvent(TypeBuilder typeBuilder,
FieldInfo fieldInfo, EventInfo eventInfo)
{
EventBuilder eventBuilder = typeBuilder.DefineEvent
(eventInfo.Name,
eventInfo.Attributes,
eventInfo.EventHandlerType);

MethodAttributes attributes = MethodAttributes.Public
| MethodAttributes.HideBySig
| MethodAttributes.SpecialName
| MethodAttributes.Virtual;

{
MethodInfo addMethodInfo = eventInfo.GetAddMethod();
MethodBuilder addMethodBuilder =
typeBuilder.DefineMethod(addMethodInfo.Name,
attributes,
addMethodInfo.CallingConvention,
addMethodInfo.ReturnType,
new Type[] { eventInfo.EventHandlerType });
ILGenerator addMethodGenerator =
addMethodBuilder.GetILGenerator();
addMethodGenerator.Emit(OpCodes.Ldarg_0);
addMethodGenerator.Emit(OpCodes.Ldfld, fieldInfo);
addMethodGenerator.Emit(OpCodes.Ldarg_1);
addMethodGenerator.Emit(OpCodes.Call, addMethodInfo);
addMethodGenerator.Emit(OpCodes.Ret);
eventBuilder.SetAddOnMethod(addMethodBuilder);
}

{
MethodInfo removeMethodInfo = eventInfo.GetRemoveMethod
();
MethodBuilder removeMethodBuilder =
typeBuilder.DefineMethod(removeMethodInfo.Name,
attributes,
removeMethodInfo.CallingConvention,
removeMethodInfo.ReturnType,
new Type[] { eventInfo.EventHandlerType });
ILGenerator removeMethodGenerator =
removeMethodBuilder.GetILGenerator();
removeMethodGenerator.Emit(OpCodes.Ldarg_0);
removeMethodGenerator.Emit(OpCodes.Ldfld, fieldInfo);
removeMethodGenerator.Emit(OpCodes.Ldarg_1);
removeMethodGenerator.Emit(OpCodes.Call,
removeMethodInfo);
removeMethodGenerator.Emit(OpCodes.Ret);
eventBuilder.SetRemoveOnMethod(removeMethodBuilder);
}
}

/// <summary>
/// Emits properties for the interface type such that they
/// delegate to the underlying field.
/// </summary>
/// <param name="type">The interface type.</param>
/// <param name="typeBuilder">The decorator type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
private static void emitProperties(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo)
{
foreach (PropertyInfo propertyInfo in type.GetProperties
())
{
emitProperty(typeBuilder, fieldInfo, propertyInfo);
}
}

/// <summary>
/// Emits a property for the interface type such that it
/// delegates to the corresponding property of the underlying
field.
/// </summary>
/// <param name="typeBuilder">The decorator type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
/// <param name="propertyInfo">The property being delegated
to.</param>
private static void emitProperty(TypeBuilder typeBuilder,
FieldInfo fieldInfo, PropertyInfo propertyInfo)
{
ParameterInfo[] parameters =
propertyInfo.GetIndexParameters();
List<Type> parameterTypes = new List<Type>();
foreach (ParameterInfo parameterInfo in parameters)
{
parameterTypes.Add(parameterInfo.ParameterType);
}
PropertyBuilder propertyBuilder =
typeBuilder.DefineProperty(propertyInfo.Name,
propertyInfo.Attributes,
propertyInfo.PropertyType,
parameterTypes.ToArray());

MethodAttributes attributes = MethodAttributes.Public
| MethodAttributes.SpecialName
| MethodAttributes.HideBySig
| MethodAttributes.Virtual;

if (propertyInfo.CanRead)
{
MethodInfo methodInfo = propertyInfo.GetGetMethod();
MethodBuilder getterBuilder = typeBuilder.DefineMethod
(methodInfo.Name,
attributes,
methodInfo.CallingConvention,
methodInfo.ReturnType,
parameterTypes.ToArray());
ILGenerator getterGenerator =
getterBuilder.GetILGenerator();
getterGenerator.Emit(OpCodes.Ldarg_0);
getterGenerator.Emit(OpCodes.Ldfld, fieldInfo);
for (int parameterIndex = 1; parameterIndex <=
parameterTypes.Count; ++parameterIndex)
{
getterGenerator.Emit(OpCodes.Ldarg,
parameterIndex);
}
getterGenerator.Emit(OpCodes.Call, methodInfo);
getterGenerator.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getterBuilder);
}
if (propertyInfo.CanWrite)
{
MethodInfo methodInfo = propertyInfo.GetSetMethod();
parameterTypes.Add(propertyInfo.PropertyType);
MethodBuilder setterBuilder = typeBuilder.DefineMethod
(methodInfo.Name,
attributes,
methodInfo.CallingConvention,
methodInfo.ReturnType,
parameterTypes.ToArray());
ILGenerator setterGenerator =
setterBuilder.GetILGenerator();
setterGenerator.Emit(OpCodes.Ldarg_0);
setterGenerator.Emit(OpCodes.Ldfld, fieldInfo);
for (int parameterIndex = 1; parameterIndex <=
parameterTypes.Count; ++parameterIndex)
{
setterGenerator.Emit(OpCodes.Ldarg,
parameterIndex);
}
setterGenerator.Emit(OpCodes.Call, methodInfo);
setterGenerator.Emit(OpCodes.Ret);
propertyBuilder.SetSetMethod(setterBuilder);
}
}

/// <summary>
/// Emits methods for the interface type such that they
/// delegate to the underlying field.
/// </summary>
/// <param name="type">The interface type.</param>
/// <param name="typeBuilder">The decorated type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
private static void emitMethods(Type type, TypeBuilder
typeBuilder, FieldInfo fieldInfo)
{
foreach (MethodInfo methodInfo in type.GetMethods())
{
if ((methodInfo.Attributes &
MethodAttributes.SpecialName) == 0)
{
emitMethod(typeBuilder, fieldInfo, methodInfo);
}
}
}

/// <summary>
/// Emits a method for the interface type such that it
/// delegates to the corresponding method of the underlying
field.
/// </summary>
/// <param name="typeBuilder">The decorator type builder.</
param>
/// <param name="fieldInfo">The backing field builder.</param>
/// <param name="methodInfo">The method being delegated to.</
param>
private static void emitMethod(TypeBuilder typeBuilder,
FieldInfo fieldInfo, MethodInfo methodInfo)
{
ParameterInfo[] parameters = methodInfo.GetParameters();
List<Type> parameterTypes = new List<Type>();
foreach (ParameterInfo parameterInfo in parameters)
{
parameterTypes.Add(parameterInfo.ParameterType);
}
MethodBuilder methodBuilder = typeBuilder.DefineMethod
(methodInfo.Name,
MethodAttributes.Public | MethodAttributes.Virtual,
methodInfo.CallingConvention,
methodInfo.ReturnType,
parameterTypes.ToArray());
ILGenerator bodyGenerator = methodBuilder.GetILGenerator
();
bodyGenerator.Emit(OpCodes.Ldarg_0);
bodyGenerator.Emit(OpCodes.Ldfld, fieldInfo);
for (int parameterIndex = 1; parameterIndex <
parameters.Length; ++parameterIndex)
{
bodyGenerator.Emit(OpCodes.Ldarg, parameterIndex);
}
bodyGenerator.Emit(OpCodes.Call, methodInfo);
bodyGenerator.Emit(OpCodes.Ret);
}

/// <summary>
/// Creates a decorator implementing the interface.
/// </summary>
/// <typeparam name="TInterface">The type of the interface.</
typeparam>
/// <returns>A decorator.</returns>
public TInterface CreateDecorator(TInterface instance)
{
ConstructorInfo constructor = _dynamicType.GetConstructor
(Type.EmptyTypes);
TInterface decorator = (TInterface)constructor.Invoke(new
Object[0]);
SetInstance(decorator, instance);
return decorator;
}

/// <summary>
/// Gets the given decorators underlying instance.
/// </summary>
/// <param name="decorator">The decorator to get the instance
for.</param>
/// <returns>The underlying instance.</returns>
public TInterface GetInstance(TInterface decorator)
{
if (decorator == null)
{
throw new ArgumentNullException("decorator", "The
decorator must not be null.");
}
Type decoratorType = decorator.GetType();
if (decoratorType != _dynamicType)
{
throw new ArgumentException("The given decorator was
not the decorator type.", "decorator");
}
FieldInfo fieldInfo = decoratorType.GetField(_fieldName,
BindingFlags.Instance | BindingFlags.NonPublic);
return (TInterface)fieldInfo.GetValue(decorator);
}

/// <summary>
/// Sets the given decorator's underlying instance.
/// </summary>
/// <param name="decorator">An instance of the decorator.</
param>
/// <param name="instance">The instance to place in the
decorator.</param>
public void SetInstance(TInterface decorator, TInterface
instance)
{
if (decorator == null)
{
throw new ArgumentNullException("decorator", "The
decorator must not be null.");
}
Type decoratorType = decorator.GetType();
if (decoratorType != _dynamicType)
{
throw new ArgumentException("The given decorator was
not the decorator type.", "decorator");
}
FieldInfo fieldInfo = decoratorType.GetField(_fieldName,
BindingFlags.Instance | BindingFlags.NonPublic);
fieldInfo.SetValue(decorator, instance);
}
}
}
 
Ad

Advertisements


Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top