Проблема с циклической ссылкой generic классов
От: Silentor Украина  
Дата: 26.06.11 17:44
Оценка:
Добрый день (или вечер)!

Пишу некий фреймворк. Там имеются базовые классы парента 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
generic генерик циклическая ссылка
Re: Проблема с циклической ссылкой generic классов
От: scale_tone Норвегия https://scale-tone.github.io/
Дата: 26.06.11 18:22
Оценка: +1
Здравствуйте, 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 классов
От: samius Япония http://sams-tricks.blogspot.com
Дата: 26.06.11 18:42
Оценка: +1
Здравствуйте, 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 классов
От: Silentor Украина  
Дата: 26.06.11 18:44
Оценка:
Здравствуйте, 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 классов
От: samius Япония http://sams-tricks.blogspot.com
Дата: 26.06.11 18:47
Оценка:
Здравствуйте, 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 классов
От: _FRED_ Черногория
Дата: 26.06.11 19:23
Оценка:
Здравствуйте, 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 классов
От: Silentor Украина  
Дата: 26.06.11 21:27
Оценка:
Здравствуйте, 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 классов
От: Silentor Украина  
Дата: 26.06.11 22:26
Оценка:
Здравствуйте, _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
Автор: samius
Дата: 26.06.11
меня полностью устраивает.

_FR>Кстати, child во множественном числе — это children, а не "childs".

Знаю, но привык так писать в идентификаторах
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.