8

I'm working with a function that takes two functions as parameters, and returns a new composed one:

public static Action<T> Compose<T>(Action<T> first, Action second)
{
    return new Action<T>(arg =>
    {
        first(arg);
        second();
    });
}

I've noticed that the compiler complains if I don't specify T, when sending it a static or member function (as opposed to an actual Action<T> object):

static void Main(string[] args)
{
    // compiler error here
    var composed = Compose(Test, () => Console.WriteLine(" world"));
    composed("hello");

    Console.ReadLine();
}

public static void Test(string arg)
{
    Console.Write(arg);
}

The error message:

The arguments for method 'ConsoleTest.Program.Compose(System.Action, System.Action)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

My question: Why can't the type argument be inferred here? The signature of Test is known at compile time, is it not? Is there really some function you could put in place of Test, that would cause its signature to be ambiguous?

Footnote: I know I can simply send new Action<string>(Test) instead of Test to Compose (as noted in this question) -- my question is "why", not "how can I do this".

3
  • 1
    FYI -- Compose<string> fixes it as well. Commented Sep 25, 2013 at 21:26
  • 3
    See this answer: stackoverflow.com/questions/6229131/… Commented Sep 25, 2013 at 23:14
  • Thanks @lukegravitt -- the top answer there references the language spec, and Lippert himself chimes in. Commented Sep 25, 2013 at 23:49

2 Answers 2

8

I suppose it probably has something to do with the fact that, at least from the compiler's perspective, Test is actually a 'method group' until the compiler has determined what it types of parameters it will have. This is true even if there is only one method in the group (only one Test method in the current scope).

Observe:

var composed = Compose<object>(Test, () => Console.WriteLine(" world"));

yields the error:

The best overloaded method match for 'Compose<object>(System.Action<object>, System.Action)' has some invalid arguments

Argument 1: cannot convert from 'method group' to 'System.Action<object>'

But this is fine:

var composed = Compose<string>(Test, () => Console.WriteLine(" world"));

My guess is that the compiler see the both method group expression (Test) and implicitly typed generic method invocation (Compose) as 'unbound' in a sense. It can't fully determine which method to select from the method group from the type 'unbound' signature of the parameter to Compose, and it can't determine which the type type parameter for Compose from the signature. It needs one or the other to be 'bound' in order to compile the whole statement.

Sign up to request clarification or add additional context in comments.

2 Comments

It seems you are on the right track ... there's more discussion along these lines, here: stackoverflow.com/a/6231921/1001985
Lambda functions work well in these cases if you specify the types of the lambda's parameters, but then again that's usually no easier than specifying the type argument explicitly.
0

It might have to do with covariance. Although the type of the argument of Test is known, you might want to create a delegate of a more specific type.

public class BaseClass { }
public class DerivedClass : BaseClass { }

static class Program
{
    static void Main(string[] args)
    {
        var composed = Compose<DerivedClass>(Test, () => Console.WriteLine(" world"));
        composed(new DerivedClass());

        Console.ReadLine();
    }

    public static void Test(BaseClass arg)
    {
        Console.Write(arg);
    }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.