我有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
的构造函数之后运行。
答案 0 :(得分:4)
没有简单的方法。
解决方案:声明protected abstract void PreInitialization()
方法并在Base
的ctor中尽快调用它。然后在Main
级别提供适当的实现。那是最接近您的要求。
其他(更好)的解决方案:提供独立的Factory<T> where T : Base
和T 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