Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using implicit conversion operators in combination with switch

Tags:

c#

Not clear behavior, if I declare type:

struct Token
{
    public static implicit operator int(Token x)
    {
        return 0;
    }

    public static implicit operator string(Token x)
    {
        return "";
    }
}

We have two implicit conversion. Work file if I use

var t = new Token();

if (t == "123")
{

}

CS0151: A switch expression or case label must be a bool, char, string, integral, enum, or corresponding nullable type

if I use:

switch (t)
{
    case "123" :
    {
        break;
    }
}

But if I remove int implicit conversion then error disappears.

struct Token
{
    public static implicit operator string(Token x)
    {
        return "";
    }
}

It is bug in compiler or correct behavior?

like image 572
Maxim Kapitonov Avatar asked Dec 30 '25 05:12

Maxim Kapitonov


2 Answers

This is the correct behavior. From the spec, 8.7.2 The switch statement:

exactly one user-defined implicit conversion (Section 6.4) must exist from the type of the switch expression to one of the following possible governing types: sbyte, byte, short, ushort, int, uint, long, ulong, char, string. If no such implicit conversion exists, or if more than one such implicit conversion exists, a compile-time error occurs.


We can also see the (next version) compiler source code that implements the spec at Conversions.UserDefinedImplicitConversions.
Note that I've removed some of the code and comments, this is just to get the general idea:

UserDefinedConversionResult? exactConversionResult = null;

foreach (UserDefinedConversionAnalysis analysis in u)
{
    TypeSymbol tx = analysis.ToType;
    if (tx.IsValidSwitchGoverningType(isTargetTypeOfUserDefinedOp: true))
    {
        if (!exactConversionResult.HasValue)
        {
            exactConversionResult = UserDefinedConversionResult.Valid(u, best.Value);
            continue;
        }

        return UserDefinedConversionResult.Ambiguous(u);
    }
}

// If there exists such unique TX in suitableTypes, then that operator is the  
// resultant user defined conversion and TX is the resultant switch governing type.
// Otherwise we either have ambiguity or no applicable operators.
return exactConversionResult.HasValue ?
    exactConversionResult.Value :
    UserDefinedConversionResult.NoApplicableOperators(u);

Basically, the code iterates over the implicit conversions and keeps the first conversion it finds to a type that can appear in switch. If it finds another type it returns Ambiguous. If it finds exactly one type, it returns it.
Again, this is a short version of the code, the full code deals with more cases and barkwards compatibility.

like image 133
Kobi Avatar answered Dec 31 '25 19:12

Kobi


The compiler's confusion seems to be understandable. The switch statement can run on both int and string. You pass it a Token that contains implicit conversions to both. Which one should the compiler choose? It has no way to know which is preferred. Implicit constructors don't have priority.

In the if case, it's obvious you're comparing to a string, so that conversion is used. But the switch is ambiguous. The error isn't entirely clear, but I'm assuming that, not knowing which conversion to choose, it chooses neither, attempts to put the Token instance itself in the switch, and thus displays this error.

like image 33
Avner Shahar-Kashtan Avatar answered Dec 31 '25 17:12

Avner Shahar-Kashtan