如何使继承的类的构造函数在类的构造函数之后运行

时间:2019-04-13 19:55:41

标签: c# inheritance constructor

我有2个班级:import sys print('are you looking to study digital media in australia?') answer = str(sys.stdin.read()) if answer == 'yes': print('Deakin University has one of the best Digital Science Program!') Base。当我运行Main的构造函数时,我想在Main的构造函数运行之前运行一些代码。在Java中,很容易,您编写一些代码然后使用Base。有没有办法使用C#获得相同的结果?

super

我希望输出为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,因为Main的构造函数在Hello的构造函数之后运行。

5 个答案:

答案 0 :(得分:4)

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

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

P.S。似乎您设计错误。这样的解决方案非常不可靠,并且没有遵循已知的良好做法。

答案 1 :(得分:1)

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

答案 2 :(得分:1)

任何语言构造都没有合法的方法来实现这一目标。想到的是通过在基类中使用构造器来模拟这种行为,该构造器将接受一些要首先执行的动作:

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"; })
    {
    }
}

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

答案 3 :(得分:1)

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

您为C#请求不自然的东西。我喜欢它。准备看一些魔术。 但是,让我们从修补初始代码开始。首先,让我们将Main class名称更改为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“基本”不包含“ ...”的定义。

更新

反射魔术:

using System;
using System.Reflection;

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

    public static class TraceHelper
    {
        public static void Trace(string message)
        {
            var color = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine($"Trace: {message}.");
            Console.ForegroundColor = color;
        }
    }

    public class Base
    {
        public string myString;

        public Base(string str)
        {
            TraceHelper.Trace("Base constructor called");

            myString = myString ?? "Hi";
            myString += str;
        }
    }

    public class Derived : Base
    {
        private static readonly ConstructorInfo baseCtor = typeof(Base).GetConstructor(new[] { typeof(string) });

        public Derived() : base(null)
        {
            TraceHelper.Trace("Derived constructor called");

            base.myString = "Hello";
            @base(" world");
        }

        private void @base(string str)
        {
            baseCtor.Invoke(this, new[] { str });
        }
    }
}

输出:

Trace: Base constructor called.
Trace: Derived constructor called.
Trace: Base constructor called.
Hello world

结果为“ Hello world” ,但基本构造函数称为两次

答案 4 :(得分:0)

免责声明

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

出于技术原因对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