views:

1438

answers:

5

One advantage of lamda expressions is that you only have to evaluate a function, when you need its result.

In the following (simple) example, the text function is only evaluated when a writer is present:

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

Unfortunately, this makes using the code a little bit ugly. You cannot call it with a constant or variable like

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

and have to call it this way:

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

The compiler is not able to "infer" a parameterless function from the passed constant. Are there any plans to improve this in future versions of C# or am I missing something?

UPDATE:

I just found a dirty hack myself:

    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;
       }
}

Now i can just define the function as:

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

and call it both with a function or a value.

+1  A: 

Well those two statements are completely different. One is defining a function, while the other is a statement. Confusing the syntax would be much trickier.

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

"SomeText" //this is a string
Yuliy
I agree. In the case of adding an overload, it would be impossible to tell the difference [if such a shortcut were supported].
Krisc
+2  A: 

You could use an overload:-

public static void PrintLine(string text, TextWriter writer)
{
    PrintLine(() => text, writer);
}
AnthonyWJones
That defeats the purpose. The argument to the overload of PrintLine you've defined gets evaluated no matter what. Adding the lambda within serves only to unnecessarily deepen the call stack.
P Daddy
Not only that, you have to create 2^n overloads for n parameters.
Rauhotz
I think I misunderstood the OPs need. Turns out, adding an overload may actually be the right thing to do, although I think I'd reverse them (have the one taking a delegate call the one taking a value, rather than the other way around).
P Daddy
+1  A: 

You could write an extension method on String to glue it in. You should be able to write "Some text".PrintLine(Console.Out); and have it do the work for you.

Oddly enough, I did some playing with lazy evaluation of lambda expressions a few weeks back and blogged about it here.

plinth
In your blog, you're saying that there's no Func<void>. What about System.Action?
Rauhotz
+1  A: 

The compiler is very good at inferring types, it is not good at inferring intent. One of the tricky things about all the new syntactic sugar in C# 3 is that they can lead to confusion as to what exactly the compiler does with them.

Consider your example:

() => "SomeText"

The compiler sees this and understands that you intend to create an anonymous function that takes no parameters and returns a type of System.String. This is all inferred from the lambda expression you gave it. In reality your lambda gets compiled to this:

delegate {
    return "SomeText";
};

and it is a delegate to this anonymous function that you are sending to PrintLine for execution.

It has always been important in the past but now with LINQ, lambdas, iterator blocks, automatically implemented properties, among other things it is of the utmost importance to use a tool like .NET Reflector to take a look at your code after it is compiled to see what really makes those features work.

Andrew Hare
+1  A: 

I doubt that C# will get this feature, but D has it. What you've outlined is a suitable way to implement lazy argument evaluation in C#, and probably compiles very similarly to lazy in D, and in more pure functional languages.

All things considered, the four extra characters, plus optional white space, are not an exceptionally large price to pay for clear overload resolution and expressiveness in what is becoming a multi-paradigm strong-typed language.

P Daddy