class MyBase<T>
where T : MyBase<T>.BaseContext, new()
{
public class BaseContext
{
public int State { get; set; }
}
public void DoSomething()
{
var context = new T { State = 123456 };
Prepare( context );
// Essential stuff goes here
Complete( context );
}
protected virtual void Prepare( T context )
{ }
protected virtual void Complete( T context )
{ }
}
class MyDerivedOne : MyBase<MyDerivedOne.ExtendedContext>
{
public class ExtendedContext : BaseContext
{
public string AdditionalInfo { get; set; }
}
protected override void Prepare( ExtendedContext context )
{
context.AdditionalInfo = "Hello!";
}
}
class MyDerivedTwo : MyBase< ?? BaseContext ?? >
{
}
В первом производном классе расширенный контекст нужен, и все хорошо.
Во втором случае хватает того, что есть в BaseContext, MyDerivedTwo.ExtendedContext не нужен.
Как при этом объявить MyDerivedTwo не добавляя пустой класс MyDerivedTwo.ExtendedContext?
До абсурда что угодно можно довести.
В моем случае вложенные типы не имеют своих типов-параметров, так что не понятно причем здесь пример из того поста.
Ну и альтернатива какая? Засорять область видимости десятком классов (этих контекстов много), не имеющих никакого смысла за пределами MyBase?
Здравствуйте, SergASh, Вы писали:
SAS>В моем случае вложенные типы не имеют своих типов-параметров, так что не понятно причем здесь пример из того поста.
Ну у вас примерно та же проблема, разве что степень абсурда поменьше. Компилятор вам должен прямым текстом про циклическую зависимость написать, емнип.
SAS>Ну и альтернатива какая? Засорять область видимости десятком классов (этих контекстов много), не имеющих никакого смысла за пределами MyBase?
Выбирайте
1. Не использовать генерики. В каждом классе —
new MyContext Context => (MyContext)ContextOrigin.
Это единственная схема, которая работает с цепочкой наследников от BaseContext. Все прочие требуют нетривиальных приседаний.
2. Пересмотреть подход к проектированию. Если не считать очень специфичных случаев, необходимость указывать тип наследника в базовом классе означает, что наследование используется не по назначению. У вас что-то типа миксинов, так и используйте стандартное комбо: объявить internal генерик-интерфейс с нужными методами (prepare-complete), явно реализовать этот интерфейс в нужном классе/структуре и вызывать логику (DoSomething) через extension-метод к интерфейсу.
3. Вытащить контекст в базовый класс, от него унаследовать MyBase<T>.
4. Не страдать фигнёй и спрятать Context в отдельный namespace.
Здравствуйте, SergASh, Вы писали:
SAS>Привет всем!
А разве так не работает:
class MyBase
{
public class BaseContext
{
public int State { get; set; }
}
}
class MyBase<T>: MyBase
where T : MyBase.BaseContext, new()
{
public void DoSomething()
{
var context = new T { State = 123456 };
Prepare( context );
// Essential stuff goes here
Complete( context );
}
protected virtual void Prepare( T context )
{ }
protected virtual void Complete( T context )
{ }
}
class MyDerivedOne : MyBase<MyDerivedOne.ExtendedContext>
{
public class ExtendedContext : BaseContext
{
public string AdditionalInfo { get; set; }
}
protected override void Prepare( ExtendedContext context )
{
context.AdditionalInfo = "Hello!";
}
}
class MyDerivedTwo : MyBase<MyBase.BaseContext>
{
}
?
Наследование введено на случай если вы вдруг захотите сделать класс BaseContext protected.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.