C#如何将对象转换为运行时类型?

3
我该如何将一个object转换为其运行时类型(如果无法转换,可以转换成另一种类型)? 我可以使用反射获取runtimeType,但是无法强制使用property.GetValue获取List<string> - 我知道我可以显式地使用as List<String>,但这是一个类似软件代码引擎的东西,在运行时未知类型。
感谢任何帮助 - 谢谢!(但请提供代码解决方案,而非“你不应该这样做…”的答案)
// trimmed-down example class
class Bus { public List<string> Passengers {get;set;} }

// create new instance of class
var obj = new Bus();

// get property value using reflection
var listPropertyName = "Passengers";
var property = GetType(obj).GetProperty($"{listPropertyName}");
var runtimeType = property.PropertyType;

// returns object, but I want List<string> instead 
// (or whatever it is; could be different types like T, List<T>, etc.)
var val = property.GetValue(obj);

// doesn't work (assigns null), expects compile-time type
var val = property.GetValue(obj) as runtimeType; 

我强烈怀疑你认为C#中的var与JavaScript中的相同(这显然是不正确的),或者与dynamic相同(这也是不正确的)...澄清你真正想要对属性值做什么可能有助于可能的解决方案(动态调用泛型方法,dynamic,纯反射,代码生成等)。 - Alexei Levenkov
旁注:我希望你明白,property 的运行时类型不一定与 runtimeType 相同... - Alexei Levenkov
@nothingisnecessary:为了学习,您需要确保自己没有陷入 XY 问题 的范畴。 - Wiktor Zychla
1
除非这是某个重复的东西 - 这很可能 - 我认为这个问题很有价值,因为这是我们许多人遇到的问题。编译器的限制是有益的,但在某些时候,我们不知道为什么会这样。如果我们能阻止编译器告诉我们该做什么,那么逻辑结论就是将每个地方都声明为“object”,或者切换到VB.NET。 - Scott Hannen
1
唯一真正的方法是当我们知道类型并将其强制转换为接口或基础类型时,这使我们能够使用预先指定的契约。或者我们需要检查类型并分支到一个知道如何处理它的地方,例如开关或某个方法重载解决方案。 - TheGeneral
显示剩余11条评论
1个回答

7

简而言之:

在运行时无法将某个对象转换为未知类型。这意味着,除非使用更多的反射技术,否则你无法知道对象的属性或方法是什么。在这种情况下,进行强制类型转换有何意义呢?


有时候我们需要处理在运行时才能确定的类型。但除非该类型根本不重要,否则该对象将毫无用处,除非我们可以将其转换为在代码中预先指定的某个类型,而不是在运行时指定。

在这行代码中:

var val = property.GetValue(obj); 
val的声明类型由编译器推断。因为GetValue返回的类型是object,所以val的类型也是object。但实际上这个对象的类型可以是stringintList<Foo>或其它任何类型。然而,你现在有一个类型为object的变量val
如果你可以这样做:
var val = property.GetValue(obj) as runtimeType;    

那么val的编译时类型会是什么呢?由于runTimeType是运行时值,它怎么可能有编译时类型呢?除非您使用更多的反射,否则该对象将无用。但是如果要调用该对象的方法或检查其属性,则不能不知道其类型。但是如果您打算这样做,那么进行强制转换就没有意义。

从您发布的代码的下游某个位置,您可能希望实际上对涉及其方法或属性的对象执行某些操作。这意味着需要知道您认为它是什么类型,并将其强制转换为该类型。这将是在您的代码中指定的类型,而不是在运行时确定的东西。

例如:

var val = property.GetValue(obj); // I know this is a string
var myString = (string)val;
var findX = myString.IndexOf("X");

我认为它是一个“字符串”,所以我将其强制转换为“字符串”。(其中可能会有额外的检查。)现在,我已经将其强制转换为声明为“字符串”类型的变量,并且可以将其用作字符串。(除非它不是一个“字符串”-那么就会抛出运行时异常。)
这就是强制转换的好处-它允许我们将某些东西分配给由我们指定类型的强类型变量,然后相应地使用它。强制转换到未知类型对我们没有好处。
冒着增加混淆的风险:有人可能会说泛型表示在运行时未指定的类型,但这并不完全正确。List不指定T的内容,但如果你创建了List,不管在何时何地,你都必须在代码中指定类型。在绝大多数正常的场景中,我们必须了解对象的类型,否则该对象对于我们来说就是无用的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接