views:

70

answers:

4

Hi,

Given the following MSDN sample code, why can't I define the Action delegate "inline":

public static void Main(string[] args)
{
    Action someAction = () => Console.WriteLine("Hello from the thread pool!");

    Task.Factory.StartNew(someAction);
}

...so "inline" like:

public static void Main(string[] args)
{
    Task.Factory.StartNew(Action someAction = () => Console.WriteLine("etc."));
}

Thanks,

Scott

+1  A: 

I wouldn't know tbh, but I think you can do:

public static void Main(string[] args)
{
    Task.Factory.StartNew(delegate() {Console.WriteLine("etc.");});
}
Anton
Yes, an annonymous delegate is acceptable in .NET 2.0 and works right up to 4.0...it's just that the Task Library is 4.0 and for the code inside it's nice to use C# 3.0 lambda's.
Scott Davies
+4  A: 

This isn't valid C#:

public static void Main(string[] args)
{
    Task.Factory.StartNew(Action someAction = () => Console.WriteLine("etc."));
}

Do this instead:

public static void Main(string[] args)
{
    Task.Factory.StartNew(() => Console.WriteLine("etc."));
}
Tim Robinson
Is the fact that the Action is omitted because it's redundant ?
Scott Davies
No... it's not valid C#. This is valid, although the `Action` is redundant: `Task.Factory.StartNew(new Action(someAction = () => Console.WriteLine("etc.")));`
Tim Robinson
@Scott: It is simply the same error as `Console.WriteLine(string s = "hello");`. You can't declare a variable everywhere.
Henk Holterman
@Henk: Ah, duh. That's right - a delegate is a type so declaring a type inside is incorrect. Thanks!
Scott Davies
+3  A: 

You're trying to delegate a variable within a method call. Just removing the variable declaration may be fine:

public static void Main(string[] args)
{
    Task.Factory.StartNew(() => Console.WriteLine("etc."));
}

Here the Action is inferred not from the lambda expression itself, but from the method call it's trying to make. Normal overload resolution is performed, and the compiler tries to convert the lambda expression to the relevant parameter type. If the parameter type were just Delegate (e.g. Control.Invoke) then type inference would fail because the compiler wouldn't have any concrete target types to try to convert to.

If that doesn't work (I can't easily test it atm) then you just need a cast to tell it which delegate type the lambda expression should be converted to:

public static void Main(string[] args)
{
    Task.Factory.StartNew((Action)(() => Console.WriteLine("etc.")));
}

To be honest though, at that point I'd prefer to see a separate variable in terms of readability.

Jon Skeet
Ah, ok, same as Quartermeister's comment about infering Action. How did you know that the compiler would infer Action ? Is this documented or more of a trial and error thing ? I was trying to following MSDN spec, but I obviously got it wrong.
Scott Davies
@Scott: I've edited my answer (the middle bit). It's all as per the specification.
Jon Skeet
@Jon: Ok, great - thanks! I will have to grab a copy of the spec and run through it again to refresh my memory.
Scott Davies
+2  A: 

You are including the declaration statement, which is not a legal expression. Try:

Task.Factory.StartNew(() => Console.WriteLine("etc."));

If you call an API where the type of the delegate can't be inferred, you can use a cast or call the delegate constructor explicitly:

Task.Factory.StartNew((Action)(() => Console.WriteLine("etc.")));
Task.Factory.StartNew(new Action(() => Console.WriteLine("etc.")));
Quartermeister
Ok, so the C# compiler is smart enough to infer "Action", but if not, it would require a type cast ?
Scott Davies
@Scott: This mostly happens if you have a method that takes in a Delegate, like Control.Invoke. Since any delegate type is legal, the compiler doesn't have a way to pick one.
Quartermeister