Как определить тип делегата во время выполнения (то есть динамический тип делегата) - PullRequest
3 голосов
/ 27 октября 2010

Для создания делегатов на лету методы могут варьироваться от Delegate.CreateDelegate, Expresion Lambda, DynamicMethod и т. Д. И т. Д. Все эти методы требуют, чтобы вы знали тип делегата.

Я пытаюсь преобразовать закрытые делегаты в открытых делегатов в общем случае , и для достижения этой цели мне кажется, что мне нужно динамически создать тип открытого делегата, прежде чем я действительно смогу создать результирующий делегат.Рассмотрим:

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);
     }
}

Целью приведенного выше кода является создание нового делегата, который идентичен исходному делегату, но имеет новый 1-й аргумент для this ... т.е.открытая версия ранее закрытого делегата.

Есть ли простой способ клонировать и изменить существующий тип делегата или является ближайшим решением для создания универсальных типов Func <> и Action <>?

Ответы [ 3 ]

1 голос
/ 27 октября 2010

После небольшого эксперимента я обнаружил, что следующий код - лучший способ достичь того, на что я надеялся:

    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();
    }

Это позор, потому что это означает (насколькоЯ могу сказать), что вы не можете динамически [повторно] создавать делегатов с ref или из аргументами, поскольку обобщенные функции Func и Action не позволят этого.

1 голос
/ 27 октября 2010

Новый делегат с другой подписью - новый тип.C #, будучи типобезопасным, сделать это невозможно, кроме как взбалтывать некоторый код и компилировать во время выполнения, что, помимо утечки памяти, не является таким элегантным подходом.

Но что вы можете сделать, это выбрать подходящего делегата из списка уже созданного Action <> или Func <> в зависимости от типа делегата.Или создайте свои собственные списки на основе ожидаемых типов и выберите их во время выполнения.

0 голосов
/ 07 апреля 2011

@ Марк - После второго примера кода вы сказали:

Это позор, потому что это значит (как насколько я могу судить), что вы не можете динамически создавать новые делегаты с ref или out аргументов, так как дженерики Func и Action не позволят это.

Это была именно моя проблема, см. Создание типа делегата C # с параметром ref во время выполнения для решения от пользователя Ani: Expression.GetDelegateType разрешает параметры ref.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...