列表<对象>与列表<动态>

本文关键字:列表 动态 对象 | 更新日期: 2023-09-27 18:32:51

我需要创建一个异构的对象List(自定义类(。我的第一个想法是创建一个List<ISomeMarkerInterface>但我很快了解到这不是我想要的。我的下一个想法是List<dynamic>,这似乎不是一个坏主意。但是,我正在做一些研究,并遇到了这篇关于装箱和拆箱的文章,在示例中,他们基本上使用 List<Object> 做我想做的事情。

除了dynamic将在运行时进行评估并在编译时Object之外,List<dynamic>List<Object>之间有什么区别?它们本质上不是一回事吗?

列表<对象>与列表<动态>

C# 中有 3 种"常规"类型(尽管并非所有类型都是真实类型(:对象、var 和动态。

对象

与任何其他类型一样,实际类型具有一个特殊规则:如果类型不继承,则从对象继承。由此可见,所有类型都直接或间接地继承自对象

强调:对象是一种类型。对象可以是 object 类型,并且类型有其方法,如 ToString((。由于一切都继承自对象,因此所有内容都可以向上转换为对象。将对象分配给对象引用时,就像将大象类型对象分配给大象从动物继承的动物引用一样。

SomeType x = new SomeType();
object obj = x;
obj.DoSomething();
  • obj 在编译时被视为对象类型,并且在运行时将被视为 object 类型(这是合乎逻辑的,因为它是实际类型 - obj 被声明为对象,因此只能是该类型(
  • obj.DoSomething(( 将导致编译时错误,因为对象没有此方法,无论 SomeType 是否有它。

瓦尔

这不是一个实际的类型,它只是"编译器,根据作业的右侧为我找出类型"的简写。

SomeType x = new SomeType();
var obj = x;
obj.DoSomething();
  • obj 在编译时被视为 SomeType 类型,在运行时将被视为 SomeType 类型,就像您编写了">SomeType"而不是"var"一样。
  • 如果 SomeType 有一个方法 DoSomething((,这段代码将起作用
  • 如果 SomeType 没有该方法,则代码将导致编译时错误

动态

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

SomeType x = new SomeType();
dynamic obj = x;
obj.DoSomething();
  • obj 在编译和运行时的类型为 dynamic
  • 如果 SomeType 有一个方法 DoSomething((,这段代码将起作用
  • 如果 SomeType 没有该方法,代码将编译,但在运行时引发异常
  • 请注意,如果使用不当,动态很容易导致异常:

    public void f(dynamic x)
    { 
        x.DoSomething();
    }
    

如果 x 的类型没有 DoSomething 方法,这将引发异常,但仍然可以调用它并将任何对象作为参数传递而不会发生编译时错误,从而导致仅在运行时显示的错误,并且可能仅在特定情况下显示 - 潜在的错误。因此,如果您在类的任何类型的公共接口中使用 dynamic,则应始终在运行时使用反射手动进行类型检查,仔细处理异常,或者首先不这样做。

注意:当然,被引用的对象永远不会改变其类型。虽然 obj 可能是对象,但它所指的 x 仍然是 SomeType

不同之处在于,如果您使用 object 并尝试访问对象的某个成员,这将是一个编译时错误(因为对象没有此成员(。为了工作,您需要知道类型是什么并强制转换它。

使用动态,您可以访问任何成员 - 没有编译时错误。如果成员在运行时不存在,则为运行时错误。例如,如果您知道您的异构对象都具有相同的成员,这就是要走的路。

但是,如果是这种情况,还有另一个更明确的解决方案:您可以使用此成员定义一个接口,然后使所有异构对象实现它,并且可以List<IYourInterface>列表。

请记住,由于动态类型分辨率的原因,动态的性能可能会稍差一些。