tags:

views:

67

answers:

3

For creating delegates on the fly, techniques vary from Delegate.CreateDelegate, to Expresion Lambda, DynamicMethod, etc. etc. All of these techniques require that you know the type of the delegate.

I'm trying to convert closed delegates to open delegates generically, and to do to achieve this it seems I need to dynamically create the type of the open delegate before I can actually create the resulting delegate. Consider:

pubic class WeakEvent<TDelegate> where TDelegate : class
{
     public WeakEvent(Delegate aDelegate)
     {
         var dgt = aDelegate as TDelegate;

         if(dgt == null)
             throw new ArgumentException("aDelegate");

         MethodInfo method = dgt.Method;
         var parameters = Enumerable
                         .Repeat(dgt.Target.GetType(),1)
                         .Concat(method.GetParameters().Select(p => p.ParameterType));

         Type openDelegateType = // ???  original delegate, with new 1st arg for @this

         var dm = new DynamicMethod("InnerCode", method.ReturnType, parameters);

         ... your favourite IL code emmisions go here

         var openDelegate = dm.CreateDelegate(openDelegateType);
     }
}

The purpsoe of the above code is to create a new delegate which is identical to the original delegate, but has a new 1st argument for this... i.e. an open version of the previously closed delegate.

Is there a simple way to clone & modify an existing delegate type, or is the nearest solution to build out the generic Func<> and Action<> types?

+1  A: 

New delegate with different signature is a new type. C# being type-safe it is not possible to do that - other than churning some code and compile on runtime which apart from memory leak is not such an elegant approach.

But what you can do is it choose appropriate delegate from the list of already made Action<> or Func<> based on the type of delegate. Alternatively create a lits of your own, based on your expected types, and choose that in runtime.

Aliostad
A: 

After a little experiemntation, I've found that the following code is about the best way to achieve what I was hoping for:

    private Type CreateOpenDelegate()
    {
        var parms = _method.GetParameters();
        bool hasReturn = _method.ReturnType != typeof (void);
        Type generic = GetGenericTypeForOpenDelegate(parms, hasReturn);

        var argTypes = new List<Type>(parms.Length + 2) {_method.DeclaringType};

        foreach (var arg in parms)
        {
            if(arg.IsOut || arg.IsRetval)
                throw new NotImplementedException();

            argTypes.Add(arg.ParameterType);
        }

        if(hasReturn)
            argTypes.Add(_method.ReturnType);

        var result = generic.MakeGenericType(argTypes.ToArray());

        return result;
    }

    private static Type GetGenericTypeForOpenDelegate(ParameterInfo[] parms, bool hasReturn)
    {
        if (hasReturn)
        {
            switch (parms.Length)
            {
                case 0:
                    return typeof (Func<,>);
                    break;
                case 1:
                    return typeof (Func<,,>);
                    break;
                case 2:
                    return typeof (Func<,,,>);
                    break;
                case 3:
                    return typeof (Func<,,,,>);
                    break;
            }
        }
        else
        {
            switch (parms.Length)
            {
                case 0:
                    return typeof (Action<>);
                    break;
                case 1:
                    return typeof (Action<,>);
                    break;
                case 2:
                    return typeof (Action<,,>);
                    break;
                case 3:
                    return typeof (Action<,,,>);
                    break;
            }
        }
        throw new NotImplementedException();
    }

This is a shame, because it means (as far as I can tell), that you can't dynamically [re]create delegates with ref or out arguments, since the generics Func and Action won't allow it.

Mark
A: 

See http://diditwith.net/PermaLink,guid,aacdb8ae-7baa-4423-a953-c18c1c7940ab.aspx for a great explanation of the topic and real world example. You can tune this solution to your specific needs.

Bazurbat