views:

79

answers:

2

I'm trying to use a method group in a lambda expression, like this:

public class Foo { public void Hello(string s) { } }

void Test()
{
    // this works as long as Hello has a void return type
    Func<Foo, Action<string>> a = foo => foo.Hello;
}

When I change the return type of Hello to int, however, I get

'Bar.Hello(string)' has the wrong return type.

I've tried playing around with Func in place of Action, but that seems to prevent me from using the method group syntax.

Any ideas?

(My goal, fwiw, is to be able to refer to numerous methods that have different return types and lots of string arguments. I don't even intend to call them - I just want to reflect over their attributes. I do like the safety of lambdas, however, versus just typing in method name strings.)


Edit: to clarify my reasons for wanting to use Action<string>: int in my example may be any of a number of types. I tried templating that type --

void Set<T>(Func<Foo, Func<string, T>> a) { }
void Test() { Set(foo => foo.Hello); }  // fails

-- but the compiler can't derive T (presumably for the same reasons I can't overload on return type?).

Any other ideas? I'm not opposed in this case to some crazy reflection as long as I can get the compiler to check the name of that method group.

+7  A: 

When it has a non-void return type, it's no longer compatible with Action<string>. In other words, this would fail too:

int Foo(string s) { return 10; }

// Error: wrong return type
Action<string> action = new Action<string>(Foo);

For the reasons why this isn't allowed, see Eric Lippert's blog post on "The void is invariant".

You should be able to use method group syntax like this:

public class Foo { public int Hello(string s) { return 10; } }

void Test()
{
    Func<Foo, Func<string, int>> a = foo => foo.Hello;
}

That works for me in both VS2008 and VS2010. There have been some changes to method group conversions and type inference for C# 4 - the details escape me unfortunately - but I don't believe this case is affected by those changes.

Jon Skeet
Interesting blog post. Unfortunately, the return types of the values I wish to exist on the right side in my example vary. I've updated my post with a bit more info.
ladenedge
+5  A: 

With a void return type, foo.Hello is an Action<string>. With an int return type, it's now a Func<string, int>.

To handle multiple return types — and assuming you don't need to do anything with the return value — you can trivially wrap non-void functions thus:

Func<Foo, Action<string>> a = foo => s => foo.Hello(s);
Marcelo Cantos
Thanks for the answer - unfortunately, the methods I'm trying to "group" in my real program are all of different return types. I've updated my post with a bit more info.
ladenedge
@ladenedge: I've amended my answer to (hopefully) address your comment.
Marcelo Cantos
I like the idea generally, but the problem then is that I lose the method group syntax! (Or at least, I can't seem to get it to compile without explicitly passing `s`.) As I mentioned above not only do I have a bunch of return types, but I also have a bunch of string arguments - I'd really like to avoid passing all of them mechanically into every appearance of `Hello`.
ladenedge