Строки являются ссылочными типами, но они неизменны. Это позволяет им быть интернированными компилятором; везде, где появляется один и тот же строковый литерал, на один и тот же объект можно ссылаться.
Делегаты также являются неизменяемыми ссылочными типами. (Добавление метода к многоадресному делегату с использованием оператора +=
составляет присваивание ; это не изменчивость.) И, как и строки, существует «буквальный» способ представления делегата в коде с использованием лямбда-выражение, например:
Func<int> func = () => 5;
Правая часть этого оператора является выражением, тип которого Func<int>
; но нигде я не вызываю явно конструктор Func<int>
(не происходит и неявное преобразование). Поэтому я рассматриваю это как буквально литерал . Я ошибаюсь в своем определении «буквальный» здесь?
В любом случае, вот мой вопрос. Если у меня есть две переменные для, скажем, типа Func<int>
, и я назначаю одинаковые лямбда-выражения для обоих:
Func<int> x = () => 5;
Func<int> y = () => 5;
... что мешает компилятору рассматривать их как один и тот же объект Func<int>
?
Я спрашиваю, потому что в разделе 6.5.1 спецификации языка C # 4.0 четко указано:
Преобразования семантически идентичны
анонимные функции с тем же
(возможно, пустой) набор захваченного внешнего
переменные экземпляры к тому же
разрешены типы делегатов (но не
требуется), чтобы вернуть тот же делегат
пример. Термин семантически
идентичный используется здесь, чтобы означать, что
выполнение анонимных функций
будет во всех случаях производить то же самое
эффекты дают одинаковые аргументы.
Это удивило меня, когда я прочитал это; если это поведение явно разрешено , я бы ожидал, что оно будет реализовано. Но, похоже, нет. Это на самом деле поставило многих разработчиков в беду, особенно. когда лямбда-выражения были использованы для успешного присоединения обработчиков событий без возможности их удаления. Например:
class EventSender
{
public event EventHandler Event;
public void Send()
{
EventHandler handler = this.Event;
if (handler != null) { handler(this, EventArgs.Empty); }
}
}
class Program
{
static string _message = "Hello, world!";
static void Main()
{
var sender = new EventSender();
sender.Event += (obj, args) => Console.WriteLine(_message);
sender.Send();
// Unless I'm mistaken, this lambda expression is semantically identical
// to the one above. However, the handler is not removed, indicating
// that a different delegate instance is constructed.
sender.Event -= (obj, args) => Console.WriteLine(_message);
// This prints "Hello, world!" again.
sender.Send();
}
}
Есть ли какая-либо причина, по которой это поведение - один экземпляр делегата для семантически идентичных анонимных методов - не реализовано?