Внутренне, компилятор должен переводить лямбда-выражения в методы. В таком случае эти методы будут закрытыми или общедоступными (или что-то еще), и можно ли это изменить?
Компилятор С# рассматривает выражение лямбда как общедоступный или закрытый метод?
Ответ 1
Это зависит. В текущей версии Visual Studio методы, реализующие lambdas, никогда не являются общедоступными, но они не всегда являются частными. Простая программа для проверки некоторых версий lambdas:
public class Program
{
public static void Main()
{
var program = new Program();
Try("A", program.A);
Try("B", program.B);
Try("C", program.C);
Console.ReadKey();
}
private static void Try(string name, Func<Action> generator)
{
var mi = generator().Method;
Console.WriteLine($"{name}: DeclaringType={mi.DeclaringType}, Attributes={mi.Attributes}");
}
private Action A() => () => { };
private Action B() => () => { ToString(); };
private Action C()
{
var c = 1;
return () => c.ToString();
}
}
печатает
A: DeclaringType=Scratch.Program+<>c, Attributes=PrivateScope, Assembly, HideBySig
B: DeclaringType=Scratch.Program, Attributes=PrivateScope, Private, HideBySig
C: DeclaringType=Scratch.Program+<>c__DisplayClass4_0, Attributes=PrivateScope, Assembly, HideBySig
A
У lambda нет никаких захватов. Он создан как метод internal
для пустого класса замыкания.
B
lambda захватывает this
. Он создается как метод private
содержащего класса.
C
lambda захватывает C
. Он создан как метод internal
непустого класса замыкания.
Все это недокументировано и изменилось в прошлом, поэтому было бы неплохо не полагаться на него. Важно то, что когда вы вызываете анонимный метод, он ведет себя как указано. Если вам нужно что-то большее, вы не должны использовать анонимные методы. В зависимости от того, что вам нужно, вы также можете использовать lambdas, но с деревьями выражений, или вам может понадобиться создать обычные именованные методы.
Ответ 2
Внутри, компилятор должен переводить лямбда-выражения в методы.
Я предполагаю, что "лямбда" означает лямбду, преобразованную в тип делегата. Lambdas, преобразованные в типы дерева выражений, конечно, не генерируются как методы.
Компилятор действительно превращает такие лямбды в методы, да. Нет необходимости, чтобы он это делал, но делать это удобно.
В этом случае эти методы будут закрытыми или общедоступными (или что-то еще), и можно ли это изменить?
Вопрос несколько бессвязен. Предположим, я сказал вам, что лямбда является публичным методом. Он не имеет имени, доступного из С#; как бы вы воспользовались своей публичностью? Модификаторы доступности применимы к членам с именами. Само понятие домена доступности дает домен имени во время разрешения имени.
На практике, конечно, компилятор должен сгенерировать некоторые биты доступности для метаданных метода невостребованного по-вами. Методы, сгенерированные в классах замыкания, являются внутренними, так как это наиболее удобный способ сделать их доступными для проверки. Методы, созданные без закрытия, могут быть частными.
Опять же, ничто из этого не требуется, и все это детали реализации могут быть изменены. Вы не должны пытаться воспользоваться деталями генерации кода компилятора.
Ответ 3
Из CLR через книгу С# Джеффри Рихтера
Компилятор автоматически определяет новый закрытый метод в классе
... Компилятор автоматически создает имя метода
... анонимные методы, сгенерированные компилятором, всегда заканчиваются будучи приватным, и этот метод является либо статическим, либо нестатическим о том, обращается ли метод к любым членам экземпляра
Таким образом, метод объявляется как private
или internal
.
Например, код
class AClass {
public void SomeMethod() {
Action lambda = () => Console.WriteLine("Hello World");
lambda();
}
}
будет выдавать декларацию IL как
.field private static class [mscorlib]System.Action 'CS$<>9__CachedAnonymousMethodDelegate1'
Как вы можете видеть, это поле private static
.
Однако заметьте, что выражение лямбда может быть оптимизировано, если вы измените пример на
class AClass
{
string a = "Hello World";
public void SomeMethod()
{
Action lambda = () => Console.WriteLine(a);
lambda();
}
}
компилятор будет оптимизировать его, и вообще не будет лямбда-декларации
IL_0001: ldstr "Hello World"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
Ответ 4
Как упоминалось в @hvd, разница между выражением лямбда использует параметры из окружающей среды (закрытие) или нет. Смотрите: Почему некоторые выражения лямбда С# компилируются для статических методов?
Таким образом, вопрос имеет смысл только для случая несоблюдения, когда выражение лямбда может быть преобразовано в оболочку делегата без каких-либо внешних зависимостей.
Вы можете передать этот сгенерированный класс (который в основном обертывает делегат), и он всегда будет ссылаться на сгенерированный делегат в определяющей сборке. Таким образом, вы можете вызывать его из любой точки, если на сборку ссылаются.
Просто проверьте, выполняется ли передача и выполнение действия, определенного в другой сборке, хотя сам Action.Method
отмечен как внутренний.
// Main, first assembly
namespace ConsoleApplication1
{
public class B : IB
{
Action _action;
public void AddAction(Action act)
{
_action = act;
}
public void Invoke()
{
Console.WriteLine(_action.Target);
Console.WriteLine("Is public: {0}", _action.Method.IsPublic);
_action();
}
}
class Program
{
static void Main(string[] args)
{
var a = new A();
var b = new B();
a.AddActionTo(b);
b.Invoke();
Console.ReadKey();
}
}
}
В другой сборке:
namespace OtherAssembly
{
public interface IB
{
void AddAction(Action act);
}
public class A
{
public void AddActionTo(IB b)
{
Action act = () => { };
b.AddAction(act);
}
}
}