Тип коллекций C # с ограниченными обобщениями

#c# #generics #collections #covariance #type-constraints

#c# #обобщения #Коллекции #ковариация #ограничения типа

Вопрос:

Я пытаюсь сделать что-то на C #, что довольно просто в Java, используя ограничение по типу подстановочных знаков. Я попытался свести его только к Java-коду, необходимому для иллюстрации проблемы (компиляции):

 public abstract class ParentClass {
    public abstract SomeBindingList<? extends ParentClass> parentList();
}

public class ChildClass extends ParentClass {
    private SomeBindingList<ChildClass> myList;

    public ChildClass() {
        // this could load from/bind to a database
        myList = new SomeBindingList<ChildClass>();
    }

    public SomeBindingList<? extends ParentClass> parentList() {
        return myList;
    }
}
  

Возможно, мне нужно подчеркнуть следующую строку, поскольку кто-то пометил это как дубликат:
SomeBindingList является сторонним BindingList , поэтому я не могу его изменить. Он параметризован и не может быть заменен непараметризованной версией.

Проблема, конечно, заключается в том, как реализовать parentList() метод в ChildClass , чтобы возвращать список, который можно использовать как список ParentClass объектов.

Похоже, что должен быть какой-то способ использовать where ключевое слово для предоставления ограниченного типа в C #, но я не могу заставить его работать (по крайней мере, синтаксически) с классом, который уже расширяет другой класс, т. Е. ChildClass , И, похоже, нет способа параметризовать только возвращаемое значениеметод (или свойство).

Я мог бы создать новый список и поместить все ChildClass элементы в новый список как ParentClass элементы, но (помимо того, что он запутанный) Я боялся, что это повлияет на поведение SomeBindingList .

Я не эксперт по C #, поэтому я уверен, что кто-то, более знакомый с языком, знает ответ. Спасибо!

@CodeCaster — я перепробовал много вариантов кода C # (это не компилируется, и я не могу найти вариант, который это делает):

 public abstract class ParentClass {
    public abstract List<T> parentList<T>() where T : ParentClass;
}

public class ChildClass {
    public List<ChildClass> myList;

    public ChildClass() {
        myList = new List<ChildClass>();
    }

    public override List<T> parentList<T>() where T : ParentClass {
        return myList;
    }
}
  

Я пробовал параметризацию ChildClass<T> , но это просто вызывает генерацию a List<ChildClass<T>> , которая не является List<T>

Комментарии:

1. Можете ли вы затем показать код, который вы написали, и какие проблемы у него есть?

2. @StriplingWarrior — я думаю, что ваше решение также было хорошим и может быть полезным для других, если вы также захотите опубликовать его здесь. Если бы я сам писал код с нуля, я бы, вероятно, попробовал это. Приветствия!

Ответ №1:

Я думаю, что ваша проблема здесь связана с желаемой совместимостью отношений между иерархией наследования общего стороннего SomeBindingList<T> класса и иерархией типов, используемых в качестве параметров.

Во-первых, позвольте мне дать вам код, который будет компилироваться:

 public interface ISomeBindingList<out T>
{
}

public abstract class ParentClass
{
    public abstract ISomeBindingList<ParentClass> parentList();
}

public class ChildClass : ParentClass
{
    private ISomeBindingList<ChildClass> myList;

    public ChildClass()
    {
        // this could load from/bind to a database
        // myList = new SomeBindingList<ChildClass>(); // <-- we need to figure out this
    }

    public override ISomeBindingList<ParentClass> parentList()
    {
        return myList;
    }
}
  

C # не предоставляет сопоставление общих типов для классов. Но это относится и к интерфейсам. Вам придется мыслить нестандартно и реализовать тривиальный адаптер для вашего стороннего SomeBindingList<T> разработчика, который реализует только те поднятые элементы, которые совместимы с различными вариантами, то есть: те элементы, которые T встречаются только в качестве выходных данных.

Например, предполагая SomeBindingList<T> , что содержит метод T Get() , вы должны перенести этот элемент в интерфейс адаптера и создать тривиальную реализацию адаптера.

Это был бы полный код:

 public interface ISomeBindingList<out T>
{
    T Get();
}

public class SomeBindingListAdapter<T> : SomeBindingList<T>, ISomeBindingList<T>
{
}

public abstract class ParentClass
{
    public abstract ISomeBindingList<ParentClass> parentList();
}

public class ChildClass : ParentClass
{
    private ISomeBindingList<ChildClass> myList;

    public ChildClass()
    {
        // this could load from/bind to a database
        myList = new SomeBindingListAdapter<ChildClass>();
    }

    public override ISomeBindingList<ParentClass> parentList()
    {
        return myList;
    }
}
  

Комментарии:

1. Большое спасибо! Я не знал этого об интерфейсах. Это хорошо работает в моем случае, когда я работаю с уже существующим кодом. Чем меньше я могу это изменить, тем лучше, и это означает, что мне не нужно параметризовать классы, что потребовало бы изменений во всем коде. Приветствия!