implicit operator causing ambiguous method signature resolution

86 views Asked by At

I have the following two methods that both create me a couple of Constraint<T> instances. The first creates types of Constraint<Quantity> and the second creates types of Constraint<IParameter<Quantity>>:

public void ConstrainBetween(Quantity min, Quantity max)
{
  .. 
}

public void ConstrainBetween(IParameter<Quantity> min, IParameter<Quantity> max)
{
  .. 
}

The argument types Quantity and IParameter<T> are like this:

public class Quantity
{
  double Value;
  .. 
}

public class IParameter<T> 
{
  T Value;
  Func<T> Formula;
  .. 
}

It all has worked nicely for months until i overloaded the implicit operator for IParameter as follows :

public class IParameter<T>
{
     public T Value;
     public Func<T> Formula;
  

     public static implicit operator T(IParameter<T> p)
     {
         return p.Value; 
     }
}

Now the following usage no longer compiles:

HydrModel.TopWaterLevel5yr.ConstrainBetween(OutletPipe.IL, Transform.Z);
HydrModel.TopWaterLevel30yr.ConstrainBetween(OutletPipe.IL, Transform.Z);

Error:

The call is ambiguous between the following methods or properties: 'QuantityInput.ConstrainBetween(Quantity, Quantity)' and 'QuantityInput.ConstrainBetween(IParameter, IParameter)'

I understand that the compiler implicitly converts IParameter<Quantity> to Quantity making both method signatures potential targets and I know that I can resolve the ambiguity problem by:

  1. not overloading the implicit operator
  2. using an explicit operator overload instead
  3. using two different method names

I don't want to to 1 or 2 because the benefits elsewhere of implicit overloading outweigh this drawback.

I am currently intending to take strategy (3) and work with different signature names.

I post this question just in case there is an alternative approach that would allow me to have my cake and eat it? For example is there some way to specify exactly the method signature in the call to invoke it?

EDIT

Someone asked why do i need both methods - why not just get rid of the second one since the implicit overload will ensure calling of the first?
The reason is that calling Constraint<Quantity>.Value always returns the same Quantity whereas calling Constraint<IParameter<Quantity>>.Value returns a different Quantity at different times according to its Formula property. Therefore I do need a strategy for creating a Constraint where T can be either Quantity or IParameter<Quantity>.

2

There are 2 answers

3
AgentFire On BEST ANSWER

If your IParameter<Quantity> is properly designed to be implicitly cast to Quantity, then you shouldn't have any need in the second method ConstrainBetween. The IParameter also seems to be just a simple DTO, carrying a single value, and not anything else.

So, my solution would be to get rid of public void ConstrainBetween(IParameter<Quantity> min, IParameter<Quantity> max) entirely and focus on the value in the first method, rather than on the value's carrying type.

Update

Since @MonkeyFace has edited his post, I would also like to add some thoughts.

It seems that now IParameter<T> doesn't carry a single value anymore, but two or more values. The difference described in the updated question between the two methods also indicates that IPerameter<T> is not an equivalent to its T value.

From this perspective it now seems odd to have an implicit operator, because (a) it loses that additional info (since losing information should only be done so deliberately - in my opinion) and (b) the two types are not equivalent to each other.

So the new proposed change would be to migrate from implicit operattor to explicit one.

I see your comment that "there are drawbacks of using explicit operatior", but I imagine those drawbacks being having to use (T) in those scenarios where the cast should occur, and I consider those cases the lesser evil.

0
MonkeyFace On

I seem to have answered my own question by changing the second signature from this:

public void ConstrainBetween(IParameter<Quantity> min, IParameter<Quantity> max)
{
}

to this:

public void ConstrainBetween<T>(T min, T max) where T : IParameter<Quantity>
{
}

which i guess is now allowing the compiler to find a method marked as having the matching type prior to implicit conversion. ...At any rate the project now compiles.

Although this is the solution I have opted for on this specific project in general I would agree with the marked solution for 90% of my projects.