C# 3.0 Generic Type Inference - Passing a Delegate as a Function Parameter

C# 3.0 generic type inference - passing a delegate as a function parameter

Maybe this will make it clearer:

public class SomeClass
{
static void foo(int x) { }
static void foo(string s) { }
static void bar<T>(Action<T> f){}
static void barz(Action<int> f) { }
static void test()
{
Action<int> f = foo;
bar(f);
barz(foo);
bar(foo);
//these help the compiler to know which types to use
bar<int>(foo);
bar( (int i) => foo(i));
}
}

foo is not an action - foo is a method group.

  • In the assignment statement, the compiler can tell clearly which foo you're talking about, since the int type is specified.
  • In the barz(foo) statement, the compiler can tell which foo you're talking about, since the int type is specified.
  • In the bar(foo) statement, it could be any foo with a single parameter - so the compiler gives up.

Edit: I've added two (more) ways to help the compiler figure out the type (ie - how to skip the inference steps).

From my reading of the article in JSkeet's answer, the decision to not infer the type seems to be based on a mutual infering scenario, such as

  static void foo<T>(T x) { }
static void bar<T>(Action<T> f) { }
static void test()
{
bar(foo); //wut's T?
}

Since the general problem was unsolve-able, they choose to left specific problems where a solution exists as unsolved.

As a consequence of this decision, you won't be adding a overload for a method and getting a whole lot of type confusion from all the callers that are used to a single member method group. I guess that's a good thing.

C# Infer generic type based on passing a delegate

Basically, the type inference process described in section 7.5.2 of the spec is relatively weak when it comes to method group conversions. In the annotated standard, in section 7.5.2.6 which talks about Output Type Inferences - including method groups - there's an annotation from Vladimir Reshetnikov stating:

This step [method group output type inference] applies only if all method type parameters occurring in the delegate parameter types are already fixed. Overload resolution does not try to select the best method based on incomplete type information.

I believe that's exactly the problem here - sure, you've only actually got one method you can call and the method group only contains a single method, but the type inference process isn't quite powerful enough to tie the two together.

How to create a generic type on the Action delegate with a ByRef parameter?

No, you cannot create an Action<T> with a by-ref parameter, because that aspect is part of the delegate signature, which is pre-defined and outside of your control. You will have to define your own delegate type, as you have done with Foo. This delegate type could be generic, but cannot declare variance - neither covariance nor contravariance is compatible with by-ref parameters (of the variant type).

How can I pass in a func with a generic type parameter?

You cannot have instances of generic functions or actions - all type parameters are defined upfront and cannot be redefined by the caller.

An easy way would be to avoid polymorphism altogether by relying on down-casting:

public void SomeUtility(Func<Type, object, object> converter)
{
var myType = (MyType)converter(typeof(MyType), "foo");
}

If you want type safety, you need to defer the definition of the type parameters to the caller. You can do this by composing a generic method within an interface:

public void SomeUtility(IConverter converter)
{
var myType = converter.Convert<MyType>("foo");
}

interface IConverter
{
T Convert<T>(object obj);
}

Edit:

If the 'converter type' is known at the call-site, and only this type will be used inside the utility method, then you can define a generic type on the method and use that, just like other posters have suggested.

Is there a way to cause type inference of the delegate passed to Control.BeginInvoke?

The issue is that myMethodThatTakesNoParams isn't really a delegate but a so-called "method group" by the compiler. A method group isn't a real type in the CLR. It must be converted to delegate type to be used. When you use a method group like this:

Action a = myMethodThatTakesNoParams;

The compiler recognizes that you want to convert the method group to a delegate and inserts the conversion for you. It produces IL that effectively says:

Action a = new Action(myMethodThatTakesNoParams);

When you say:

Delegate d = myMethodThatTakesNoParams

The compiler doesn't really know what to do. It could theoretically pick any compatible delegate type for you, but C#, in general, does not insert types you did not use into expressions. Since it does not know what delegate you want the method group converted to, the compiler produces an error.

I used variable assignment in my examples, but the same logic applies for parameters to methods.

A work around would be to write your own extension method that has a specific delegate type in it:

static class ControlExtensions
{
public static IAsyncResult BeginInvoke(this Control c, Action a)
{
return c.BeginInvoke(a);
}
}

Generic inference of type parameter with constraint to other type parameter

You can't infer just some type parameters within a method call. Generic type inference either infers all type parameters, or none. There's no way of inferring THost from the parameters (there could be multiple classes which derive from HostBase<Config>), which means you basically can't use type inference for that method.

Looking at this specific example, I think you're going to find it tricky to use type inference at all, because of the way the relationships work.

type arguments can't be inferred from the usage for higher-order function

The details of the issue you are dealing with are answered by Eric Lippert on his blog, here.

Basically, as "David B" said in your comments, "IsNullOrWhiteSpace is a method group. The method group only has one participating member today, but it could get more in the future."

C# function that takes pointer to function as an in parameter without declaring any specific delegate types?

You can just use delegate if you want, although it's a bit old school :)

public void TestInvokeDelegate()
{
InvokeDelegate( new TestDelegate(ShowMessage), "hello" );
}

public void InvokeDelegate(TestDelegate del, string message)
{
del(message);
}

public delegate void TestDelegate(string message);

public void ShowMessage(string message)
{
Debug.WriteLine(message);
}

Passing Delegate object to method with Func parameter

It works if you pass a reference to the method directly:

Foo4(Foo1);

This is because actual delegates with the same shape are not inherently considered compatible. If the contracts are implicit, the compiler infers the contract and matches them up. If they are explicit (e.g. declared types) no inference is performed - they are simply different types.

It is similar to:

public class Foo
{
public string Property {get;set;}
}

public class Bar
{
public string Property {get;set;}
}

We can see the two classes have the same signature and are "compatible", but the compiler sees them as two different types, and nothing more.



Related Topics



Leave a reply



Submit