..and a side of fries.
I have a codebase that I'm compiling for both Windows and MonoTouch. In the wee hours of the morning I coded something like this contrived example which compiles on MonoTouch but fails on Windows:
void Run()
{
// okay on both
exec("hello", 1);
// okay on MonoTouch
// compiler error on windows
exec("hello");
}
interface IFace { void foo(); }
void exec(string s, int n=0)
{
Console.Write("A");
}
void exec<T>(T t) where T:IFace
{
Console.Write("B");
}
On MonoTouch, this compiles and runs, printing:
AA
On Windows, this example gives a compile-time error:
The type 'string' cannot be used as type parameter 'T' in the generic type or method 'App.Program.exec<T>(T)'. There is no implicit reference conversion from 'string' to 'App.Program.IFace'.
The C# spec on 7.4.2 Overload Resolution says that an 7.4.2.1 Applicable function member must have an identical number of arguments:
The number of arguments in A is identical to the number of parameters in the function member declaration. 7.4.2.1
So it would seem the MonoTouch compiler is considering default arguments when searching for applicable function members, but the Windows compiler is not. So the candidate function members are:
// exec with no default parameters. not applicable because no int supplied
void exec(string,int);
// exec with default value for the second parameter.
// only considered on MonoTouch.
void exec(string,int=0);
// generic exec with string as the type, which is invalid
// due to the IFace constraint. Invalid on both platforms.
void exec<string>(string) : where T:IFace;
So, is this a bug in the Applicable Function Member search on MonoTouch, or should the Windows compiler consider the default-parameterized non-generic method as valid?
Cheers, cm
EDIT
After dlev
's answer, I tested both the constrained and unconstrained generic methods, and it appears the Mono compiler chooses the correct method in the unconstrained case. In the constrained case, it appears that the Mono compiler is either considering the constraint or backtracking to find an alternative when the constraint fails.
The problem/bug reduces to:
void Run()
{
foo(1);
bar(1);
}
void foo(int a, int b = 0) { print("A"); }
void foo<T>(T t) { print("B"); }
void bar(int a, int b=0) { print("X"); }
void bar<T>(T t) where T : IFace { print("Y"); }
On both Windows and MonoTouch, foo
correctly prints B
. But bar
fails to compile on Windows yet prints X
on MonoTouch.
EDIT2
For those interested, my solution was to remove the default parameters and require explicit invoke. In my actual system, the type constraint specifies two interfaces, so I can't easily change the generic call to exec(IFace t) { ... }
. I suppose I could refactor, but this is the meat of my system and the following solves my current compile problems:
void exec(string a) { exec(a,0); }
void exec(string a, int b) { ... }
void exec<T>(T t) where T : IFace, IFace2 { ... }
Double Cheers, cm