Ash
Ash

Reputation: 379

Casting an object with a conversion operator fails

So I have this Object, say DoubleContainer.

public struct DoubleContainer
{
    private readonly double _value;

    private DoubleContainer(double value)
    {
        _value = value;
    }

    public static implicit operator double(DoubleContainer doubleContainer)
    {
        return doubleContainer._value;
    }

    public static DoubleContainer Create(double value)
    {
        return new DoubleContainer(value);
    }
}

Casting it works as expected in almost all cases, except where it's passed into a function as an Object.

The following code generates an InvalidCastException if I pass in a DoubleContainer:

public double GetDouble(Object input)
{
    return (double)input;
}

I can get it to work if I do use dynamic:

public double GetDouble(Object input)
{
   return (double)(dynamic)input;
}

My problem with this solution is that Visual Studio grays out the (dynamic), because it should be redundant, so someone may remove it. Also I don't know if there are any other places in the codebase where this same problem may occur.

Is there anything I can do to my implementation of DoubleContainer what will make my first implementation of GetDouble() work? I tried adding another implicit conversion operator from Object to DoubleContainer, but "user-defined conversions to or from a base class are not allowed"....

Upvotes: 3

Views: 115

Answers (2)

Evk
Evk

Reputation: 101443

You cannot make it work, because you can only unbox boxed struct (this is what you are doing with (double) input) to the exact undelying type, for the reasons best described in this article by Eric Lippert. So whenever you do (double) someObject - it will only work if object is actually a double, not int, not float , not DoubleContainer. If you expect other types - you can better use Convert.ToDouble. For that to work with your type you need it to implement IConvertible:

public struct DoubleContainer : IConvertible
{
    private readonly double _value;

    private DoubleContainer(double value)
    {
        _value = value;
    }

    public static implicit operator double(DoubleContainer doubleContainer)
    {
        return doubleContainer._value;
    }

    public static DoubleContainer Create(double value)
    {
        return new DoubleContainer(value);
    }

    public double ToDouble(IFormatProvider provider) {
        return _value;
    }

    public bool ToBoolean(IFormatProvider provider) {
        // delegate to your double
        return ((IConvertible) _value).ToBoolean(provider);
    }

    // ... rest is skipped ...

Then it will work with

public double GetDouble(Object input)
{
    return Convert.ToDouble(input);
}

Upvotes: 3

Gian Paolo
Gian Paolo

Reputation: 4249

When casting an object to double, compiler does not know that it should call your existing operator double, so it will not insert any call to your user defined operator double.

When you insert dynamic in the middle, compiler will generate something like "if this reference has an operator double, call it`, so it works.

So, it actually cannot work as long as you cast a System.Object to a double, while code suggested by @juharr, slightly modified, will work:

public double GetDouble(Object input)
{
    if (input is DoubleContainer)
    {
        var dc = (DoubleContainer)input; 
        return (double)dc;
    }
    return (double)input;
}

EDIT: modified code as per @SergiyKlimkov comment

Upvotes: 1

Related Questions