【问题标题】:Calling Overridden Constructor and Base Constructor in C#在 C# 中调用重写的构造函数和基构造函数
【发布时间】:2010-09-25 00:26:06
【问题描述】:

我有两个类,Foo 和 Bar,它们的构造函数如下:

class Foo
{
    Foo()
    {
      // do some stuff
    }

    Foo(int arg)
    {
      // do some other stuff
    }
}

class Bar : Foo
{
    Bar() : base()
    {
      // some third thing
    }
}

现在我想为 Bar 引入一个采用 int 的构造函数,但我希望 Bar() 中发生的东西能够像 Foo(int) 中的东西一样运行以及。像这样的:

Bar(int arg) : Bar(), base(arg)
{
  // some fourth thing
}

有没有办法在 C# 中做到这一点?到目前为止,我最好的方法是将 Bar() 完成的工作放入一个函数中,该函数也由 Bar(int) 调用,但这很不优雅。

【问题讨论】:

    标签: c# constructor


    【解决方案1】:

    我会重新链接构造函数,所以它们被称为

    Bar() : this(0) 
    Bar(int) : Foo(int) initializes Bar
    Foo(int) initializes Foo
    Foo() : this(0) 
    

    如果无参数构造函数为其他构造函数的 int 参数假设某种默认值,这是合适的。如果构造函数不相关,则您的类型可能有问题,或者我们可能需要更多信息来了解您想要实现的目标。

    【讨论】:

      【解决方案2】:

      不,这是不可能的。如果您使用 Reflector 检查为每个构造函数生成的 IL,您就会明白原因——您最终会为基类调用两个构造函数。理论上,编译器可以构造隐藏的方法来完成你想要的,但实际上并没有比你明确地做同样的事情有任何优势。

      【讨论】:

      • 可能被否决了,因为您没有给出解决问题的方法,即使显然有一个。
      【解决方案3】:

      我建议将您的构造函数链更改为从最不具体到最具体。

      class Foo
      {
          Foo()
          {
            // do some stuff
          }
      
          Foo(int arg): this()
          {
            // do some other stuff
          }
      }
      
      class Bar : Foo
      {
          Bar() : Bar(0)
          {
            // some third thing
          }
      
          Bar(int arg): base(arg)
          {
            // something
          }
      }
      

      任何 Bar 对象的创建现在都将调用所有 4 个构造函数。构造函数链应该为更具体的构造函数提供默认值,而不是相反。你真的应该看看你正在努力完成什么,并确保你所做的事情是有意义的。 Curt 是对的,您不能这样做有技术原因,但也有不应该这样做的合乎逻辑的原因。

      【讨论】:

        【解决方案4】:

        这是我唯一能想到的……

         public class Foo
        {
            public Foo()
            {
            }
            public Foo(int? arg): this()
            {
            }
        
        }
        public class Bar : Foo
        {
            private int x;
            public Bar(): this(new int?()) // edited to fix type ambiguity
            {
                // stuff that only runs for paramerless ctor
            }
            public Bar(int? arg)
                : base(arg)
            {
                if (arg.HasValue)
                {
                    // Do stuff for both parameterless and parameterized ctor
                }
                // Do other stuff for only parameterized ctor
            }
        }
        

        【讨论】:

        • 您对可空的想法有 +1,但它有一些流程 - 如果您添加另一个带有一个参数(类类型,而不是结构)的 ctor,它会失败,就像现在 this(null ) 将不知道该选择哪一个。
        【解决方案5】:

        你不能让 Bar 构造函数接受 int 调用无参数构造函数吗?

        【讨论】:

        • 他还想调用基类的参数化构造函数。
        【解决方案6】:

        你能把来自 Bar() 的东西放在 Bar(int) 中,然后用默认值的 Bar() 调用 Bar(int) 吗?然后 Bar(int) 可以调用基础构造函数。

        class Bar : Foo
        {
            Bar() : this(0)
            {
            }
        
            Bar(int arg) : base(arg)
            {
            }
        }
        

        这并不能完全回答您的问题,但根据您的情况可能是一个可行的解决方案。

        【讨论】:

          【解决方案7】:

          你能把 Bar() 的初始化代码变成一个方法,然后从两个构造函数中调用它,然后让新的构造函数调用 base(arg) 吗?

          【讨论】:

            【解决方案8】:

            您可以使用此代码:

            public Foo
            {
                public Foo()
                {
                    this.InitializeObject();
                }
            
                public Foo(int arg) : this()
                {
                    // do something with Foo's arg
                }
            
                protected virtual void InitializeObject()
                {
                    // initialize object Foo
                }
            }
            
            public Bar : Foo
            {
                public Bar : base() { }
            
                public Bar(int arg) : base(arg)
                {
                   // do something with Bar's arg
                }
            
                protected override void InitializeObject()
                {
                   // initialize object Bar
            
                   base.InitializeObject();
                }
            }
            

            只需像上面的代码一样覆盖InitializeObject() 方法,然后将您想要放入的所有代码放入无参数构造函数中。最后在代码末尾调用base.InitializeObject()

            希望这是有用的。

            【讨论】:

            • 值得注意的是,虽然构造函数可以编写readonly 字段,但InitializeObject 方法不能直接这样做。要在InitializeObject 方法中初始化这些字段,必须让构造函数将它们作为refout 参数传递(我倾向于ref,因为在构造对象时字段值被定义为空白,并且由于我不喜欢虚拟方法上的out 参数,因为其他语言中的覆盖可能会将它们视为ref 参数)。
            • 您可以对只读字段执行此操作:private readonly string _ro1 = "read only 1"; private readonly string _ro2; private readonly int _ro3; public Bar() : base() { } public bar(int arg) : base(arg) { _ro3 = arg; _ro2 = "read only : " + arg; } 基本上,如果只读字段的值由构造函数参数确定,则可以将字段的初始化放在构造函数上。如果没有,您可以像上面的第一个只读字段(_ro1)一样进行初始化。
            • 您的回答是建议将初始化代码从构造函数移到虚拟方法。可以在构造函数或字段初始化程序中初始化只读字段,但也可以使用虚拟方法来完成,if 将相关字段作为refout 参数传递给有问题的方法。顺便说一句,我真正希望在 vb.net 和 C# 中看到的一件事是定义一个字段或伪字段的方法,该字段或伪字段将使用构造函数参数中的值进行初始化,并且可以在以后的字段初始化程序中使用。类似的东西:...
            • ...class Thing {int ArrSize = (int Size); int[] MyData = new int[ArrSize]; Thing(int Size) {};} 其中(int Size) 初始化ArrSize 将表明此类型的任何构造函数都将有一个名为int 的参数称为Size 或链接到一个,并且应该使用传递给该参数的值来初始化ArrSize。在我看来,如果一个字段将被设置为可以基于构造函数参数计算的东西,并且此后不再修改,那么将声明和初始化结合起来比在不同的地方编写 then 会更干净。
            猜你喜欢
            • 1970-01-01
            • 2018-05-25
            • 1970-01-01
            • 1970-01-01
            • 2011-05-23
            • 2018-07-21
            • 2015-08-18
            • 1970-01-01
            • 2013-03-24
            相关资源
            最近更新 更多