Предложения, требуемые со списками или перечислителями T при наследовании от универсальных классов

Я знаю, что ответ будет непростым, и я уже использую парочку (я думаю, уродливых) ключей. Я просто ищу элегантные ответы.

Абстрактный класс:

public interface IOtherObjects;

public abstract class MyObjects<T> where T : IOtherObjects
{
   ...

   public List<T> ToList()
   {
       ...
   }
}

Дети:

public class MyObjectsA : MyObjects<OtherObjectA> //(where OtherObjectA implements IOtherObjects)
{


}

public class MyObjectsB : MyObjects<OtherObjectB> //(where OtherObjectB implements IOtherObjects)
{


}

Возможно ли, перебирая коллекцию MyObject (или другую подобную группировку, общую или другую), чтобы затем использовать метод Составлять список базового класса MyObjects, поскольку мы не знаем конкретно тип T на данный момент.

РЕДАКТИРОВАТЬ Что касается конкретных примеров, всякий раз, когда это возникало, я некоторое время думал об этом и вместо этого делал что-то другое, поэтому в настоящее время нет требований. но так как он появлялся довольно часто, я подумал, что буду его спустить на воду.

РЕДАКТИРОВАТЬ @Sara, это не конкретный тип коллекции, о которой я забочусь, это может быть список, но все же метод ToList каждого экземпляра относительно непригоден для использования без анонимного типа)

@aku, правда, и этот вопрос может быть относительно гипотетическим, однако возможность извлекать и работать со списком T объектов, зная только их базовый тип, была бы очень полезной. То, что ToList возвращает список BaseType, было одним из моих обходных путей

РЕДАКТИРОВАТЬ @ all: До сих пор я надеялся на это обсуждение, хотя оно в значительной степени подтверждает все, о чем я подозревал. Спасибо всем, но кто угодно, не стесняйтесь вносить свой вклад.

РЕДАКТИРОВАТЬ @ Rob, да, это работает для определенного типа, но не тогда, когда тип известен только как List of IOtherObjects.

@Rob Опять таки Спасибо. Обычно это был мой неуклюжий обходной путь (без неуважения :)). Либо это, либо с помощью функции ConvertAll для Downcast через делегата. Спасибо, что нашли время разобраться в проблеме.

КВАЛИФИКАЦИОННОЕ РЕДАКТИРОВАНИЕ на случай, если я немного запутался

Чтобы быть более точным (возможно, я позволил моей последней реализации этого сделать это слишком сложным):

скажем, у меня есть 2 типа объектов, B и C, унаследованные от объекта A.

Было представлено множество сценариев, в которых из Списка B или Списка C, или в других случаях из Списка того и другого, но я не знаю, какие, если я нахожусь в базовом классе, мне нужен менее конкретный Список А.

Приведенный выше пример был упрощенным примером последнего воплощения проблемы Список менее конкретных.

Обычно он проявляется, поскольку я обдумываю возможные сценарии, которые ограничивают объем кода, который необходимо написать, и кажутся немного более элегантными, чем другие варианты. Мне очень хотелось обсудить возможности и другие точки зрения, которые у меня более или менее понятны. Я удивлен, что до сих пор никто не упомянул ConvertAll (), поскольку это еще один обходной путь, который я использовал, но слишком подробный для рассматриваемых сценариев.

@Rob Опять же и Сара

Спасибо, однако мне кажется, что я понимаю дженерики во всей их статической контекстной славе и понимаю проблемы, которые здесь возникают.

Фактический дизайн нашей системы и использование универсальных компонентов (и я могу сказать это без предвзятости, поскольку я был только одним из участников разработки) были выполнены хорошо. Это когда я работал с базовым API, я обнаруживал ситуации, когда я был не в той области, чтобы делать что-то просто, вместо этого мне приходилось иметь дело с ними немного менее элегантно, чем мне нравится (пытаясь быть умным или, возможно, ленивый - я приму любой из этих ярлыков).

Мое отвращение к тому, что я назвал кладжем, в основном состоит в том, что нам нужно выполнить цикл по нашему набору записей просто для преобразования объектов в их базовое значение, что может снизить производительность.

Думаю, мне было интересно, сталкивался ли кто-нибудь еще с этим в своей кодировке раньше, и был ли кто-нибудь умнее или, по крайней мере, более элегантным, чем я, справляясь с этим.

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

Jon Limjap 10.09.2008 08:19

Джон, возможно, в этом конкретном случае, когда реализация ToList одинакова для обоих классов, компилятор мог бы быть немного умнее. Но так оно и есть, нужно использовать стандартные средства - интерфейсы или наследование от базового класса.

aku 10.09.2008 08:50
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
290
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

В вашем случае MyObjectsA и MyObjectsB не имеют общего предшественника. Общий класс - это шаблон для классов разные, а не общий базовый класс. Если вы хотите иметь общие свойства в разных классах, используйте интерфейсы. Вы не можете вызвать Составлять список в цикле, потому что у него разные сигнатуры в разных классах. Вы можете создать ToList, который возвращает объекты, а не конкретный тип.

Конечно, есть: «// (где OtherObjectA реализует IOtherObjects)»

Frank Krueger 10.09.2008 08:45

Что они делают? MyObjectA и MyObjectB - разные классы. ToList является частью абстрактного класса. Пожалуйста, внимательно прочтите вопрос, прежде чем делать такие комментарии.

aku 10.09.2008 08:53

почему у вас есть коллекция MyObjects? Есть ли конкретная причина, по которой у вас нет списка?

В общем, я должен согласиться. Это просто плохой дизайн.

Frank Krueger 10.09.2008 09:41

Думаю, я стал бы третьим ... Если мой последний ответ верен, я бы не был доволен его дизайном.

Rob Cooper 10.09.2008 09:53

Я использую термин "Коллекция" в общем, а не конкретно

johnc 19.09.2008 03:25

Вероятно, вы все еще можете получить доступ к методу ToList (), но, поскольку вы не уверены в типе, не сработает ли это?

foreach(var myObject in myObjectsList)
    foreach(var obj in myObject.ToList())
        //do something

Конечно, это будет работать только на C# 3.0.

Обратите внимание, что использование var просто устраняет необходимость знать, какой тип содержат списки; в отличие от комментариев Фрэнка о том, что я заблуждаюсь, что var сделает набор текста динамичным.

var не волшебный. Вы также можете сделать это в C# 2.0. Спросите у типа myObject и obj, и вы увидите, что в них нет ничего особенного. var не динамичен - он статичен.

Frank Krueger 10.09.2008 08:32

Это так, но это устраняет необходимость указывать нужный тип, а не вводить MyObjectA myObject в myObjectsList.

Jon Limjap 10.09.2008 08:34

Универсальные шаблоны используются для статических проверок типа времени нет runtime dispatch. Используйте наследование / интерфейсы для диспетчеризации во время выполнения, используйте универсальные шаблоны для гарантий типов во время компиляции.

interface IMyObjects : IEnumerable<IOtherObjects> {}
abstract class MyObjects<T> : IMyObjects where T : IOtherObjects {}

IEnumerable<IMyObjects> objs = ...;
foreach (IMyObjects mo in objs) {
    foreach (IOtherObjects oo in mo) {
        Console.WriteLine(oo);
    }
}

(Очевидно, я предпочитаю Enumerables спискам.)

ИЛИ ЖЕ Просто используйте подходящий динамический язык, например VB. :-)

Хорошо, я сбит с толку, у меня работает следующий код (любопытство взяло верх!):

// Original Code Snipped for Brevity - See Edit History if Req'd

Или я что-то упустил?

Обновить после ответа от OP

Хорошо, теперь я действительно запутался .. Вы говорите, что хотите получить список значений Набранный из общего / абстрактного списка? (поэтому дочерние классы становятся неактуальными).

Вы не можете вернуть типизированный список, если типы являются дочерними элементами / разработчиками интерфейса - они не совпадают! Конечно, вы можете получить список элементов определенного типа из абстрактного списка следующим образом:

    public List<OfType> TypedList<OfType>() where OfType : IOtherObjects
    {
        List<OfType> rtn = new List<OfType>();

        foreach (IOtherObjects o in _objects)
        {
            Type objType = o.GetType();
            Type reqType = typeof(OfType);

            if (objType == reqType)
                rtn.Add((OfType)o);
        }

        return rtn;
    }

Если я все еще нахожусь здесь вне базы, не могли бы вы перефразировать свой вопрос?! (Не похоже, что я единственный, кто не уверен, к чему вы клоните). Я пытаюсь установить, есть ли с вашей стороны недопонимание дженериков.

Другое обновление: D

Правильно, похоже, вам нужна / нужна опция для получения типизированного списка или базового списка, да?

Это сделает ваш абстрактный класс таким: вы можете использовать ToList, чтобы получить конкретный тип, или ToBaseList (), чтобы получить список типа интерфейса. Это должно работать в любых ваших сценариях. Это поможет?

public abstract class MyObjects<T> where T : IOtherObjects
{
    List<T> _objects = new List<T>();

    public List<T> ToList()
    {
        return _objects;
    }

    public List<IOtherObjects> ToBaseList()
    {
        List<IOtherObjects> rtn = new List<IOtherObjects>();
        foreach (IOtherObjects o in _objects)
        {
            rtn.Add(o);
        }
        return rtn;
    }
}

Обновление # 3

Это не совсем "громоздкий" обходной путь (без неуважения) - это единственный способ сделать это .. Я думаю, что более серьезной проблемой здесь является проблема дизайна / грока. Вы сказали, что у вас есть проблема, этот код ее решает. Но если вы ожидали сделать что-то вроде:

public abstract class MyObjects<T> where T : IOtherObjects
{
    List<T> _objects = new List<T>();

    public List<IOtherObjects> Objects
    { get { return _objects; } }
}
#warning This won't compile, its for demo's sake.

И уметь выбирать типы, которые выходят из этого, как еще мог вы это делаете ?! У меня такое чувство, что вы не совсем понимаете, в чем смысл дженериков, и пытаетесь заставить их делать то, для чего они не предназначены !?

Его беспокоило (я думал), когда у него был коллекция MyObject <?> S, а не один MyObject <Whatever>.

Frank Krueger 10.09.2008 09:26
Ответ принят как подходящий

Если у вас есть

class B : A
class C : A

И у вас есть

List<B> listB;
List<C> listC;

который вы хотите рассматривать как список родительского типа

Тогда вам следует использовать

List<A> listA = listB.Cast<A>().Concat(listC.Cast<A>()).ToList()

Я недавно нашел

List<A>.Cast<B>().ToList<B>()

шаблон.

Он делает именно то, что я искал,

Другие вопросы по теме