Подтвердить что ты не робот

Выполните производный конструктор перед базовым конструктором в С#

Моя проблема заключается в том, что я хотел бы передать объект производному классу, но он должен быть выполнен перед конструктором базового класса, так как базовый класс немедленно вызовет метод производного класса 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()); 
    }
}

Я сомневаюсь, что вы можете заставить С# выполнить производный конструктор перед базовым конструктором; поэтому я действительно ищу решение для передачи объекта производному классу до использования объекта.

Я обошел это, поставив блок Initialize/Start if внутри конструктора MyDerived. Однако существуют другие классы, происходящие из базового класса; поэтому мне пришлось повторить этот блок кода Initialize/Start в каждом производном классе. Я бы хотел увидеть альтернативу модификации базового класса.

4b9b3361

Ответ 1

То, что вы пытаетесь сделать, невозможно в С#. Конструктор в базовом классе должен быть запущен перед конструктором любого производного класса, иначе существовал бы потенциал для состояния поврежденного объекта. Детский объект должен иметь возможность предположить, что его база полностью построена и доступна.

Ответ 2

ИМХО, ваш дизайн неправильный. Вы не должны запускать процесс из конструктора. В случае необходимости ваш код потребления должен явно вызвать метод Start().

Ответ 3

Я бы переработал ваш дизайн, чтобы Инициализировать (и, возможно, Start() - хотя обычно я бы это публичный метод вызывал пользователь) вызывается после построения.

Если вы создаете BarcodeScanner, вы можете сделать это при первой попытке сканирования. Просто лениво инициализируйте своих членов, используя данные из производного класса.

Это будет работать вокруг вашей проблемы без реальных изменений в использовании пользователя.

Ответ 4

Извините за добавление в старую ветку, но, возможно, кто-то заинтересован в другом ответе. Я нашел (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;
    }
}