How do you directly type cast a boxed struct in C#? – Education Career Blog

I have a namespace of structs which represent various units of measure (Meters, Feet, Inches, etc.) … anout 12 in total, generated courtesy of T4 templates 🙂 .

Each struct carries implicit casting operators to support casting the value to any other measurement value-type, so the following sytax is legal:

var oneThousandMeters = new Meters(1000);    
Kilometers aKilo = oneThousandMeters ;     // implicit cast OK. Value = 1 Km

To add to the joy, there’s a catch-all class called Distance which can hold any unit of measure, and can also be implicitly cast to and from and measurement value…

var magnum = new Distance(12, DistanceUnits.Inches); 
Feet wifesDelight = magnum;               // implicit cast OK. Value = 1 foot.

Following the .NET framework standard, all string formatting and parsing is handled by external a FormatProvider, which implements ICustomFormatter. Sadly, this means that the value is boxed when it is passed to the Format method, and the format method needs to test the object against every known measurement type before it can act upon it. Internally, the Format method just casts the measurement to a Distance value anyway, so here comes the question….

Question:

public string Format(string format, object arg, IFormatProvider formatProvider)
{
    Distance distance;           

    // The following line is desired, but fails if arg != typeof(Distance)   
    distance = (Distance)arg;    

    // But the following tedious code works:
    if(arg is Distance)
       distance = (Distance)arg;
    else if(arg is Meters)
       distance = (Distance)(Meters)arg;     // OK. compile uses implicit cast. 
    else if(arg is Feet)
       distance = (Distance)(Feet)arg;       // OK. compile uses implicit cast. 
    else if(arg is Inches)
       distance = (Distance)(Inches)arg;     // OK. compile uses implicit cast. 
    else
        ... // tear you hair out for all 12 measurement types
}

Are there any solutions for this, or is this just one of those unsolvable drawbacks of value types?

PS: I’ve checked this post, and though the question is similar, it’s not what I’m looking for.

,

Well, it’s a matter of separating the unboxing conversion from the user-defined conversion. You want both to occur – and you have to specify the type to unbox, as well as letting the compiler know when you want a user-defined conversion. The user-defined conversion has to be picked at compile time unless you’re using dynamic typing, so the compiler needs to know which type it’s trying to convert from.

One option is to have an IDistance interface which all the structs implement. Then you could just use:

IDistance distanceArg = arg as IDistance;
if (distanceArg != null)
{
    Distance distance = distanceArg.ToDistance();
}

As you’ve got a boxed value already, using an interface won’t cause extra boxing or anything like that. Each ToDistance implementation can probably just use the implicit conversion:

public Distance ToDistance()
{
    return this;
}

… or you could make the conversion use ToDistance.

,

Yeah it’s just one of those things you have to live with.

You run into the same thing if you shove an integer into an object:

int a = 0;
object b = a;
int c = (int)b; // this works
short d = (short)b; // this fails
short e = (short)(int)b; // this works

Leave a Comment