为啥在尝试调用采用动态参数的基本构造函数/方法时会出现此编译错误?

Posted

技术标签:

【中文标题】为啥在尝试调用采用动态参数的基本构造函数/方法时会出现此编译错误?【英文标题】:Why do I get this compile error trying to call a base constructor/method that takes a dynamic argument?为什么在尝试调用采用动态参数的基本构造函数/方法时会出现此编译错误? 【发布时间】:2011-12-27 07:00:11 【问题描述】:

在重构一些代码时,我遇到了这个奇怪的编译错误:

构造函数调用需要动态调度,但不能因为它是构造函数初始化程序的一部分。考虑强制转换动态参数。

在尝试调用采用动态参数的基本方法/构造函数时似乎会发生这种情况。例如:

class ClassA

    public ClassA(dynamic test)
    
        Console.WriteLine("ClassA");
    


class ClassB : ClassA

    public ClassB(dynamic test)
        : base(test)
    
        Console.WriteLine("ClassB");
    

如果我将参数转换为object,它会起作用,如下所示:

public ClassB(dynamic test)
    : base((object)test)

所以,我有点困惑。为什么我必须加入这个讨厌的演员表 - 为什么编译器无法理解我的意思?

【问题讨论】:

【参考方案1】:

必须在编译时确定构造函数链 - 编译器必须选择一个重载,以便它可以创建有效的 IL。虽然通常重载决议(例如对于方法调用)可以推迟到执行时间,但这不适用于链式构造函数调用。

编辑:在“普通”C# 代码中(基本上在 C# 4 之前),all 重载决议是在编译时执行的。但是,当成员调用涉及动态值时,将在执行时解决。例如考虑一下:

using System;

class Program

    static void Foo(int x)
    
        Console.WriteLine("int!");
    

    static void Foo(string x)
    
        Console.WriteLine("string!");
    

    static void Main(string[] args)  
    
        dynamic d = 10;
        Foo(d);
    

编译器在这里不会直接调用Foo - 它不能,因为在调用Foo(d) 时它不知道它会解析到哪个重载。取而代之的是,它会发出执行某种“及时”迷你编译的代码,以在执行时使用 d 的值的 实际 类型来解决重载问题。

现在这不适用于构造函数链接,因为有效的 IL 必须包含对特定基类构造函数的调用。 (不知道动态版本是不是连IL都不能表达,还是可以,但结果是无法验证的。)

您可能会争辩说,C# 编译器应该能够判断出实际上只有一个可见的构造函数可以被调用,并且该构造函数将始终可用。 . 但是一旦你开始走这条路,你最终会得到一种语言,非常很难指定。 C# 设计者通常会采用更简单的规则,但有时这些规则并不像您希望的那样强大。

【讨论】:

太好了,感谢您提供的信息!我忘记了动态重载解决方案是在运行时(尽管在同一个项目中利用了这一点!)。基本 method 调用也会发生此错误,因此这似乎与基本相关而不是与构造函数相关。关于为什么这也不能在运行时完成的任何想法? @DannyTuppeny:有趣的是,基方法调用也会发生。这可能是因为它们被非虚拟调用。我怀疑出于封装原因,以非虚拟方式调用虚拟方法可能能够在正在进行非虚拟方法调用的子类实例的 IL 内完成。由于 DLR 想要从任意代码非虚拟地调用该方法,这将是一个问题。 我也可以理解为什么你不想让构造函数链接 within 一个类(即this(...))是动态的 - 你无法验证它'永远完成。基本上,当您考虑静态类型语言时,有一堆规则听起来完全合理,但在动态上下文中就没有意义了:)

以上是关于为啥在尝试调用采用动态参数的基本构造函数/方法时会出现此编译错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥尝试使用显式类型参数调用模板成员函数时会出错?

java中,当实例化子类时会递归调用父类中的构造方法。这个说法对么?为啥

为啥在声明子类的对象时会调用超类的构造函数? (爪哇)

当我们将对象作为参数传递给方法时,为啥会调用复制构造函数?

为啥 GCC 在匹配函数时会查看私有构造函数?

子类为啥要调用父类的构造函数