2013-08-29 245 views
13

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

举个例子,如果是这样的类:

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 

但是当我尝试从objectRow<object>它似乎忽略我的显式操作符并抛出System.InvalidCastException:

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

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

我错过了什么?

+0

这样做var c =(Row )a; – Ehsan

+1

不确定你期望的 - 在你的示例中没有'明确的运算符行(object o)'... –

+0

这可能是因为显式运算符期望'行'而你给它一个'对象'。 –

回答

4

这里的问题是,铸造不找一个转换操作符,除非一个已定义的您正在尝试投射的值的静态类型。在您的示例中,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<object>的责任),并且无论Value是什么类型,都可以轻松访问NameValue
  • Row.Value二传手是受保护的,因此您不能投到并使Value例如,来自外部的string,维护类型安全。
+0

哇,现在只是测试这个设计,最初看起来确实解决了根本原因(不是很好的设计)。需要订购Jon Skeet的书... – Phil

6

使用dynamic代替object强制运行时实际类型检查:

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

它会打电话给你Row<T> -> Row<object>转换运算符。

+1

你不能声明'明确的操作符Row (object o)'。 –

+0

@AdamHouldsworth没错。 – MarcinJuraszek

+0

Visual Studio抱怨...所以我应该先把对象反射返回到动态然后我的类型? – Phil

2

你可以用反射做到这一点:

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