3

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"....

5
  • Why would you wrap a double into a class just to cast it to object? If this is just an example and you have a more complex class with a valid cast to double then why cast it to object. Basically you should really look at your design if you have these type of casting issues. Commented Oct 20, 2017 at 18:26
  • I agree that our code design in this area is questionable. Unfortunately redesigning at this point in the release is out of the question. I'm just trying to find a fix for a bug that hopefully somewhat robust and not super hacky. Commented Oct 20, 2017 at 18:39
  • @Ash That class itself is super hacky Commented Oct 20, 2017 at 18:43
  • You could do something like var dc = input as DoubleContainer; if(dc != null) return (double)dc; else return (double)input; I guess it really depends on what all you expect to pass into GetDouble. Commented Oct 20, 2017 at 18:47
  • "Is there anything I can do to my implementation of DoubleContainer what will make my first implementation of GetDouble() work" - nope, there is not. Commented Oct 20, 2017 at 18:52

2 Answers 2

3

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);
}
Sign up to request clarification or add additional context in comments.

2 Comments

That article you linked explained it very well.
@Ash: Glad to hear it! This is a very frequently asked question about casts.
1

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

1 Comment

DoubleContainer is a struct. So "input as DoubleContainer" will not compile. It needs to replace by "(DoubleContainer)input ".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.