无效的强制转换异常泛型

本文关键字:异常 泛型 转换 无效 | 更新日期: 2023-09-27 18:29:23

我遇到了这个问题,我正在使用反射从类中提取属性,但问题是反射将它们作为对象返回,我无法将其放入我的实际类型中。

举个例子,如果这是类:

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; }
}

从一说Row<bool>Row<object>作品的选角:

    var a = new Row<bool>
    {
        Name = "Foo",
        Value = true
    };
    var b = (Row<object>)a; // Works

但是当我尝试从object转到Row<object>时,它似乎忽略了我的显式运算符并抛出了一个System.InvalidCastException:

var c = (object) a; // Simulate getting from reflection
var d = (Row<object>) c; // System.InvalidCastException

我错过了什么?

无效的强制转换异常泛型

使用 dynamic 而不是 object 强制运行时实际类型检查:

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

它将调用您的Row<T> -> Row<object>铸造操作员。

这里的问题是,除非在尝试强制转换的值的静态类型上定义了转换运算符,否则转换不会查找转换运算符。在您的示例中,c的静态类型是object的,并且object既不派生自转换运算符,也不具有要Row<object>的转换运算符,从而导致运行时异常。

看起来这个问题可以很容易地通过更好的设计来回避。

您希望将任何类型的Row<T>视为Row<object>,而转换运算符所做的只是解决这些类型不分层相关的事实。那么,为什么不使它们相关并首先避免问题呢?

例如:

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; }
    }
}

这似乎可以满足您的需求:

  • 转换问题已解决,因为您现在可以将任何类型的Row<T>转换为基类Row(在初始设计中接管Row<object>的责任(,并且无论Value是什么类型,都可以轻松访问NameValue
  • Row.Value二传手受到保护,因此您不能将Row<int>投向Row并进行Value,例如从外部进行string,从而保持类型安全。

您可以通过反射来实现此目的:

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)
        };
    }
}

你可以用以下方式来称呼它:

var d = RowHelper.LoadRow(c);