Маленький вопрос про дженерики
От: SergASh  
Дата: 14.05.16 11:49
Оценка:
Привет всем!

Имеется вот что
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?

Спасибо.
Re: Маленький вопрос про дженерики
От: Sinix  
Дата: 14.05.16 12:00
Оценка:
Здравствуйте, SergASh, Вы писали:

SAS>Привет всем!


Вот не надо заводить nested types в генериках.

Пример в самом конце вот этого
Автор: Sinix
Дата: 03.04.15
поста наглядно показывает, почему.
Re[2]: Маленький вопрос про дженерики
От: SergASh  
Дата: 14.05.16 12:34
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Вот не надо заводить nested types в генериках.


S>Пример в самом конце вот этого
Автор: Sinix
Дата: 03.04.15
поста наглядно показывает, почему.


До абсурда что угодно можно довести.
В моем случае вложенные типы не имеют своих типов-параметров, так что не понятно причем здесь пример из того поста.
Ну и альтернатива какая? Засорять область видимости десятком классов (этих контекстов много), не имеющих никакого смысла за пределами MyBase?
Re[3]: Маленький вопрос про дженерики
От: Sinix  
Дата: 14.05.16 13:42
Оценка: 4 (1)
Здравствуйте, SergASh, Вы писали:

SAS>В моем случае вложенные типы не имеют своих типов-параметров, так что не понятно причем здесь пример из того поста.

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


SAS>Ну и альтернатива какая? Засорять область видимости десятком классов (этих контекстов много), не имеющих никакого смысла за пределами MyBase?

Выбирайте

1. Не использовать генерики. В каждом классе —
new MyContext Context => (MyContext)ContextOrigin.


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

2. Пересмотреть подход к проектированию. Если не считать очень специфичных случаев, необходимость указывать тип наследника в базовом классе означает, что наследование используется не по назначению. У вас что-то типа миксинов, так и используйте стандартное комбо: объявить internal генерик-интерфейс с нужными методами (prepare-complete), явно реализовать этот интерфейс в нужном классе/структуре и вызывать логику (DoSomething) через extension-метод к интерфейсу.

3. Вытащить контекст в базовый класс, от него унаследовать MyBase<T>.

4. Не страдать фигнёй и спрятать Context в отдельный namespace.

Ещё пара способов была вроде, вспомню — допишу.
Re: Маленький вопрос про дженерики
От: hardcase Пират http://nemerle.org
Дата: 15.05.16 16:39
Оценка:
Здравствуйте, SergASh, Вы писали:

SAS>Привет всем!


Вложенные типы не нужны. Ограничение new() — вредно.


interface IContext
{
  int State { get; set; }
}

public class DefaultContext : IContext;
{
  public int State { get; set; }
}

class MyBase<T>
  where T : IContext
{
  protected abstract T CreateContext(int state);

  public void DoSomething()
  {
    var context = CreateContext(123456);
    Prepare( context );
    // Essential stuff goes here
    Complete( context );
  }

  protected virtual void Prepare( T context )
  { }

  protected virtual void Complete( T context )
  { }
}

class ExtendedContext : DefaultContext
{
  public string AdditionalInfo { get; set; }
}

class MyDerivedOne : MyBase<ExtendedContext>
{
  protected override ExtendedContext CreateContext(int state)
  {
    return new ExtendedContext { State = state; }
  }

  protected override void Prepare( ExtendedContext context )
  { 
    context.AdditionalInfo = "Hello!";
  }
}

class MyDerivedTwo : MyBase<DefaultContext>
{
  protected override DefaultContext CreateContext(int state)
  {
    return new DefaultContext { State = state; }
  }
}
/* иЗвиНите зА неРовнЫй поЧерК */
Re: Маленький вопрос про дженерики
От: Sinclair Россия https://github.com/evilguest/
Дата: 16.05.16 08:29
Оценка:
Здравствуйте, 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.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Маленький вопрос про дженерики
От: Sharov Россия  
Дата: 16.05.16 10:34
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>3. Вытащить контекст в базовый класс, от него унаследовать MyBase<T>.


Лично я пошел бы по этому пути. Собственно как и комментаторы выше.
Кодом людям нужно помогать!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.