在C#中的基础构造函数之前执行派生的构造函数

时间:2009-04-09 17:22:58

标签: c# constructor abstract-class derived-class

我的问题是我想将一个对象传递给派生类,但它必须在基类构造函数之前完成,因为基类会立即调用派生类的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代码块。我想看一个修改基类的替代方法。

4 个答案:

答案 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;
    }
}