Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# why is this type inferred method call ambiguous? [duplicate]

Tags:

c#

The following pair of functions attempt to replicate the null conditional operator available in C# 6.0:

public static TResult Bind<T, TResult>(this T obj, Func<T, TResult> func)
    where T : class
{
    return obj == null ? default(TResult) : func(obj);
}

public static TResult Bind<T, TResult>(this Nullable<T> obj, Func<T, TResult> func)
    where T : struct
{
    return obj.HasValue ? func(obj.Value) : default(TResult);
}

The first function is constrained to classes and for a String s allows me to write something like:

var x = s.Bind(a => a.Substring(1));

The second function is where I am running into trouble. For example, given a int? number I would like to write:

var y = number.Bind(a => a + 1);

However, this gives me the following error:

The call is ambiguous between the following methods or properties: 'BindingExtensions.Bind<T, TResult>(T, Func<T, TResult>)' and 'BindingExtensions.Bind<T, TResult>(T?, Func<T, TResult>)'

I'm guessing that this has something to do with the interplay between the type inference of the anonymous function and the method overload resolution. If I specify the type of a as int than it compiles just fine.

var y = number.Bind((int a) => a + 1);

However, this is clearly less than desirable. Can anyone tell me why the compiler thinks the above call to bind is ambiguous and/or offer a way to fix this? I know I could simply name the two functions differently, but what fun is that?

like image 782
Micah Hahn Avatar asked Jul 27 '15 17:07

Micah Hahn


1 Answers

Overloaded functions cannot be disambiguated by type constraints (see "Generic constraints, where T : struct and where T : class"). Any nullable type N satisfies N : T and N : Nullable<T>, required by the former and latter Bind definitions respectively. I am guessing that number is of type Nullable<int> or similar.

var x = s.Bind(a => a.Substring(1));

This is unambiguous because s is of type string and for all T not string : Nullable<T>, so only the first overload is acceptable.

var y = number.Bind(a => a + 1);

This is ambiguous because the type of a => a + 1 may be inferred as either Func<int?,int?> or Func<int,int>. If inferred as Func<int?,int?> the first overload applies, and if inferred as Func<int,int> the second overload applies.

var y = number.Bind((int a) => a + 1);

This is unambiguous if number is of type Nullable<int>, for example. For the first overload for all T not T : Nullable<int> and T : int, so it does not apply. For the second overload you just need T : int which is easily satisfied by T = int.

like image 200
erisco Avatar answered Oct 22 '22 10:10

erisco