Reputation: 54117
I have a class (simplified for the purposes of this question) that wraps a decimal
value and uses a couple of implicit operator
declarations for converting between the type and the wrapped value:
private class DecimalWrapper
{
public decimal Value { get; private set; }
public DecimalWrapper(decimal value)
{
Value = value;
}
public static implicit operator decimal(DecimalWrapper a)
{
return a != null ? a.Value : default(decimal);
}
public static implicit operator DecimalWrapper(decimal value)
{
return new DecimalWrapper(value);
}
}
The usages of implicit operator
here allow things like these to be done:
DecimalWrapper d1 = 5; // uses implicit operator DecimalWrapper
DecimalWrapper d2 = 10;
var result = d1 * d2; // uses implicit operator decimal
Assert.IsTrue(result.Equals(50));
Assert.IsTrue(result == 50);
Now, consider a second class (again, simplified) that has an overloaded constructor that can take a decimal
or a DecimalWrapper
:
private class Total
{
private readonly DecimalWrapper _total;
public Total(DecimalWrapper total)
{
_total = total;
}
public Total(decimal totalValue)
{
_total = totalValue;
}
}
I would expect to be able to instantiate an instance of Total
by passing in an integer value, which would get converted to a decimal:
var total = new Total(5);
However, this results in a compiler error:
The call is ambiguous between the following methods or properties: 'Namespace.Total.Total(TypeTests.DecimalWrapper)' and 'Namespace.Total.Total(decimal)'
To fix this, you have to remove the implicit operator decimal
or specify that the value 5
is in fact a decimal
:
var total = new Total(5m);
This is all well and good, but I don't see why the implicit operator decimal
is relevant here. So, what is going on?
Upvotes: 3
Views: 87
Reputation: 70671
Are you looking for a citation from the language specification?
The cause of this has to do with overload resolution. When you specify an int
value as the constructor parameter, no overload is considered "best" because both require a conversion. The specification doesn't consider two levels of conversion different from one level, so the two constructor overloads are equivalent to each other.
As Blorgbeard noted in the comments, you can easily resolve the issue by getting rid of one of the constructors. He suggests removing the DecimalWrapper
overload, but since your field is of the DecimalWrapper
type, I'd get rid of the decimal
overload instead. Doing it this way, if you specify an int
value for the constructor, the compiler will implicitly convert to decimal
and then DecimalWrapper
for you. If you specify a decimal
value for the constructor, the compiler will implicity convert to DecimalWrapper
for that call, which is what your decimal
constructor would have done anyway.
Of course, yet another way to address the issue would be to add other constructors to the Total
class, e.g. one that takes an int
. Then no conversion is required and the int
constructor would be chosen. But this seems like overkill to me.
Upvotes: 3