Пишу некий фреймворк. Там имеются базовые классы парента BaseParent и чайлдов BaseChild. В паренте я хочу иметь список чайлдов, а в чайлде — ссылку на парента. Пишу через generic'и:
public abstract class BaseParent<TChild>
where TChild : BaseChild<BaseParent<TChild>>
{
protected List<TChild> _childs = new List<TChild>();
}
public abstract class BaseChild<TParent>
where TParent : BaseParent<BaseChild<TParent>>
{
protected TParent _parent;
}
Компилятор ругается:
The type 'GenericsTest.BaseParent<TChild>' cannot be used as type parameter 'TParent' in the generic type or method 'GenericsTest.BaseChild<TParent>'. There is no implicit reference conversion from 'GenericsTest.BaseParent<TChild>' to 'GenericsTest.BaseParent<GenericsTest.BaseChild<GenericsTest.BaseParent<TChild>>>'.
The type 'GenericsTest.BaseChild<TParent>' cannot be used as type parameter 'TChild' in the generic type or method 'GenericsTest.BaseParent<TChild>'. There is no implicit reference conversion from 'GenericsTest.BaseChild<TParent>' to 'GenericsTest.BaseChild<GenericsTest.BaseParent<GenericsTest.BaseChild<TParent>>>'.
И я его понимаю Но хочется, чтобы клиент мог использовать конструкцию:
public class MyParent : BaseParent<MyChild>
{}
public class MyChild : BaseChild<MyParent>
{}
и получать доступ к типизированным чайлдам и паренту.
Можно ли как-то реализовать мой сценарий, или же надо кардинально менять архитектуру? Если менять, то в какую сторону? Разрывать взаимную ссылку и требовать, чтобы клиент ручками приводил, например, ссылку на парента:
public abstract class BaseParent<TChild>
where TChild : BaseChild
{
protected List<TChild> _childs = new List<TChild>();
}
public abstract class BaseChild
{
protected Object _parent;
}
public class MyParent : BaseParent<MyChild>
{ }
public class MyChild : BaseChild
{
private new MyParent _parent { get { return (MyParent)base._parent; } }
}
Уже голову сломал, размышляя, не хватает опыта работы с генериками Хоть Эрику Липперту пиши :-D
Здравствуйте, Silentor, Вы писали:
S>Пишу некий фреймворк. Там имеются базовые классы парента BaseParent и чайлдов BaseChild. В паренте я хочу иметь список чайлдов, а в чайлде — ссылку на парента.
interface IParent
{
IList<IChild> Children { get; }
}
interface IChild
{
IParent Parent { get; set; }
}
class BaseParent : IParent
{
private readonly IList<IChild> _children = new List<IChild>();
public IList<IChild> Children
{
get { return this._children; }
}
}
class BaseChild : IChild
{
public IParent Parent { get; set; }
}
class MyParent : BaseParent
{
}
class MyChild : BaseChild
{
}
Re: Проблема с циклической ссылкой generic классов
Здравствуйте, Silentor, Вы писали:
S>Добрый день (или вечер)!
S>И я его понимаю Но хочется, чтобы клиент мог использовать конструкцию: S>
S>public class MyParent : BaseParent<MyChild>
S> {}
S>public class MyChild : BaseChild<MyParent>
S> {}
S>
Мешают констрейнты. Без них компилится. Если они таки нужны для каких-то действий с объектами в базовых классах, то можно так
public abstract class BaseParent<TChild, TParent>
where TParent : BaseParent<TChild, TParent>
where TChild : BaseChild<TParent, TChild>
{
protected List<TChild> _childs = new List<TChild>();
}
public abstract class BaseChild<TParent, TChild>
where TParent : BaseParent<TChild, TParent>
where TChild : BaseChild<TParent, TChild>
{
protected TParent _parent;
}
public class MyParent : BaseParent<MyChild, MyParent>
{ }
public class MyChild : BaseChild<MyParent, MyChild>
{ }
Re[2]: Проблема с циклической ссылкой generic классов
Здравствуйте, scale_tone, Вы писали:
_>Здравствуйте, Silentor, Вы писали:
S>>Пишу некий фреймворк. Там имеются базовые классы парента BaseParent и чайлдов BaseChild. В паренте я хочу иметь список чайлдов, а в чайлде — ссылку на парента.
_>
_> interface IParent
_> {
_> IList<IChild> Children { get; }
_> }
_> interface IChild
_> {
_> IParent Parent { get; set; }
_> }
_> class BaseParent : IParent
_> {
_> private readonly IList<IChild> _children = new List<IChild>();
_> public IList<IChild> Children
_> {
_> get { return this._children; }
_> }
_> }
_> class BaseChild : IChild
_> {
_> public IParent Parent { get; set; }
_> }
_> class MyParent : BaseParent
_> {
_> }
_> class MyChild : BaseChild
_> {
_> }
_>
Да, но в этом случае MyParent не сможет без приведения типов дёргать методы экземпляра MyChild; ну и наоборот, чайлд не сможет вызывать специфические методы парента. В данном примере это не нужно, но в рабочей схеме необходимо.
Re: Проблема с циклической ссылкой generic классов
Здравствуйте, Silentor, Вы писали:
S>Добрый день (или вечер)!
S>Пишу некий фреймворк. Там имеются базовые классы парента BaseParent и чайлдов BaseChild. В паренте я хочу иметь список чайлдов, а в чайлде — ссылку на парента. Пишу через generic'и: S>
S>public abstract class BaseParent<TChild>
S> where TChild : BaseChild<BaseParent<TChild>>
S> {
S> protected List<TChild> _childs = new List<TChild>();
S> }
да, проблемма собственно не в рекурсивном определении типа, а в том что констрент указан
TChild: BaseChild<BaseParent<TChild>>
а тип Child является наследником другого типа BaseChild<TParent>. Так как TParent не является именно BaseParent<TChild> (он ведь его наследник), то и не компилится.
Re: Проблема с циклической ссылкой generic классов
Здравствуйте, Silentor, Вы писали:
S>Пишу некий фреймворк. Там имеются базовые классы парента BaseParent и чайлдов BaseChild. В паренте я хочу иметь список чайлдов, а в чайлде — ссылку на парента. Пишу через generic'и:
S>public abstract class BaseParent<TChild>
S> where TChild : BaseChild<BaseParent<TChild>>
S> {
S> protected List<TChild> _childs = new List<TChild>();
S> }
S>public abstract class BaseChild<TParent>
S> where TParent : BaseParent<BaseChild<TParent>>
S> {
S> protected TParent _parent;
S> }
Вам действительно нужны в базовых классах фреймворка защищённые поля? Вам известен какой-либо [разумный] сценарий, который потребует этого?
Какие конкретно вызовы у "детей" будет делать ваш BaseParent? А "дитё" у родителя? Или это просто так связь, "для наглядности"? Нужно ли в этих вызовах знать чей-либо конкретный тип?
Хорошего "общего" решения вашей проблемы не отыскать, поэтому дополнительные детали позволили бы выбрать оптимальный вариант.
Кстати, child во множественном числе — это children, а не "childs".
Help will always be given at Hogwarts to those who ask for it.
Re[2]: Проблема с циклической ссылкой generic классов
Здравствуйте, samius, Вы писали:
S>Мешают констрейнты. Без них компилится. Если они таки нужны для каких-то действий с объектами в базовых классах, то можно так
S>
S>public abstract class BaseParent<TChild, TParent>
S> where TParent : BaseParent<TChild, TParent>
S> where TChild : BaseChild<TParent, TChild>
S>{
S> protected List<TChild> _childs = new List<TChild>();
S>}
S>public abstract class BaseChild<TParent, TChild>
S> where TParent : BaseParent<TChild, TParent>
S> where TChild : BaseChild<TParent, TChild>
S>{
S> protected TParent _parent;
S>}
S>public class MyParent : BaseParent<MyChild, MyParent>
S>{ }
S>public class MyChild : BaseChild<MyParent, MyChild>
S>{ }
S>
Именно то, что нужно! Жаль, что сам не догадался Не пришла в голову мысль передавать типом-параметром в класс сам класс Премного благодарен
Re[2]: Проблема с циклической ссылкой generic классов
Здравствуйте, _FRED_, Вы писали:
_FR>Здравствуйте, Silentor, Вы писали:
S>>Пишу некий фреймворк. Там имеются базовые классы парента BaseParent и чайлдов BaseChild. В паренте я хочу иметь список чайлдов, а в чайлде — ссылку на парента. Пишу через generic'и: _FR>
S>>public abstract class BaseParent<TChild>
S>> where TChild : BaseChild<BaseParent<TChild>>
S>> {
S>> protected List<TChild> _childs = new List<TChild>();
S>> }
S>>public abstract class BaseChild<TParent>
S>> where TParent : BaseParent<BaseChild<TParent>>
S>> {
S>> protected TParent _parent;
S>> }
_FR>
_FR>Вам действительно нужны в базовых классах фреймворка защищённые поля? Вам известен какой-либо [разумный] сценарий, который потребует этого? _FR>Какие конкретно вызовы у "детей" будет делать ваш BaseParent? А "дитё" у родителя? Или это просто так связь, "для наглядности"? Нужно ли в этих вызовах знать чей-либо конкретный тип?
Надеялся избежать этого вопроса Parent на самом деле сервис, предоставляющий набор базовых операций. Child обслуживает подключение клиента, реализует базовый контракт. Класс сервиса и класс клиента наследуется и расширяется, туда добавляются новые операции.
Пускай у нас будет
abstract class BaseService
, реализующий некий базовый функционал, общий для всех сервисов проекта: создает WCF ServiceHost, привязывает эндпоинт на контракт IBaseWcfContract, настраивает сервис-хост так, чтобы когда к нам стукает клиент, создавался объект BaseClient, при наступлении определенных условий оповещает подключенных BaseClient. Вот его класс:
abstract class BaseClient : IBaseWcfContract
, где IBaseWcfContract — WCF ServiceContract с базовым набором ServiceOperation, а сам BaseClient хранит состояние клиента, обслуживает запросы согласно контракту, при необходимости дергая BaseService, передавая ему параметры, переданные клиентом + своё собственное состояние и отдавая результат обратно клиенту. Также передавая клиенту оповещения сервиса через Callback-интерфейс.
Задача состоит в том, чтобы была возможность написать
class MyService : BaseService
, реализующий несколько дополнительных методов, и
class MyClient : BaseClient, IMyWcfContract
реализующий несколько дополнительных операций, описанных в IMyWcfContract. Эти операции будут дёргать новые методы MyService. Работа в базовых BaseService и BaseClient будет идти без изменений и должна быть скрыта от пользователя, но, к примеру, эндпоинт должен привязываться к IMyWcfContract, на подключение клиента должен создаваться экземпляр класса MyClient, сервис должен иметь список подключенных MyClient для новых обратных оповещений, а в MyClient должна быть ссылочка на MyService, чтобы использовать новый функционал.
Отсюда вся игра с дженериками и передачами типов туда-сюда. Может есть какой убер-паттерн для таких ситуаций, буду рад узнать
Но ответ камрада Samius