【问题标题】:Are virtual members called via reflection (in normal circumstances)?虚拟成员是否通过反射调用(在正常情况下)?
【发布时间】:2012-07-05 00:43:29
【问题描述】:

我正在测试在构造函数中调用虚拟成员的效果,发现在调用该成员时产生的异常被包装在 TargetInvocationException 中。

根据docs,这是:

通过反射调用的方法抛出的异常

但是我不知道通过反射进行的任何调用。那么这是否意味着总是通过反射调用虚拟成员?如果不是,为什么在这种情况下会这样?

代码:

class ClassA
    {
        public ClassA()
        {
            SplitTheWords();
        }

        public virtual void SplitTheWords()
        {
            //I've been overidden
        }
    }

class ClassB : ClassA
    {
        private readonly String _output;

        public ClassB()
        {
            _output = "Constructor has occured";
        }

        public override void SplitTheWords()
        {
            String[] something = _output.Split(new[]{' '}); //TargetInvocationException!
        }
    }

【问题讨论】:

    标签: c# .net exception virtual access-modifiers


    【解决方案1】:

    不,虚拟方法是通过virtual dispatch 调用的。

    这里没有使用反射。也不是任何虚拟方法调用。我认为该异常的文档有点误导,因为这种类型的异常是由通过反射调用的方法抛出的,但并非完全如此。

    如果有人好奇为什么问题中的代码会给出异常,那是因为构造函数的执行顺序。 ClassB 构造函数与:

    public ClassB() : base()
    {
        _output = "Constructor has occured";
    }
    

    注意对base() 的调用,这会在ClassB 构造函数运行之前调用基本构造函数,因此在分配_output 之前。在基础构造函数中调用SplitTheWords 虚方法,解析为ClassB.SplitTheWords。此方法尝试使用_output,因此出现错误。

    要更详细地了解为什么不应从构造函数调用虚方法this SO question 有一些有用的信息。 Eric Lippert 也有一篇非常好的博文,说明为什么会这样here

    【讨论】:

    • +1 表示“有点误导”,这对于文档来说是一件非常令人困惑的事情。可以编写一个除了throw new TargetInvocationException(); 之外什么都不做的控制台应用程序——那么反射会在哪里?
    • 我明白为什么虚拟成员不应该从构造函数中调用,这就是促使我尝试这个的原因
    • @m.edmondson 抱歉,我从您关于“我正在测试...的效果”的行中理解这一点,我并不打算让我的回答居高临下(但我认为它现在可能是),我只包括最后几段以供其他看到此答案的人完整。我会尝试改写。
    • 没问题,最好提供完整答案
    【解决方案2】:

    是否通过反射调用虚拟成员(在正常情况下)?

    没有。

    也不是来自构造函数,所以发生了其他事情。查看调用您显示的代码的代码以及异常的堆栈跟踪会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-31
      • 1970-01-01
      • 2019-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多