Я знаю, что ответ будет непростым, и я уже использую парочку (я думаю, уродливых) ключей. Я просто ищу элегантные ответы.
Абстрактный класс:
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, я обнаруживал ситуации, когда я был не в той области, чтобы делать что-то просто, вместо этого мне приходилось иметь дело с ними немного менее элегантно, чем мне нравится (пытаясь быть умным или, возможно, ленивый - я приму любой из этих ярлыков).
Мое отвращение к тому, что я назвал кладжем, в основном состоит в том, что нам нужно выполнить цикл по нашему набору записей просто для преобразования объектов в их базовое значение, что может снизить производительность.
Думаю, мне было интересно, сталкивался ли кто-нибудь еще с этим в своей кодировке раньше, и был ли кто-нибудь умнее или, по крайней мере, более элегантным, чем я, справляясь с этим.
Джон, возможно, в этом конкретном случае, когда реализация ToList одинакова для обоих классов, компилятор мог бы быть немного умнее. Но так оно и есть, нужно использовать стандартные средства - интерфейсы или наследование от базового класса.





В вашем случае MyObjectsA и MyObjectsB не имеют общего предшественника. Общий класс - это шаблон для классов разные, а не общий базовый класс. Если вы хотите иметь общие свойства в разных классах, используйте интерфейсы. Вы не можете вызвать Составлять список в цикле, потому что у него разные сигнатуры в разных классах. Вы можете создать ToList, который возвращает объекты, а не конкретный тип.
Конечно, есть: «// (где OtherObjectA реализует IOtherObjects)»
Что они делают? MyObjectA и MyObjectB - разные классы. ToList является частью абстрактного класса. Пожалуйста, внимательно прочтите вопрос, прежде чем делать такие комментарии.
почему у вас есть коллекция MyObjects? Есть ли конкретная причина, по которой у вас нет списка?
В общем, я должен согласиться. Это просто плохой дизайн.
Думаю, я стал бы третьим ... Если мой последний ответ верен, я бы не был доволен его дизайном.
Я использую термин "Коллекция" в общем, а не конкретно
Вероятно, вы все еще можете получить доступ к методу 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 не динамичен - он статичен.
Это так, но это устраняет необходимость указывать нужный тип, а не вводить MyObjectA myObject в myObjectsList.
Универсальные шаблоны используются для статических проверок типа времени нет 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
Или я что-то упустил?
Хорошо, теперь я действительно запутался .. Вы говорите, что хотите получить список значений Набранный из общего / абстрактного списка? (поэтому дочерние классы становятся неактуальными).
Вы не можете вернуть типизированный список, если типы являются дочерними элементами / разработчиками интерфейса - они не совпадают! Конечно, вы можете получить список элементов определенного типа из абстрактного списка следующим образом:
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;
}
Если я все еще нахожусь здесь вне базы, не могли бы вы перефразировать свой вопрос?! (Не похоже, что я единственный, кто не уверен, к чему вы клоните). Я пытаюсь установить, есть ли с вашей стороны недопонимание дженериков.
Правильно, похоже, вам нужна / нужна опция для получения типизированного списка или базового списка, да?
Это сделает ваш абстрактный класс таким: вы можете использовать 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;
}
}
Это не совсем "громоздкий" обходной путь (без неуважения) - это единственный способ сделать это .. Я думаю, что более серьезной проблемой здесь является проблема дизайна / грока. Вы сказали, что у вас есть проблема, этот код ее решает. Но если вы ожидали сделать что-то вроде:
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>.
Если у вас есть
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>()
шаблон.
Он делает именно то, что я искал,
Можете ли вы указать, что именно вы пытаетесь сделать, например, какой код, который вы пытались использовать, не сработал?