18

我的问题是我想将一个对象传递给派生类,但必须在基类构造函数之前完成,因为基类会立即调用派生类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# 在基本构造函数之前执行派生构造函数;所以我真的只是在寻找一种解决方案,在使用对象之前将对象传递给派生类。

我通过将 Initialize/Start if 块放在MyDerived构造函数中来解决这个问题。但是,还有其他类派生自基类;所以我最终不得不在每个派生类中重复这个初始化/启动代码块。我希望看到修改基类的替代方法。

4

4 回答 4

22

您尝试做的事情在 C# 中是不可能的。基类中的构造函数必须在任何派生类的构造函数之前运行,否则可能会损坏对象状态。子对象必须能够假定其基础已完全构建且可用。

于 2009-04-09T17:24:16.167 回答
21

恕我直言,您的设计是错误的。您不应该从构造函数中启动该过程。您的消费代码应在需要时显式调用 Start() 方法。

于 2009-04-09T17:25:41.047 回答
1

我会重新设计您的设计,以便在构造后调用 Initialize(可能还有 Start() - 尽管我通常将此作为用户调用的公共方法)。

如果您正在制作 BarcodeScanner,您可以在第一次扫描时执行此操作。只需使用派生类中的数据对您的成员进行延迟初始化。

这将解决您的问题,而用户的使用没有真正的变化。

于 2009-04-09T17:29:45.267 回答
0

很抱歉添加到旧线程,但也许有人对另一个答案感兴趣。我发现了一种(IMO)巧妙的方法来处理逻辑,而不仅仅是在此处涉及继承的类构造函数中分配字段(以及之后) 。如果您只想为这个特定的层次结构使用 if 而不是使用带有接口和扩展方法的通用解决方案,您可以在一个类树中使用相同的概念,如下所示:

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;
    }
}
于 2019-08-19T15:10:27.297 回答