我的问题是我想将一个对象传递给派生类,但它必须在基类构造函数之前完成,因为基类会立即调用派生类的Start()
方法,该方法使用对象
以下是基类的摘录(为方便起见,从BarcodeScanner重命名)。
public abstract class MyBase
{
public MyBase()
{
if (Initialize())
this.Start();
}
public abstract bool Initialize();
public abstract void Start();
}
这是我正在创建的派生类。
class MyDerived : MyBase
{
private string sampleObject;
public MyDerived (string initObject)
{
sampleObject = initObject;
}
public override bool Initialize()
{
return GetDevice();
}
public override void Start()
{
Console.WriteLine("Processing " + sampleObject.ToString());
}
}
我怀疑你可以让C#在基础构造函数之前执行派生构造函数;所以我真的只是在寻找一个解决方案,在使用对象之前将对象传递给派生类。
我通过在MyDerived
构造函数中放置Initialize / Start if块来解决这个问题。但是,还有其他类派生自基类;所以我最终不得不在每个派生类中重复这个Initialize / Start代码块。我想看一个修改基类的替代方法。
答案 0 :(得分:17)
在C#中你不可能做的事情是不可能的。必须在任何派生类的构造函数之前运行基类中的构造函数,否则可能存在损坏的对象状态。子对象必须能够假设其基础是完全构造和可用的。
答案 1 :(得分:17)
恕我直言,你的设计是错误的。您不应该从构造函数中启动该过程。您的消费代码应在需要时显式调用Start()方法。
答案 2 :(得分:1)
我会重新设计你的设计,以便在构造之后调用Initialize(以及可能的Start() - 虽然我通常将这是一个由用户调用的公共方法)。
如果您正在制作BarcodeScanner,则可以在首次扫描时执行此操作。只是使用派生类中的数据对您的成员进行延迟初始化。
这可以解决您的问题,而且用户的使用情况没有实际变化。
答案 3 :(得分:0)
很抱歉添加到旧线程,但也许有人对另一个答案感兴趣。我发现了一种(IMO)整齐的方法来处理逻辑,而不是(和之后)仅在涉及继承的类构造函数中分配字段here。 如果您只想拥有特定的层次结构,而不使用带有接口和扩展方法的通用解决方案,则可以在一个类树中使用相同的概念,如下所示:
public abstract class MyBase
{
protected MyBase()
{
Initialize(this) // just to illustrate this will never do anything as MyBase can never be the run time type.
}
protected bool IsInitialized { get; private set; } = false;
protected static bool Initialize<T>(T instance) where T: MyBase
{
if (instance?.GetType() == typeof(T)) // check if this is called from the constructor of instance run time type
return instance.IsInitialized || ( instance.IsInitialized = instance.Initialize() );
return false;
}
protected abstract bool Initialize();
public abstract void Start();
}
并导出:
class MyDerived : MyBase
{
private string sampleObject;
protected bool started = false;
public MyDerived (string initObject)
{
sampleObject = initObject;
if (Initialize(this)) // if this is the most derived constructor, this will run Initialize() and return whether it was successful
this.Start();// EDIT: Just to illustrate. Normally constructors should only initialize an instance and not perform operations on it (as mentioned in other answers).
}
protected override bool Initialize()
{
return GetDevice();
}
public override void Start()
{
// if Start() would be protected, we don't need the IsInitialized property and we can move this check to the constructor on the returned value of the Initialize<T>() call.
if (!IsInitialized) throw new InvalidOperationException("Initialization failed.");
// if you want to have this method exposed public, we need to check if this instance is successfully initialized from the constructor and not in started state already.
if (started) return;
Console.WriteLine("Processing " + sampleObject.ToString());
started = true;
if (!Run(sampleObject)) started = false;
}
}