Строки являются ссылочными типами, но они неизменяемы. Это позволяет им быть интернированным компилятором; везде появляется один и тот же строковый литерал, на который можно ссылаться.
Делегаты также являются неизменяемыми ссылочными типами. (Добавление метода к делегату многоадресной рассылки с использованием оператора +=
представляет собой присвоение, это не изменчивость.) И, например, строки, существует "буквальный" способ представления делегата в коде с использованием лямбда-выражения, например:
Func<int> func = () => 5;
Правая часть этого оператора - выражение, тип которого Func<int>
; но я никоим образом не ссылаюсь на конструктор Func<int>
(а также неявное преобразование). Поэтому я рассматриваю это как по существу буквальное. Я ошибаюсь в своем определении "буквальный" здесь?
Независимо, вот мой вопрос. Если у меня есть две переменные для, скажем, типа Func<int>
, и я назначаю одинаковые лямбда-выражения для обоих:
Func<int> x = () => 5;
Func<int> y = () => 5;
... что мешает компилятору рассматривать их как один и тот же объект Func<int>
?
Я спрашиваю, потому что в разделе 6.5.1 спецификации С# 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();
}
}
Есть ли причина, почему это поведение - один экземпляр делегата для семантически идентичных анонимных методов - не реализован?