Являются ли лямбды-конструкторы типами делегатов? - PullRequest
9 голосов
/ 22 сентября 2011

Я обнаружил, что AssertWasCalled из Rhino Mocks завершается ошибкой, когда я использую лямбду в качестве параметров для утверждаемого метода.

ТЕСТ:
_mockDoer.AssertWasCalled(x => x.Print(y => Console.WriteLine("hi")));

КОД ВНУТРЕННЕЙ СИСТЕМЫ ПОД ТЕСТОМ:
_doer.Print(y => Console.WriteLine("hi")));

Это заставило меня думать о лямбдах как о конструкторах для типов делегатов.

Я что-то упускаю, когда думаю о лямбдах как о конструкторах для типов делегатов?

Ответы [ 2 ]

9 голосов
/ 22 сентября 2011

Ну, на самом деле они не являются "конструкторами" ни при каком обычном использовании слова "конструктор".

Это выражения, которые можно преобразовать в типы делегатов или типы деревьев выражений - последнее важно при работе с LINQ вне процесса.

Если вы действительно спрашиваете, ожидается ли, что использование двух «эквивалентных» лямбда-выражений может создать неравные экземпляры делегата: да, это так. IIRC, в спецификации языка C # даже говорится, что это так.

Однако, если использовать одно и то же лямбда-выражение более одного раза, всегда не будет создавать различные экземпляры:

using System;

class Test
{
    static void Main(string[] args)
    {
        Action[] actions = new Action[2];
        for (int i = 0; i < 2; i++)
        {
            actions[i] = () => Console.WriteLine("Hello");
        }
        Console.WriteLine(actions[0] == actions[1]);
    }
}

На моем боксе, которые на самом деле печатают True - actions[0] и actions[1], имеют одинаковое значение - они ссылаются на один и тот же экземпляр. Действительно, мы можем пойти дальше:

using System;

class Test
{
    static void Main(string[] args)
    {
        object x = CreateAction();
        object y = CreateAction();
        Console.WriteLine(x == y);
    }

    static Action CreateAction()
    {
        return () => Console.WriteLine("Hello");
    }
}

Опять же, это печатает True. Это не гарантировано, но здесь компилятор фактически создал статическое поле для кэширования делегата в первый раз, когда это требуется - потому что он не захватывает никакие переменные и т. Д.

В основном это детали реализации компилятора, на которые вы должны не полагаться.

6 голосов
/ 22 сентября 2011

Просто чтобы расширить ответ Джона (отлично, как всегда): в текущих реализациях C #, если «* одна и та же» лямбда появляется в двух разных местах исходного кода , тогда две лямбды никогда не реализуются в равныеделегаты.Тем не менее, мы оставляем за собой право в будущем выявлять эту ситуацию и унифицировать ее.

Независимо от того, верно ли обратное, зависит также от реализации.Если два делегата создаются из лямбды в определенной позиции исходного кода, делегаты могут иметь или не иметь равенство.В некоторых сценариях делегаты не могут иметь равенство (например, потому что они закрыты для разных экземпляров локальных переменных).В ситуациях, когда для возможно равенство, большую часть времени они делают , но есть несколько случаев, когда мы могли бы выполнить эту оптимизацию и не делать этого, просто потому чтоКомпилятор еще не достаточно сложен.

...