C # лямбда-выражения и ленивая оценка - PullRequest
7 голосов
/ 05 января 2009

Одним из преимуществ лямбда-выражений является то, что вы должны оценивать функцию только тогда, когда вам нужен ее результат.

В следующем (простом) примере текстовая функция оценивается только при наличии писателя:

public static void PrintLine(Func<string> text, TextWriter writer)
{
    if (writer != null)
    {
        writer.WriteLine(text());
    }
}

К сожалению, это делает использование кода немного уродливым. Вы не можете вызывать его с помощью константы или переменной типа

PrintLine("Some text", Console.Out);

и должны называть это так:

PrintLine(() => "Some text", Console.Out);

Компилятор не может "вывести" функцию без параметров из переданной константы. Есть ли планы улучшить это в будущих версиях C # или я что-то упустил?

UPDATE:

Я сам нашел грязный хак:

    public class F<T>
    {
       private readonly T value;
       private readonly Func<T> func;

       public F(T value) { this.value = value; }
       public F(Func<T> func) {this.func = func; }

       public static implicit operator F<T>(T value)
       {
            return new F<T>(value);
       }

       public static implicit operator F<T>(Func<T> func)
       {
           return new F<T>(func);
       }

       public T Eval()
       {
           return this.func != null ? this.func() : this.value;
       }
}

Теперь я могу просто определить функцию как:

public static void PrintLine(F<string> text, TextWriter writer)
{
    if (writer != null)
    {
        writer.WriteLine(text.Eval());
    }
}

и вызовите его вместе с функцией или значением.

Ответы [ 6 ]

3 голосов
/ 05 января 2009

Я сомневаюсь, что C # получит эту функцию, но D имеет ее. То, что вы обрисовали в общих чертах, является подходящим способом реализовать ленивую оценку аргументов в C # и, вероятно, компилируется очень похоже на lazy в D и на более чистых функциональных языках.

Учитывая все вышесказанное, четыре лишних символа плюс необязательный пробел не являются исключительно большой ценой, чтобы заплатить за четкое разрешение перегрузки и выразительность в том, что становится языком со строгой типизацией для нескольких парадигм.

3 голосов
/ 05 января 2009

Вы можете использовать перегрузку: -

public static void PrintLine(string text, TextWriter writer)
{
    PrintLine(() => text, writer);
}
2 голосов
/ 13 августа 2011

К сожалению, уродливый синтаксис - это все, что у вас есть в C #.

«Грязный хак» из обновления не работает, потому что он не задерживает оценку параметров строки: они оцениваются перед передачей в operator F<T>(T value).

Сравнить PrintLine(() => string.Join(", ", names), myWriter) с PrintLine(string.Join(", ", names), myWriter) В первом случае строки объединяются, только если они напечатаны; во втором случае строки соединяются несмотря ни на что: только печать является условной. Другими словами, оценка совсем не ленивая.

1 голос
/ 05 января 2009

Компилятор очень хорош в выводе типов , он не хорош в выводе намерения . Одна из хитростей во всех новых синтаксических сахарах в C # 3 заключается в том, что они могут привести к путанице относительно того, что именно компилятор делает с ними.

Рассмотрим ваш пример:

() => "SomeText"

Компилятор видит это и понимает, что вы намереваетесь создать анонимную функцию, которая не принимает параметров и возвращает тип System.String. Это все вытекает из лямбда-выражения, которое вы ему дали. На самом деле ваша лямбда компилируется так:

delegate {
    return "SomeText";
};

и это делегат этой анонимной функции, которую вы отправляете на PrintLine для выполнения.

В прошлом это всегда было важно, но теперь с LINQ, лямбдами, блоками итераторов, автоматически реализуемыми свойствами, среди прочего, крайне важно использовать такой инструмент, как .NET Reflector Посмотрите на ваш код после его компиляции, чтобы увидеть, что действительно заставляет работать эти функции.

1 голос
/ 05 января 2009

Вы можете написать метод расширения для String, чтобы склеить его. Вы должны быть в состоянии написать «Some text» .PrintLine (Console.Out); и пусть это сделает всю работу за вас.

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

1 голос
/ 05 января 2009

Ну, эти два утверждения совершенно разные. Один определяет функцию, а другой - оператор. Запутать синтаксис было бы гораздо сложнее.

() => "SomeText" //this is a function

"SomeText" //this is a string
...