Deutsch

C# - вернуть типизированную коллекцию

1135  1 2 все
alex445 патриот04.04.24 10:12
04.04.24 10:12 
Последний раз изменено 04.04.24 10:21 (alex445)

Есть система классов


class Base

class Derived1 : Base

class Derived2 : Base


Есть коллеции объектов наследованных классов


List<Derived1> Derived1Collection
List<Derived2> Derived2Collection


Хочу дженерик метод для класса, где хранятся эти коллекции, который вернёт любую из этих коллекций. Типа такого


GetData<Derived1>();


Сделал так


public static IEnumerable<T> GetData<T>() where T : Base
    => typeof(T) switch
    {
        _ when typeof(T) == typeof(Derived1) => Derived1Collection.Cast<T>(),
        _ when typeof(T) == typeof(Derived2) => Derived2Collection.Cast<T>(),
        _ => throw new Exception($"{typeof(T)} is not supported."),
    };


Ошибок компиляции не показывает, но ещё не юзал.


Мне не нравится - некрасиво. Лишняя хреть typeof(T) перед свичом. И зачем-то требует Cast, а без него пишет


Cannot implicitly convert type List<Derived1> to IEnumerable<T>


Что за глупость? Не может сконверить более унаследованный класс в менее унаследованный? Причём менее унаследованный что для коллекции, что для типа коллекции - Derived1 кастуется в Base, List кастуется в IEnumerable - у нас же ковариация тут работает для IEnumerable .

#1 
7495 старожил04.04.24 11:14
7495
NEW 04.04.24 11:14 
в ответ alex445 04.04.24 10:12
Ошибок компиляции не показывает, но ещё не юзал.


Отвлекаешь нас непроверенным нерабочим кодом? Как протестишь - приходи снова! зло

Вопросы и Ответы - Программируем калькулятор пособий для беженцев вместе.
#2 
alex445 патриот04.04.24 12:36
NEW 04.04.24 12:36 
в ответ 7495 04.04.24 11:14

Ты, поридж, не в ту комнату забрёл - коины в другой майнят. ))

#3 
alex445 патриот04.04.24 12:38
NEW 04.04.24 12:38 
в ответ alex445 04.04.24 10:12

Так, мне ответили в другом месте. В принципе, понятно. Добавлю в список своих каверзных вопросов, чтобы мучить всяких неудачников, которых мы не будем брать на работу. Какой ты нафиг сеньёр, если азы не знаешь?

))

#4 
7495 старожил04.04.24 13:50
7495
NEW 04.04.24 13:50 
в ответ alex445 04.04.24 12:38

с одной стороны, молодец, решил

С другой - опять же нифига не понял, в чем проблема была

Вопросы и Ответы - Программируем калькулятор пособий для беженцев вместе.
#5 
Бесконечный цикл постоялец04.04.24 18:21
NEW 04.04.24 18:21 
в ответ alex445 04.04.24 12:38

#6 
Бесконечный цикл постоялец04.04.24 18:39
NEW 04.04.24 18:39 
в ответ 7495 04.04.24 13:50, Последний раз изменено 04.04.24 18:40 (Бесконечный цикл)
С другой - опять же нифига не понял, в чем проблема была

Это заморочки ООП и генериков с коллекциями. В классическом ООП есть только объекты, который имеют класс, а классы существуют в иерерхии. Далле определяется кому и что можно присваивать и т.д. А теперь мы вводим две категории: 1) обычные объекты плюс 2) коллекции (ну или любой другой класс) с параметром обычных объектов. Каждый существует в своей собственной иерархии: иерархия обычных объектов и иерархия коллекций (но с параметром класса объекта). Теперь надо вводить более сложные правила в плане кому и что можно присваивать и кто с кем может работать. Называется ковариантность (движение в одном направлении в обоих иерархиях) и контравариантность (движение в разных направлениях).


На самом деле там корни в старой махровой функциональщине, а потом уже через долгое время это скоммуниздили и переиначили оопешники. По любому гемор обеспечен.

#7 
Срыв покровов патриот04.04.24 20:08
NEW 04.04.24 20:08 
в ответ alex445 04.04.24 10:12

Хранить коллекции в общей куче и выдавать вот так?

return commonCollection.OfType<T>();


#8 
Срыв покровов патриот04.04.24 23:00
NEW 04.04.24 23:00 
в ответ alex445 04.04.24 10:12, Последний раз изменено 04.04.24 23:00 (Срыв покровов)

Твой метод не работает без каста по той же причине, что и вот этот

IEnumerable GetData() where T : Base

{

return Derived1Collection ;

}


Ну и твой свитч это полный Пэ.

#9 
alex445 патриот05.04.24 01:22
NEW 05.04.24 01:22 
в ответ Срыв покровов 04.04.24 20:08, Последний раз изменено 05.04.24 01:35 (alex445)
Хранить коллекции в общей куче и выдавать вот так?


return commonCollection.OfType();

Не пойдёт - это при каждом обращении к коллекции придётся проходить по всем элементам и фильтровать. А если кешировать, то вот у меня уже есть коллекции, разделённые по типу.


Мой вариант нормальный и уже работает. Я просто хочу узнать, можно ли лучше организовать, и почему каст требуется. Узнал уже, что каст нужен из-за ограничений языка - создатели так решили, что производный тип к базовому привести можно, а коллекцию производных типов к коллекции базовых - нельзя. Они считают это более типобезопасным. Ещё. Неявным образом нельзя, но если ты хочешь явно, то кастуй.

#10 
alex445 патриот05.04.24 01:32
NEW 05.04.24 01:32 
в ответ Срыв покровов 04.04.24 23:00
Ну и твой свитч это полный Пэ.

Ваш вариант, без полного Пэ?

#11 
alex445 патриот05.04.24 01:35
NEW 05.04.24 01:35 
в ответ Срыв покровов 04.04.24 23:00

Твой метод не работает без каста по той же причине, что и вот этот

IEnumerable GetData() where T : Base

{

return Derived1Collection ;

}

У меня и есть такой метод, только добавлен выбор типа.

#12 
alex445 патриот05.04.24 11:41
NEW 05.04.24 11:41 
в ответ alex445 04.04.24 10:12, Последний раз изменено 05.04.24 11:43 (alex445)
public static IEnumerable<T> GetData<T>() where T : Base
    => typeof(T) switch
    {
        _ when typeof(T) == typeof(Derived1) => Derived1Collection.Cast<T>(),
        _ when typeof(T) == typeof(Derived2) => Derived2Collection.Cast<T>(),
        _ => throw new Exception($"{typeof(T)} is not supported."),
    };

Такой вариант тоже работает - приведение типа сразу для всей коллекции вместо кастинга каждого её элемента:


_ when typeof(T) == typeof(Derived1) => (IEnumerable<T>)Derived1Collection,


Это даже получше будет, т.к. не надо при каждом обращении к GetData проходить по всей коллекции.


typeof(T) перед выражением switch, кстати, не используется. Просто это требования синтаксиса свича. Лучше, наверное, пачку if-else вместо этого корявого свича.

#13 
alex445 патриот05.04.24 11:51
NEW 05.04.24 11:51 
в ответ alex445 05.04.24 11:41, Последний раз изменено 05.04.24 11:56 (alex445)

Да, пачка ифов лучше. Ну, вроде теперь почти идеально.


public static IEnumerable<T> GetData<T>() where T : Base
{
    if (typeof(T) == typeof(Derived1)) return (IEnumerable<T>)Derived1Collection;
    else if (typeof(T) == typeof(Derived2)) return (IEnumerable<T>)Derived2Collection;
    else throw new Exception($"{typeof(T)} is not supported.");
}
#14 
Срыв покровов патриот05.04.24 11:56
NEW 05.04.24 11:56 
в ответ alex445 05.04.24 11:41
typeof(T) перед выражением switch, кстати, не используется

в этом и заключается полный Пэ.

#15 
Срыв покровов патриот05.04.24 12:04
NEW 05.04.24 12:04 
в ответ alex445 05.04.24 01:22
Узнал уже, что каст нужен из-за ограничений языка - создатели так решили, что производный тип к базовому привести можно, а коллекцию производных типов к коллекции базовых - нельзя

не совсем
вот такое ведь работает

IEnumerable<Base> result = new List<Derived1>();

#16 
7495 старожил05.04.24 12:07
7495
NEW 05.04.24 12:07 
в ответ alex445 05.04.24 11:41

Вот почему ты такой? Когда ты спрашиваешь - то тебе ребята на стековерфлоу всё разжевали, помогли решить твою задачку.


А как тебя просят - нарисуй цикл, помоги начинающим товарищам запустить квантовый блокчейн, так сразу в кусты убежал.


Для тебя делов то 3-4 минуты времени, сам при этом новые вещи для себя откроешь, если вдруг от индусов уйдёшь, помогай!

Вопросы и Ответы - Программируем калькулятор пособий для беженцев вместе.
#17 
Срыв покровов патриот05.04.24 12:08
NEW 05.04.24 12:08 
в ответ Срыв покровов 05.04.24 12:04

ты не понял, почему это не работает

public static IEnumerable<T> GetData<T>() where T : Base
    => typeof(T) switch
    {
        _ when typeof(T) == typeof(Derived1) => Derived1Collection, //здесь компилятор не уверен, что твой Т является Derived1
        ...
    };
#18 
alex445 патриот05.04.24 12:39
NEW 05.04.24 12:39 
в ответ 7495 05.04.24 12:07

Вот почему ты такой? Когда ты спрашиваешь - то тебе ребята на стековерфлоу всё разжевали, помогли решить твою задачку.


А как тебя просят - нарисуй цикл, помоги начинающим товарищам запустить квантовый блокчейн, так сразу в кусты убежал.


Для тебя делов то 3-4 минуты времени, сам при этом новые вещи для себя откроешь, если вдруг от индусов уйдёшь, помогай!

Тоже спросите ребят на Stackoverflow.

#19 
alex445 патриот05.04.24 12:41
NEW 05.04.24 12:41 
в ответ Срыв покровов 05.04.24 12:08, Последний раз изменено 05.04.24 12:42 (alex445)
public static IEnumerable<T> GetData<T>() where T : Base
    => typeof(T) switch
    {
        _ when typeof(T) == typeof(Derived1) => Derived1Collection, //здесь компилятор не уверен, что твой Т является Derived1
        ...
    };

Да, такое объяснение тоже встречал - в compile time он не уверен, и ему нужно явное приведение. А в run time все дженерики становятся явными типами, поэтому по идее код должен нормально выполняться, если заигнорить эту ошибку компиляции. Но меня вариант с явным приведением устраивает - не будем ломать статический анализ компилятора.

#20 
1 2 все