Here there be tigers – operator overloading and conversions


I came across a discussion in the last few days that I thought I would share with you. It concerns the following code:


class Utility
{
    public void Process(object o);
    public void Process(string s);
}


and then a call to Process that passed in a string.


C# does permit this sort of overloading, and it has some complex rules (see 7.4.2.2 in the spec for the details) to decide which is the better conversion, and therefore which function will be called. The rules are complex – I wrote our compiler conformance suites for this, and I still have to diagram it to get it right.


It’s string here, which probably didn’t surprise anybody. For classes, the rules will prefer the more-derived/more-specific type.


But what about the following:


public void Process(int i);
public void Process(uint j);

ushort x = 55;
inst.Process(x);


Which method is called?


The answer is “Process(int i)“, and this happens because, the language always prefers conversions to the signed types over conversions to unsigned types (a preference one way or the other is required to make things coherent in the presence of both signed and unsigned types).


Behavior like this can be more than a little confusing to users of a class. As language designers, we have to make sure that there’s a well-defined behavior in cases such as these, but that doesn’t mean that you have to create such cases.


My advice:


Think deeply before overloading on two types that have conversions defined between them. If you do need to overload on numeric types – say you’re writing something like String.Format – then you need to overload on all the types.

Comments (5)

  1. CausticMango says:

    Hmm, good point. However, I’d have to question the intent for such an ambiguous overloading to begin with. It may be legitimate (aka a visitor implementation), but it’s definitely suspicious.

    There may be better ways to skin that cat (such as using the template method pattern and avoiding the ambiguous overload).

  2. Joe McRay says:

    Hi Eric!

    class Utility

    {

    public void Process(object o);

    public void Process(string s);

    }

    And what about

    Utility.Process(null);

    ?

    How does the compiler decide in this situation?

    I think/know there must be rules, nevertheless I couldn’t find any topic in the MSDN Lib (7.4.2.2). Maybe you can give me a hint.

    Thx. Joe.

  3. Eric says:

    Joe,

    I think that case falls out from the rules. There’s an implicit conversion from null to object, and to string (by the reference conversion rules).

    There’s an implicit conversion from string to object, but not from object to string, so Process(string s) is better.

  4. Keith says:

    Joe,

    You need to cast the null to either Object or String (I have a couple of cases in some uses of String.Format where I have this problem in addition to having to do it sometimes when using ? : notation. Well, it’s not really a problem, since it is a very rare case for me that is an error not a warning, so I’ve never had an issue knowing which was going to be executed)

  5. MBA says:

    Helpful For MBA Fans.

Skip to main content