List<Object>与List<dynamic>的区别

23

我需要创建一个异构对象(自定义类)的列表。我的第一个想法是创建一个List<ISomeMarkerInterface>,但我很快发现这不是我想要的。我的下一个想法是List<dynamic>,这似乎不是一个坏主意。然而,我做了一些研究,发现这篇关于装箱和拆箱的文章,并且在例子中,他们正在使用List<Object>来实现基本上我想要的功能。

除了dynamic会在运行时评估,Object会在编译时评估外,List<dynamic>List<Object>之间有什么区别呢?它们本质上不是一样的吗?


2
为什么需要将异构对象存储在同一个列表中?只是好奇... - Mitch Wheat
你应该寻找动态类型与对象的区别,请查看此线程,它可能回答了你的问题:https://dev59.com/VnA75IYBdhLWcg3wKlqM - Habib
我有一个类,其中一个属性是该类相关的项目集合,这些项目不是相同类型,但作为一组是相关的。 - Jason
顺便问一下,您能解释一下为什么 ISomeMarkerInterface 不好吗? - yamen
ISomeMarkerInterface 不是很好,因为(也许只有我这样),但我在将其强制转换回实际对象以执行任何有用操作时遇到了问题。由于标记接口没有属性或方法,所以在LINQ lambda表达式中使用它非常困难:\ - Jason
1
如果你只想要简单的LINQ,你是否看过使用List<Object>的Enumerable.OfType<T>()?http://msdn.microsoft.com/en-us/library/bb360913.aspx - Tanzelax
2个回答

27
在C#中有三种“通用”类型(虽然不是所有都是真正的类型):object、var和dynamic。
强调:object是一种类型。一个对象可以是object类型,并且该类型具有其方法,例如ToString()。由于所有类型都直接或间接继承自object,因此一切都可以向上转型为object。当你将一个对象分配给一个object引用时,就像将一个Elephant类型对象分配给一个Animal引用一样,其中Elephant从Animal继承而来,你正在进行向上转型。
SomeType x = new SomeType();
object obj = x;
obj.DoSomething();
  • obj在编译时被视为类型object,并且在运行时将是object类型(这是合理的,因为它是一个实际类型-obj声明为object,因此只能是该类型)。
  • obj.DoSomething()将导致编译时错误,因为object没有此方法,无论SomeType是否有该方法。

Var

这不是一个实际的类型,它仅仅是"编译器,请根据赋值右侧的类型来推断出类型"的简写。

SomeType x = new SomeType();
var obj = x;
obj.DoSomething();
  • obj 在编译时被处理为 SomeType 类型,在运行时也将是 SomeType 类型,就好像你写了 "SomeType" 而不是 "var" 一样。
  • 如果 SomeType 有一个方法 DoSomething(),则此代码将正常运行
  • 如果 SomeType 没有该方法,则代码将导致编译时错误

动态类型(Dynamic)

这是一种类型,告诉编译器在变量上禁用编译时类型检查。对象在编译时和运行时都被视为具有类型 dynamic

SomeType x = new SomeType();
dynamic obj = x;
obj.DoSomething();
  • 在编译时和运行时,obj的类型为dynamic
  • 如果SomeType有一个DoSomething()方法,那么这段代码可以工作
  • 如果SomeType没有这个方法,则代码可以编译通过,但在运行时会抛出异常
  • 注意,如果不小心使用dynamic,很容易引起异常:

public void f(dynamic x)
{ 
    x.DoSomething();
}
如果x的类型没有DoSomething方法,代码将抛出异常。但是仍然可以调用它并将任何对象作为参数传递而不会在编译时出现错误,这可能导致在运行时显示错误,并可能仅在特定情况下发生 - 可能存在潜在的错误。因此,如果您在类的任何公共接口中使用动态语言,您应该始终使用反射手动进行类型检查,小心处理异常或者根本不这样做。

注意:所引用的对象类型从未更改。虽然obj可能是object,但它所引用的x 仍然是SomeType


5
说“dynamic”不是一个真正的类型会被扣分是错误的(这与你正确指出的“var”非常不同,因为“var”并不是一个真正的类型)。在C#中,“dynamic”绝对是一种类型,甚至是一种静态类型,但是“dynamic”类型的对象将跳过静态类型检查。请参阅C#语言规范第4.7节或此链接(http://msdn.microsoft.com/en-us/library/dd264736.aspx)了解更多信息。 - Julian
哎呀,我修好了。我应该先检查一下的。:D 我以为动态类型在赋值运行时会立即解析为适当的类型,并且实际上从未被标记为“动态”,但显然情况并非如此。 - svinja
顺便提一下,如果你遇到了“missing compiler required member”编译器错误(https://connect.microsoft.com/VisualStudio/feedback/details/1630573/missing-compiler-required-member-microsoft-csharp-runtimebinder-csharpargumentinfo-create),那么你需要添加对`Microsoft.CSharp.dll`的引用。 - skst

2
区别在于,如果使用对象并尝试访问对象的某个成员,它将是一个编译时错误(因为对象没有这个成员)。为了使其工作,您需要知道类型并进行转换。
使用动态类型,您可以访问任何成员-没有编译时错误。如果成员在运行时不存在,则会出现运行时错误。如果您知道您的异构对象都有相同的成员,则应该采用这种方式。
但是,如果是这种情况,还有另一种更清晰的解决方案:您可以定义一个接口,并使用该成员使所有异构对象实现它,然后您的列表可以是List<IYourInterface>
请记住,动态类型的性能可能略差,因为涉及到动态类型解析。

使用动态语言,你能否在成员不存在时继续执行而不抛出错误?就像在JavaScript中可以这样做:if (data.something) {} - Jason
1
这被称为反射。你可以使用它与或不使用dynamic。查看GetType(),GetMethod()等。 - svinja
+1,如果你要使用反射路线,那么你最好直接使用Object(但我仍然建议在其上使用标记接口)。 - MattDavey

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