【问题标题】:How to make the inherited class's constructor run after a class's constructor如何使继承类的构造函数在类的构造函数之后运行
【发布时间】:2019-08-11 19:48:13
【问题描述】:

我有 2 个课程:BaseMain。当我运行Main 的构造函数时,我想在Base 的构造函数运行之前运行一些代码BEFORE。在java 中,这很容易,您编写一些代码然后使用super。有没有办法使用C# 获得相同的结果?

class Base
{
    public string myString = "Hi";
    public Base(string str)
    {
        myString += str;
    }
}

class Main : Base
{
    public Main() : base(" world")
    {
        base.myString = "Hello";
    }
}

class Program
{
    Console.WriteLine(new Main().myString);
}

我希望输出是Hello world,但它只是Hello,因为Main 的构造函数在Base 的构造函数之后运行。

【问题讨论】:

  • 不,无法更改顺序,您需要另一种解决方案。但是,我从来没有需要过这样的东西,真的很奇特。
  • 在这种情况下组合可能会更好
  • 不,没有简单的方法。使用工厂模式构造,然后在实例上调用“PostInitialize”或类似方法,然后从工厂方法返回它。

标签: c# inheritance constructor


【解决方案1】:

没有简单的方法。 解决方案:声明protected abstract void PreInitialization() 方法并在Base 的ctor 中尽快调用它。然后在Main 级别提供适当的实现。那是最接近您的要求的。

其他(更好的)解决方案:提供独立的Factory<T> where T : BaseT Make(Action preInitializationAction);。然后您决定何时创建对象以及何时调用提供的回调;但是,访问T 的字段可能仍然存在问题:您的回调将无法访问它们,因为该对象尚不存在。

附:好像你设计错了什么。这样的解决方案非常不可靠,并且不遵循已知的良好做法。

【讨论】:

  • 在构造函数中调用虚方法是一个问题的根源,因为它会在定义该方法的类型的构造函数执行之前,在该方法的最重写版本中执行。 推荐。
  • 我同意。这就是为什么我附加了“P.S.”底部的部分。
【解决方案2】:

我认为这是不可能的,如果是这样,您将无法对基类做任何事情,因为在构造函数运行之前它仍然为 null。您可以做的是将构造函数留空并创建一个 void 'Construct' 并从 Main 构造函数的末尾调用它,这样您就可以更改 myString 因为对象存在。

【讨论】:

    【解决方案3】:

    没有任何合法的方法可以通过任何语言结构来实现这一点。想到的是在基类中使用构造函数来模拟这种行为,该构造函数将首先接受一些要执行的操作:

    class Base
    {
        public string myString = "Hi";
        public Base(string str)
        {
            myString += str;
        }
    
        public Base(string str, Action<Base> runFirts)
        {
            runFirts?.Invoke(this);
            myString += str;
        }
    }
    
    class Main : Base
    {
        public Main() : base(
            "world",
            instance => { instance.myString = "Hello"; })
        {
        }
    }
    

    无论如何也不知道为什么需要它,但请注意,这样的实现要求可能表明设计存在问题。

    【讨论】:

      【解决方案4】:

      免责声明

      这只是一个演示。不要在生产中使用它。

      你对 c# 提出了一些不自然的要求。我喜欢它。准备观看一些魔术。 但让我们从修补初始代码开始。首先,让我们将 Main 类名称更改为 Derived。 众所周知,字段内联初始化是一种语法糖。编译器对所有构造函数进行初始化。我们也会。让我们添加空合并。起初它不会影响结果,但接下来将是一个很好的帮助。 我们还应该将我们的类放入一个单独的类库(例如 ClassLibrary)中。 所以我们有:

      namespace ClassLibrary
      {
          public class Base
          {
              public string myString;
      
              public Base(string str)
              {
                  myString = myString ?? "Hi";
                  myString += str;
              }
          }
      
          public class Derived : Base
          {
              public Derived() : base(" world")
              {
                  base.myString = "Hello";
              }
          }
      }
      

      然后编译库,创建控制台应用程序并通过添加引用添加对已编译 dll 的引用... -> 浏览 -> 浏览... 添加类似你的代码:

      using System;
      using ClassLibrary;
      
      namespace ConsoleApp
      {
          class Program
          {
              static void Main(string[] args)
              {
                  Console.WriteLine(new Derived().myString);
              }
          }
      }
      

      输出只是 Hello 和你一样。

      现在用 ildasm 打开 dll 并将其转储(文件 -> 转储,检查转储 IL 代码)到 ClassLibrary.il 并退出 ildasm。使用任何文本编辑器打开 ClassLibrary.il 并找到 Derived 类构造函数。它以.class public auto ansi beforefieldinit ClassLibrary.Derived 开头并包含:

      IL_0000:  ldarg.0
      IL_0001:  ldstr      " world"
      IL_0006:  call       instance void ClassLibrary.Base::.ctor(string)
      IL_000b:  nop
      IL_000c:  nop
      IL_000d:  ldarg.0
      IL_000e:  ldstr      "Hello"
      IL_0013:  stfld      string ClassLibrary.Base::myString
      IL_0018:  ret
      

      将其更改为(在 IL 中手动在任何地方调用基类构造函数):

      IL_0000:  ldarg.0
      IL_0001:  ldstr      "Hello"
      IL_0006:  stfld      string ClassLibrary.Base::myString
      IL_000b:  nop
      IL_000c:  nop
      IL_000d:  ldarg.0
      IL_000e:  ldstr      " world"
      IL_0013:  call       instance void ClassLibrary.Base::.ctor(string)
      IL_0018:  ret
      

      然后保存 ClassLibrary.il 并从命令提示符运行:

      "%ILASM_LOCATION%\ilasm.exe" "%ClassLibrary.il_LOCATION%\ClassLibrary.il" /dll /output:"%ClassLibrary.dll_LOCATION%\ClassLibrary.dll"
      

      再次运行控制台应用程序并查看: Hello world

      使用 ILSpy 派生类外观打开:

      public class Derived : Base
      {
          public Derived()
          {
              myString = "Hello";
              base..ctor(" world");
          }
      }
      

      但放到 ClassLibrary 项目中:

      错误 CS1001 标识符应为...

      错误 CS7036 没有给出与 'Base.Base(string)' 的所需形参 'str' 相对应的参数...

      错误 CS0117 'Base' 不包含 '' 的定义...

      【讨论】:

        【解决方案5】:

        免责声明

        这只是一个演示。不要在生产中使用它。

        出于技术原因,稍微修改Base

        class Base
        {
            public string myString;
        
            protected Base(object obj) { }
        
            public Base(string str)
            {
                Console.WriteLine("\tTracing: ctor 'Base(string)' called");
                myString = myString ?? "Hi";
                myString += str;
            }
        }
        

        修改Main

        class Main : Base
        {
            private Action<string> ctor;
        
            private Action<string> @base =>
                ctor ?? (ctor = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), this as Base, ".ctor"));
        
            public Main() : base(new object())
            {
                Console.WriteLine("\tTracing: ctor 'Main()' called");
                myString = "Hello";
                @base(" world");
            }
        }
        

        现在测试一下。

        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine(new Base(" from base").myString);
                Console.WriteLine(new Main().myString);
            }
        }
        

        给予:

                Tracing: ctor 'Base(string)' called
        Hi from base
                Tracing: ctor 'Main()' called
                Tracing: ctor 'Base(string)' called
        Hello world
        

        【讨论】:

          猜你喜欢
          • 2018-03-31
          • 1970-01-01
          • 2012-09-03
          • 1970-01-01
          • 2022-07-25
          • 2021-08-13
          • 2019-12-14
          • 2011-05-20
          相关资源
          最近更新 更多