#c# #oop #generics
#c# #ооп #общие
Вопрос:
В настоящее время я использую стороннюю dll и не имею возможности отключить ее или не использовать. У меня есть класс, который выглядит следующим образом:
public abstract class ParentClass<T> : ApiClass<T> where T : ApiClass<T>
{
//...stuff
}
Это вызывает несколько проблем, если я хочу создать словарь или вернуть тип ParentClass, чтобы мне не нужно было заранее знать, какой это дочерний класс. Чтобы упростить разработку вокруг этого, я хотел бы сделать что-то вроде этого:
//ChildClass should be what is passed to the generic, without directly referencing it
public abstract class ParentClass : ApiClass<InheritedClass>
{
//...stuff
}
public class ChildClass : ParentClass
{
}
//Now I could define a dictionary like
Dictionary<KeyClass, ParentClass> Map;
Определение его, как во втором примере, позволило бы мне создать словарь, как и в первом. Я не уверен, что смогу определить словарь, когда у меня нет значения для общего? Немного больше информации о ApiClass, определение для ApiClass выглядит следующим образом:
public abstract class ApiClass<Wrapper> : OtherApiClass where Wrapper : ApiClass<Wrapper>
{
//...Stuff
}
Итак, в подобном сценарии, как бы я определил свой родительский класс без общего, но где тип, передаваемый ApiClass, является дочерним классом родительского класса (ApiClass явно нуждается в дочернем типе)?
Редактировать: немного прояснено значение InheritedClass
Редактирование 2: я не ищу альтернативы, я спрашиваю, как это сделать. Если я передам что-либо, кроме последнего унаследованного класса (текущего родительского класса, который я определил в начале вопроса), произойдет сбой Api. Если бы синтаксис существовал, он выглядел бы примерно так
public abstract class ParentClass : ApiClass<typeof(this)>
Комментарии:
1. Корень ваших проблем, похоже, вызван определением
class ApiClass<T> where T : ApiClass<T>
. Это очень необычное определение. Это класс, который вы написали, или это что-то определенное в сторонней библиотеке?2. Это определяется сторонней библиотекой, и я бы действительно предпочел, чтобы ее не существовало. К сожалению, для того, чтобы мой родительский класс или любой его дочерний элемент могли правильно загружаться в их приложение, он должен следовать этому ограничению, при котором он передает себя или дочерний класс в качестве общего. У меня также нет или нет доступа к исходному коду для него.
3. Исходя из этого определения, я предполагаю, что вы могли бы просто сохранить a
Dictionary<KeyClass, OtherApiClass>
и привести значение кParentClass<T>
тому, когда вам нужна конкретная функциональность4. Это может не помочь. Вероятно, это не поможет. Хорошо, это не поможет. scotthannen.org/blog/2018/04/05 /…
5. @andrewwwilliamson на самом деле, я не уверен, почему я не подумал просто использовать OtherApiClass в словаре вместо этого. Это, вероятно, решает все проблемы, которые мне нужны для этого, поскольку мне в основном нужно ссылаться на него в функциях и словарях. Спасибо! Я просто оставлю класс как есть, даже если захочу его изменить. В любом случае, это в основном ограничение из-за зависимости.
Ответ №1:
Я полагаю, что сторонняя библиотека, которую вы используете, определяет ApiClass
следующим образом:
public abstract class ApiClass<T> where T : ApiClass<T>
Это пример любопытно повторяющегося шаблона шаблона.
Давайте рассмотрим это на конкретном примере:
public abstract class ApiClass<T> where T : ApiClass<T>
{
public abstract T GetNewInstance();
}
Если бы у меня был этот код, я мог бы использовать его следующим образом:
public class MyApiClass : ApiClass<MyApiClass>
{
public override MyApiClass GetNewInstance()
{
return new MyApiClass();
}
}
Это позволяет MyApiClass
иметь метод, определенный как абстрактный метод в этом родительском классе, который знает о текущем экземпляре. Эрик Липперт обсуждает это здесь .
Недостатком этого является то, что им можно злоупотреблять:
public class MyNefariousApiClass : ApiClass<MyApiClass>
{
public override MyApiClass GetNewInstance()
{
throw new NotImplementedException();
}
}
Это законно, но в C # нет способа принудительно использовать сам класс в качестве универсального параметра. Если бы у нас class MyNefariousApiClass : ApiClass<this>
это было, но у нас этого нет.
Теперь, когда вы говорите, что у вас есть класс public abstract class ParentClass<T> : ApiClass<T> where T : ApiClass<T>
, который является вашим кодом для использования сторонней библиотеки.
Если это так, то это не обычный способ использования этого шаблона. Если вы переопределяете класс, он должен выглядеть так:
public abstract class ParentClass<T> : ApiClass<T> where T : ParentClass<T>
Затем мы можем довести это до конечной точки и определить ChildClass
примерно так:
public class ChildClass : ParentClass<ChildClass>
{ }
Теперь, если вам нужен словарь, который может содержать любой такой ChildClass
, вы застряли, поскольку родительский тип является общим. Единственное разумное решение — использовать интерфейс.
public interface IParent
{ }
public abstract class ParentClass<T> : ApiClass<T>, IParent where T : ParentClass<T>
{ }
Затем вы можете написать Dictionary<KeyClass, IParent> dictionary = new ...
.
Комментарии:
1. Не уверен, что это действительно что-то решает, так как теперь мне нужен интерфейс с нулевыми функциями, просто чтобы указать его в словаре. Как @andrewwwilliamson в комментариях, поскольку ApiClass наследует OtherApiClass, я могу просто указать OtherApiClass . Это не помогает мне решить проблему с шаблоном, но если у нас не было чего-то подобного
class Parent : ApiClass<T> where T : ApiClass<typeof(this)>
, я действительно не вижу способа обойти это. Не уверен, что я получу, указав ParentClass<T> вместо ApiClass<T> в общем? Есть ли польза в этом?2. @VextaRelos — Цель этого шаблона — позволить конечному классу возвращать экземпляр самого себя. Если вы придерживаетесь
ApiClass<T>
этого, вы не сможете этого сделать. Возможно, у вас другие потребности, но именно поэтому они выбрали этот шаблон в первую очередь. И я ясно дал понять, что вы можете подорвать это, потому что C # не позволяетclass Parent : ApiClass<T> where T : this
.3. Я немного обеспокоен двумя отрицательными голосами. Я думал, что это было довольно четкое обсуждение рассматриваемого шаблона и того факта, что вы не можете делать то, что хотите, и вам нужно договориться с интерфейсом — другого пути нет.
4. Не уверен, кто проголосовал против, но да, похоже, на самом деле нет возможности делать то, что мне нужно. Дело не в том, что я хочу передать <typeof(this)> в ApiClass , дело в том, что ApiClass буквально приведет к сбою программы, если я этого не сделаю. Мой проект выполняется как дополнение к этому Api. У меня нет другого варианта определения класса.
5. @VextaRelos — В вашем вопросе должны быть такие детали, включая полный код, чтобы показать, что вам нужно.