Класс Base() конструктор и передать это
Я пытаюсь реализовать хорошие шаблоны проектирования для программы, которую я пишу. У меня есть структура классов, как это.
abstract class SomeBase
{
public SomeObject obj { get; protected set; }
protected SomeBase(SomeObject x)
{
obj = x;
}
//Other methods and fields...
}
public class SomeDerived : SomeBase
{
public SomeDerived() : base(new SomeObject(this))
{
}
}
Теперь, я уверен, вы сейчас не можете передать это в конструктор, потому что объект не был инициализирован. Но я действительно надеялся, что был обходной путь. Для меня не рекомендуется разрешать SomeDerived() обрабатывать настройки поля базовых классов. Я хотел бы передать этот новый объект по цепочке.
Есть идеи?
2 ответа
Это невозможно, используйте метод Init после конструктора:
abstract class SomeBase
{
private SomeObject _obj { get; set; }
public SomeObject obj
{
get
{ // check _obj is inited:
if (_obj == null) throw new <exception of your choice> ;
return _obj;
}
}
protected SomeBase()
{
obj = null;
}
protected void Init()
{
obj = x;
}
//Other methods and fields...
}
public class SomeDerived : SomeBase
{
public SomeDerived() : base()
{
Init(new SomeObject(this));
}
}
На самом деле он у вас есть в базовом конструкторе, поэтому передавать его не нужно.
using System;
abstract class SomeBase
{
public SomeObject obj { get; protected set; }
protected SomeBase()
{
// Will be executed as 1st step
Console.WriteLine(1);
// "this" here is SomeDerived object
obj = new SomeObject((SomeDerived)this);
// If you don`t like to depend upon SomeDerived type here,
// you can do the same using the following line:
//obj = (SomeObject)Activator.CreateInstance(typeof(SomeObject), this);
}
}
class SomeObject
{
public SomeObject(SomeDerived obj)
{
if (obj.obj == null)
{
// Will be executed as 2nd step
Console.WriteLine(2);
// You have the reference to SomeDerived here
// But its properties are not yet initialized
// (both SomeDerived and SomeBase constructors are in the progress)
// So you should not access these properties
// in the SomeObject class constructor,
// but you can do it in any method of SomeObject class
// (at the time the method will be called all constructors are finished).
}
}
}
class SomeDerived : SomeBase
{
public SomeDerived()
{
// Will be executed as 3rd step
Console.WriteLine(3);
if (this.obj != null)
{
// You have the reference to SomeObject here,
// which itself already got an access to SomeDerived reference
}
}
}
class MainClass
{
public static void Main (string[] args)
{
var instance = new SomeDerived();
Console.WriteLine (instance.obj); // Outputs SomeObject
}
}
https://repl.it/@Konard/LinenZealousBlockchain
Другим решением будет использование лямбда-выражения, поэтому класс SomeDerived будет иметь полный контроль над тем, как ссылка на него будет передаваться в SomeObject.
using System;
abstract class SomeBase
{
public SomeObject obj { get; protected set; }
protected SomeBase(Func<SomeBase, SomeObject> someObjectInitilizer)
{
// Will be executed as 1st step
Console.WriteLine(1);
// "this" here is SomeDerived object
obj = someObjectInitilizer(this);
}
}
class SomeObject
{
public SomeObject(SomeDerived obj)
{
if (obj.obj == null)
{
// Will be executed as 2nd step
Console.WriteLine(2);
// You have the reference to SomeDerived here
// But its properties are not yet initialized
// (both SomeDerived and SomeBase constructors are in the progress)
// So you should not access these properties
// in the SomeObject class constructor,
// but you can do it in any method of SomeObject class
// (at the time the method will be called all constructors are finished).
}
}
}
class SomeDerived : SomeBase
{
public SomeDerived() : base((@this) => new SomeObject((SomeDerived)@this))
{
// Will be executed as 3rd step
Console.WriteLine(3);
if (this.obj != null)
{
// You have the reference to SomeObject here,
// which itself already got an access to SomeDerived reference
}
}
}
class MainClass
{
public static void Main (string[] args)
{
var instance = new SomeDerived();
Console.WriteLine (instance.obj); // Outputs SomeObject
}
}
1) Конструкторы вообще НЕПРАВИЛЬНЫ по своему замыслу - это похоже на метод экземпляра, но на самом деле это полу-метод половины экземпляра.
2) "Хорошие программы проектирования с шаблонами" не вызывают немедленной циклической зависимости между классами в агрегации, как мы видим здесь - оба класса должны знать и использовать друг друга при создании (!!!), кто знает, что SomeObject делает с "this" в его конструктор???
Таким образом, в "шаблонах", где есть 2 проблемы - высокая зависимость между классами и неиспользуемая инкапсуляция логики инициализации. Таким образом, мы должны найти "шаблон" способ решить его... хм.. что делать...
В коде, который вы предоставляете, я вижу, что производный класс просто предоставляет свою собственную логику для свойства, поэтому вы можете переписать его для автоматической инициализации.
public abstract class MyClass{
private SomeObject _obj ;
public SomeObject Obj {get { return _obj ?? (_obj = InitializeObj() );}} //no setter needed
protected abstract SomeObject InitializeObj();
}
public class MyRealClass:MyClass {
protected override SomeObject InitializeObj(){
return new VerySpecialSomeObject(this, other, another, 1, 2 , false, option: new Options());
}
}
Для вашего примера такое решение предусматривает один "шаблон", который выигрывает - "полиморфизм")) и получает дополнительный бонус - если "Obj" не будет полезен - он никогда не будет создан))))