One important thing to know about implicit conversions in C#: they can exist but still fail at compile-time. Yes, I mean a very compile-time error, not a runtime exception. It can lead to subtle surprises in overload resolution. Let us look at examples:
1)
class A
{
static void Main()
{
B b = new B();
Foo(b);
}
static void Foo(A x) { }
static void Foo(IB x) { }
public static implicit operator A(B x)
{
return null;
}
}
interface IB {}
class B : IB
{
public static implicit operator A(B x)
{
return null;
}
}
The compilation of this program leads to the following compile-time error:
error CS0121: The call is ambiguous between the following
methods or properties: 'A.Foo(A)' and 'A.Foo(IB)'
The second overload Foo(IB) is obviously applicable, because there is an implicit conversion from the class B to the interface IB, which it implements. Indeed, if you comment out the first overload Foo(A), the compilation succeeds. But what will happen, if you comment out the second overload and leave Foo(A)? You will get an ambiguity in the conversion from B to A, because the compiler will find two user-defined conversion operators from B to A. Interesting part begins here! Does an implicit conversion from B to A exist? Is the method Foo(A) applicable?
The right answer is 'Yes'. The implicit conversion exist, but it is ambiguous and fails at compile-time. Nevertheless, the method Foo(A) is applicable. So, if you have both overloads, the invocation of Foo becomes ambiguous.
Note: If you have ReSharper installed, you might notice that its error analysis shows no errors in this code. So, it does not know about this subtlety. I has already logged a bug for this.2)
using System;
class A
{
static void Main()
{
Bar(Foo);
}
static void Foo(object x) { }
static void Bar(Action<object> x){}
static void Bar(Action<int> x){}
}
The compilation of this program leads to the following compile-time error:
error CS0121: The call is ambiguous between the following
methods or properties: 'A.Bar(System.Action<object>)'
and 'A.Bar(System.Action<int>)'
Again, the first overload Bar(Action<object>) is obviously applicable. If you comment out it and leave only Bar(Action<int>), you will get the following error:
error CS0123: No overload for 'A.Foo(object)'
matches delegate 'System.Action<int>'
This is correct, because a contravariance in parameter types works only for reference types. So, does an implicit conversion from the method group Foo to Action<int> exist? Again, we are tempted to answer 'No', but the correct answer is 'Yes. Exists, but fails an compile-time'. So, the overload Bar(Action<int> x) is applicable, and we have an ambiguity.
Note: Again, ReSharper is wrong here.There are many reasons, why an existing implicit conversion from a method group to a delegate type may fail at compile-time:
- Overload resolution cannot pick the best method from the method group being converted.
- The best method is generic, and constraints on its type parameters are not satisfied.
- The best method has a wrong return type.
- For at least one parameter, the conversion from the type of the parameter of the delegate to the type of the parameter of the best method is not an identity or an implicit reference conversion.
3)
using System;
using System.Linq.Expressions;
class A
{
static void Main()
{
Bar(x => { return x; });
}
static void Bar(Func<int,int> x) { }
static void Bar(Expression<Func<int, int>> x) { }
}
A lambda-expression with a statement body cannot be converted to an expression tree type, but the conversion still exists, since there is a conversion to the corresponding delegate type. The invocation of Bar is ambiguous.
Note: ReSharper is correct here.