2012-06-20 3 views
0

У меня есть два класса, один из которых наследуется от другого. Базовый класс - MustInherit/abstract и определяет свойство MustOverride/abstract.Инициализация начальной загрузки

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

В принципе, мне нужно инициализировать часть базового класса, а затем разрешить наследовательному классу инициализировать некоторые его свойства, а затем вернуться к базовому классу, чтобы завершить инициализацию дополнительных свойств.

Я бы сделал свойство частью базового класса, но наследующие классы используют сильную типизацию, тогда как базовому классу нужен только интерфейс.

Пример кода:

MustInherit Class A 
    MustOverride Property X As IExample 

    Sub New() 
    ' Do some stuff 
    _privateY = X.Foo() ' NullReferenceException 
    End Sub 
End Class 

Class B 
    Inherits A 

    Override Property X As IExample ' returns StrongX 
    Property StrongX As ConcreteExample ' ConcreteExample implements IExample 

    Sub New(x As ConcreteExample) 
    MyBase.New(x) 
    StrongX = x 
    End Sub 
End Class 
+0

(с тегами как на C#, так и на VB.NET, поскольку это относится к обоим языкам, хотя я заинтересован в более релевантных тегах, если есть предложения.) – JDB

+0

Просто потому, что C# и VB.NET оба OO не означает что этот вопрос квалифицируется как C# один. –

+0

Этот вопрос не имеет ничего общего с C# !!! –

ответ

3

Именно по этой причине, что абстрактные члены не должны называться в конструкторе. http://msdn.microsoft.com/en-us/library/ms182331(v=vs.100).aspx

Если у вас есть контроль над абстрактным классом, я бы предложил передать значение через конструктор базового класса.

public abstract class A 
{ 
    public A(IExample x) 
    { 
     // Do Stuff 
     var _privateY = x.Foo(); 
    } 
} 

public class B : A 
{ 
    public B(IExample x):base(x) {} 
} 
+0

Может быть, есть 'public B(): base (new ConcreteExample())' –

+0

Как говорит Эрик Липперт, «его плохая практика программирования для вызова виртуальных методов из конструкторов. Причина - это плохая идея вызывать виртуальные методы от конструкторов потому что метод на производном классе может работать до выполнения конструктора производного класса ». Решение Kevin элегантно и имеет правильную идею. –

+0

Спасибо за документацию.Мне понравился подход с параметрами конструктора, но я не большой поклонник, так как он вызывает чрезмерное количество параметров и может дать неправильное впечатление, что базовый класс каким-то образом устанавливает свойство (или должен быть). Однако это похоже на единственный вариант. – JDB

0

Вы можете использовать Lazy<T>:

public abstract class A 
{ 
    private Lazy<IExample> _privateY = new Lazy(() => this.X.Foo()); 
} 
+0

Класс A не реализует IExample, это свойство. Я на самом деле не рассматривал ленивую загрузку, хотя это и делает некоторые очень неприятные сценарии отладки. Это одна из моих самых больших проблем с LINQ. – JDB

1

EDIT: К сожалению, мой ответ в C# :(

Один из вариантов будут иметь виртуальную (или абстрактную) методу инициализации базового вызова абстрактного класса, подкласс может осуществлять:

public abstract class A 
{ 
    public abstract IExample X { get; set; } 

    private object _privateY; 

    protected A() 
    { 
     PreInit(); 

     PostInit(); 
    } 

    protected abstract void PreInit(); 

    protected virtual void PostInit() 
    { 
     if (X == null) 
      throw new InvalidOperationException("Must assign a value to X."); 

     _privateY = X.Foo(); 
    } 
} 

Тогда в B, переопределить PreInit и присвоить данные:

public class B : A 
{ 
    public override IExample X { get; set; } 

    public B() 
    { 

    } 

    protected override void PreInit() 
    { 
     X = new ConcreteExample(); 
    } 
} 
+0

Мне нравится идея здесь, но я не инициализирую ConcreteExample в B ... она передается как параметр конструктора. Кроме того, не было бы никакого способа обеспечить, чтобы какой-то будущий C корректно обрабатывал PreInit (что, скорее всего, так как это не очень интуитивно понятно, что принадлежит где). Спасибо хоть. – JDB

+0

Да, я в основном согласен, что это предпочтительный случай. Я просто бросил это здесь как возможность рассмотреть (поскольку мы не знаем, что именно представляет собой ваш дизайн приложения), и так как Кевен избил меня. –

Смежные вопросы