Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invalid cast exception generics

Tags:

c#

I'm having this issue, I'm using reflection to pull properties from a class but the problem is reflection returns them as an object and I can't get it into my actual type.

Take for example, if this is the class:

public class Row<T>
{
    public static explicit operator Row<object>(Row<T> o)
    {
        return new Row<object>
        {
            Name = o.Name,
            Value = o.Value
        };
    }

    public string Name { get; set; }

    public T Value { get; set; }
}

Casting from one say Row<bool> to Row<object> works:

    var a = new Row<bool>
    {
        Name = "Foo",
        Value = true
    };

    var b = (Row<object>)a; // Works

But when I try to go from object to Row<object> it seems to ignore my explicit operator and throw a System.InvalidCastException:

var c = (object) a; // Simulate getting from reflection

var d = (Row<object>) c; // System.InvalidCastException

What am I missing?

like image 683
Phil Avatar asked Aug 29 '13 08:08

Phil


3 Answers

Use dynamic instead of object to force runtime real type check:

var c = (dynamic)a;
var d = (Row<object>)c; // Works fine

It will call your Row<T> -> Row<object> cast operator.

like image 112
MarcinJuraszek Avatar answered Nov 18 '22 11:11

MarcinJuraszek


The problem here is that casting does not look for a conversion operator unless one is defined on the static type of the value you are trying to cast. In your example the static type of c is object and object neither derives from nor has a conversion operator to Row<object>, resulting in the runtime exception.

It looks like this problem can be easily sidestepped with a better design.

You want to treat any type of Row<T> as a Row<object> and the conversion operator does nothing more than work around the fact that these types are not hierarchically related. So why not make them related and avoid the problem in the first place?

For example:

public abstract class Row
{
    public string Name { get; set; }

    public object Value { get; protected set; }
}

public class Row<T> : Row
{
    public new T Value
    { 
        get { return (T)base.Value; }
        set { base.Value = value; }
    }
}

This seems to do what you want:

  • The casting problem is solved because you can now cast any type of Row<T> to the base class Row (which takes over the responsibilities of Row<object> in your initial design) and easily access Name and Value no matter what type the Value is.
  • The Row.Value setter is protected so you cannot cast a Row<int> to Row and make Value e.g. a string from outside, maintaining type safety.
like image 26
Jon Avatar answered Nov 18 '22 09:11

Jon


You can accomplish this with reflection:

public class RowHelper
{
    public static Row<object> LoadRow(object o)
    {
        var type = o.GetType();
        return new Row<object>
        {
            Name = (string)type.InvokeMember("Name", BindingFlags.GetProperty, null, o, null),
            Value = type.InvokeMember("Value", BindingFlags.GetProperty, null, o, null)
        };
    }
}

You would call this with:

var d = RowHelper.LoadRow(c);
like image 2
JLRishe Avatar answered Nov 18 '22 11:11

JLRishe