Pull to refresh

Comments 15

Если метод экземплярный то в поле target записывается ссылка на экземпляр-владелец метода (он нужен нам, ведь если метод экземплярный то это как минимум подразумевает работу с полями этого объекта target), а в methodPtr ссылка на метод. Если метод статический то записываются в поля target и methodPtr будут записаны null и ссылка на метод соответственно.

Для статических методов можно точно так же указывать target. В таком случае он станет первым аргументом метода (соответственно, у метода должно быть на один параметр больше, чем в сигнатуре делегата).


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




Также не могу не напомнить, что лямбда-выражение не обязано быть делегатом, оно может быть и типа Expression<>

В обратную сторону тоже работает и экземплярные методы можно использовать как статические (без сохранения target). А при вызове передавать объект первым параметром.

А можно поподробнее? А То я в свое время велосипедил трамплин, что не очень удобно:


public static MethodInfo CompileToInstanceMethod(this LambdaExpression expression, TypeBuilder tb, string methodName)
{
    var paramTypes = expression.Parameters.Select(x => x.Type).ToArray();
    var proxyParamTypes = new Type[paramTypes.Length - 1];
    Array.Copy(paramTypes, 1, proxyParamTypes, 0, proxyParamTypes.Length);
    var proxy = tb.DefineMethod(methodName, MethodAttributes.Public | MethodAttributes.Virtual, expression.ReturnType, proxyParamTypes);
    var method = tb.DefineMethod($"<{proxy.Name}>__StaticProxy", MethodAttributes.Private | MethodAttributes.Static, proxy.ReturnType, paramTypes);
    expression.CompileToMethod(method);

    proxy.GetILGenerator().EmitCallWithParams(method, paramTypes.Length);
    return proxy;
}

Странное у вас какое-то решение. У меня не получалось заставить LambdaExpression иметь TypeBuilder первым аргументом...


А с делегатом — все просто. Если вам нужна не реализация какого-то интерфейса или переопределение виртуального метода, а просто метод чтобы вызвать его через делегат — то этот метод объявляется в статическом классе, а контекст передается ему первым параметром.

Не знаю, что странного, вроде со своей задачей справлялся. Правда, потом переносил на Core, а там выпилен CompileToMethod, к сожалению. Обсуждается его возврат, но покааа там разберутся…


В такой формулировке знал, но изначально подумал, что чего-то я пропустил в этой жизни и можно сильно упростить себе жизнь. Но — нет :)

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

Func<> и Action<> — это такие же делегаты, вот их определение из referencesource:


public delegate void Action();
public delegate void Action<in T>(T obj); 
public delegate void Action<in T1,in T2>(T1 arg1, T2 arg2);
public delegate void Action<in T1,in T2,in T3>(T1 arg1, T2 arg2, T3 arg3);
public delegate void Action<in T1,in T2,in T3,in T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);

Но лучше их использовать только там, где смысл их аргументов понятен из контекста. Пользовательский делегат позволяет указать имена для параметров — а это важная часть самодокументируемого кода.


А еще Func<> и Action<> не могут иметь ref или out-параметров, а также для их параметров нельзя указать пользовательские атрибуты.

Абсолютно согласен, хотя на самом деле в статье есть абзац про их Generic версии, которые по моему более универсальны. Подсветил их жирным шрифтом после прочтения вашего комментария)
Перечитайте свой текст заново. Много ошибок, большинство по невнимательности.

А за статью спасибо, содержательная статья.
Большое спасибо, поправил что нашел)
Получается довольно бессмысленно, но работает.
void MyFunc(myDelegate deleg, int arg){deleg.Invoke(arg);}

Очень даже не бессмысленно.


Пусть нам нужно выполнить тяжёлую обработку большого набора данных. Такой метод может распараллеливать вычисления автоматически между потоками (PLINQ, Parallel.ForEach) или компьютерами (Ignite):
IEnumerable<TRes> Apply<TArg, TRes>(Func<TArg, TRes> func, IEnumerable<TArg> args)


P.S. .Net -> .NET

Ну блин, так хорошо начали…
MyDeleg myDeleg = (string x) => { x + "world!"};
CS1643 Not all code paths return a value in lambda expression

Правило простое, если лямбду поместить в { } (блок кода?), например, то она автоматически становится анонимной функцией и снова требует ключевое слово return:
MyDeleg @delegate = x => { return x + "world!"; };


Верно, даже не буду оправдываться, а просто поправлю текст статьи. Спасибо)

Я то думал тут будет действительно шпаргалка, а тут целая глава Рихтера:). Спасибо за статью, повторение мать учения!

Sign up to leave a comment.

Articles