this.DataBindings.Add(new Binding("Text", brain, "Dump"));

Development and .Net

  Home  |   Contact  |   Syndication    |   Login
  5 Posts | 0 Stories | 11 Comments | 4 Trackbacks

News

Archives

Post Categories

.Net Development

Last post I promised that I would post the final code that I was working on, in regards to Generics and DynamicMethods.  The basic idea of the code is a class that generates DynamicMethod delegates that have been casted as a certain type of delegate, using Generics.  Here's what I was thinking the calling code would look like

    TestDelegate
del = generator.SomeMethod<TestDelegate>();
Surprisingly, this was a lot hard than it first seems.  It seems that there are some restrictions with Generics and delegates.  First off, you can not use System.Delegate as a limiter for a Generic type.  So you can not do the following :
   
    public
T SomeMethod<T>() where T : System.Delegate
If you try to compile the previous code in some program you get the following error message: Constraint cannot be special class 'System.Delegate'.  Now DynamicMethod's CreateDelegate will only return an object of  type System.Delegate.  That is all and good, but if you are trying to have type safe delegate, you need to cast that object into the correct delegate type.  But the following code will also fail:

    return
(T)dynamicMethod.CreateDelegate(typeof(T));
That section of code will won't compile and will generate this error: Cannot convert type 'System.Delegate' to 'T'.  It seems that you have to use the as keyword to do a proper cast.

    return
dynamicMethod.CreateDelegate(typeof(T)) as T;
Now the code won't compile because The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint.  So you need to add a class constraint for T like

    private
T SomeMethod<T>() where T : class
Now once you set up your code in this way, the code will finally compile.  All this information about using Generics and delegates in this way comes from  Mike Woodring's post Constraints on contraints, I just applied his knowledge to DynamicMethods.

Here's the final class

    public class MethodGenerator
    {
        
public event EventHandler<CreatingMethodBodyEventArgs> CreatingMethodBody;
        
public MethodGenerator()
        {}

        
/// <summary>
        /// Generates a new method with a signature that matches the signature of the delegate T.
        /// </summary>
        /// <typeparam name="T">The type of the delegate to return and use as a method signature.</typeparam>
        /// <typeparam name="K">The type of the object with which the generated method will be associated.</typeparam>
        /// <param name="name">The identifying name of the generated method.</param>
        /// <returns>A delegate of type T which points to the generated method</returns>
        public T Generate<T, K>(string name) where T : class
        {
            
MethodInfo method = GetMethod(typeof(T));
            
ParameterInfo[] parameterDefinitions = method.GetParameters();
            
Type[] parameters = new Type[parameterDefinitions.Length];

            
for (int index = 0; index < parameterDefinitions.Length; index++)
            {
                parameters[index] = parameterDefinitions[index].ParameterType;
            }

            
return (ConstructDynamicMethod(name, method, typeof(T), parameters, typeof(K)).CreateDelegate(typeof(T)) as T);
        }

        
/// <summary>
        /// Generates a new method with a signature that matches the signature of the delegate T.
        /// </summary>
        /// <typeparam name="T">The type of the delegate to return and use as a method signature.</typeparam>
        /// <typeparam name="K">The type of the object with which the generated method will be associated.</typeparam>
        /// <param name="name">The identifying name of the generated method.</param>
        /// <param name="instance">The instance of type K to which the method will be bound.</param>
        /// <returns>A delegate of type T which points to the generated method</returns>
        public T Generate<T, K>(string name, K instance) where T : class
        {
            
MethodInfo method = GetMethod(typeof(T));
            
ParameterInfo[] parameterDefinitions = method.GetParameters();
            
Type[] parameters = new Type[parameterDefinitions.Length + 1];

            parameters[0] =
typeof(K);
            
for (int index = 0; index < parameterDefinitions.Length; index++)
            {
                parameters[index + 1] = parameterDefinitions[index].ParameterType;
            }

            
return (ConstructDynamicMethod(name, method, typeof(T), parameters, typeof(K)).CreateDelegate(typeof(T), instance) as T);
        }

        
private DynamicMethod ConstructDynamicMethod(string name, MethodInfo method, Type delegateType, Type[] parameters, Type ownerType)
        {
            
DynamicMethod dm = new DynamicMethod(name, method.ReturnType, parameters, ownerType);
            
ILGenerator il = dm.GetILGenerator();

            
if (CreatingMethodBody != null)
            {
                CreatingMethodBody(
this, new CreatingMethodBodyEventArgs(name, delegateType, ownerType, il));
            }
            
else
            {
                il.Emit(
OpCodes.Ret);
            }

            
return dm;
        }

        
private MethodInfo GetMethod(Type delegateType)
        {
            
if (!delegateType.IsSubclassOf(typeof(Delegate)))
            {
                
throw new ArgumentException("Type T must be a delegate", "T");
            }
            
return delegateType.GetMethod("Invoke");
        }
    }

    
public class CreatingMethodBodyEventArgs : EventArgs
    {
        
private string _name;
        
private Type _delegateType;
        
private Type _ownerType;
        
private ILGenerator _generator;

        
public Type DelegateType
        {
            
get { return _delegateType; }
        }

        
public ILGenerator ILGenerator
        {
            
get { return _generator; }
        }

        
public Type OwnerType
        {
            
get { return _ownerType; }
        }

        
public string Name
        {
            
get { return _name; }
        }

        
public CreatingMethodBodyEventArgs(string name, Type delegateType, Type ownerType, ILGenerator ilGenerator)
        {
            _name = name;
            _generator = ilGenerator;
            _delegateType = delegateType;
            _ownerType = ownerType;
        }
    }
In the Generate function the first generic type parameter specifies the delegate to imitate.  The second generic type parameter is the "owner" of the generated function.  The return value will be a strongly typed delegate (no more Invoke(new object[]... stuff).  When it is time to generate the body of the method, the class fires off an event (CreateingMethodBody), that allows the caller to specify the body of the method.  Unfortunately you still have to use the ILGenerator class to generate the method body.  There are still some issues with the above code too.  
  1).  If T is a type other than System.Delegate or subclass, it will throw a runtime error not a compile time error.  This is because there is no way to constraint the type parameter to only System.Delegate.  It just isn't allowed.
  2)  If the caller of the class doesn't specify a method body or specifies an incorrect body (wrong return type, incorrect code, etc), the error won't be caught until the CreateDelegate method is called on the DynamicMethod, so runtime again, not compile time.
  3)  I still haven't thought of a good use for it ;)  

posted on Wednesday, March 29, 2006 1:08 PM

Feedback

# re: Strongly Typed DynamicMethods 3/29/2006 2:19 PM Wesner Moise
You can always circumvent compile-time cast checks by using

(Delegate)(object) expr

instead of

(Delegate) expr.

The object cast should be a no-op with no performance cost.


# re: Strongly Typed DynamicMethods 9/12/2006 8:02 AM Dave dolan
I've thought of a good use for it... creating delegates for property accessors on types that are not known at runtime. It's been my obsession lately, because I need to cut out the boxing I've been doing with the same type of thing by casting everything to objects ad infinitum.

Post Feedback

Title:
Name:
Email: (never displayed)
Url:
Comments: 
Please add 5 and 5 and type the answer here: