Я получил класс «ListOfT» из List<T>
, чтобы иметь место для размещения функций, которые работают со списком T.
У меня есть функция, которая перебирает список и должна возвращать подсписок T с помощью Linq:
public class ListOfT : List<T>
{
//no explicit constructor
//other functions to work on this list of T's ...
public ListOfT GenerateSubList()
{
return this.Where(t => t.SomeCriteria).ToList(); // <- compiler will complain
return (ListOfT) this.Where(t => t.SomeCriteria).ToList(); // <- compiler is ok, but I get a runtime error
}
}
Компилятор теперь жалуется, что не может привести List<T>
к ListOfT. Очевидно, что вызов «ToList()» создаст новый List<T>
, но мне бы хотелось иметь что-то, что создает экземпляр ListOfT
.
Я попробовал явное приведение (что успокоит компилятор), но это приведет к ошибке во время выполнения.
Есть ли элегантный способ выполнить это приведение (или заменить вызов ToList())?
Решение (см. ответ @SomeBody): Спасибо всем за ваши добрые комментарии и предложенные решения. Вместо того, чтобы комментировать каждый из них, я попытаюсь суммировать его здесь:
public class ListOfT : List<T>
{
//public constructor
public ListOfT() {}
//hidden constructor to create a ListOfT from an enumerable
private ListOfT(IEnumerable<T> subList)
: base(subList) { }
public ListOfT GenerateSubList()
{
//using linq to create an enumerator that gets
//passed to the hidden constructor
return new ListOfT(this.Where(t => t.SomeCriteria));
}
}
Возможно, мне следовало использовать SomeClass
вместо T
в своем вопросе, чтобы уточнить, что класс ListOfT
является конкретным примером использования List<T>
в качестве SomeClass
, а не шаблонным классом сам по себе.
@Tim Schmelter: Часть T
должна была указать, что каждый экземпляр t.SomeCriteria
, содержащийся в списке, имеет свойство, которое я хочу проверить, чтобы включить его в подсписок. Спасибо за ссылку на Почему бы не наследовать от List<T>, я читал ее, ожидая ответов. См. мои комментарии ниже относительно «имеет» и «есть».
Кроме того, (@Rookie), да, новый список, возвращаемый T
, будет содержать не копии исходных элементов, а ссылки на исходные GenerateSubList()
, хранящиеся в экземпляре ListOfT, который я вызываю T
on. Это предназначено.
@Орен Тамам и @Филдор: я не пошел по маршруту «имеет» вместо «есть», так как для этого мне потребовалось бы реализовать интерфейс GenerateSubList()
. Как упоминал выше @Tim, ведется продолжительная дискуссия о том, почему бы не использовать классы, производные от IList<T>
. Лично я считаю, что это хороший способ собрать в одном месте функции, работающие со списками определенного типа. По моему мнению, разница между has и is сводится к личному выбору и сценарию, в котором вы его используете.
@Ralf упомянул методы расширения как еще один способ «улучшить» конкретный тип списка. Я использовал их в некоторых местах, где у меня была иерархия классов, где каждый потомок должен был быть «перечислен». В этом случае статический класс, содержащий расширения, был хорошим способом собрать все функции моего зоопарка классов, связанные со списками, в одном месте.
«Я получил класс «ListOfT» от List<T>» — это вообще не рекомендуется. И вам это тоже не нужно. С помощью этого простого фильтра вы не сможете получить ничего, кроме T-Generic (List<T>, IList<T> или IEnumerable<T>... или T).
Возможно, если вы более подробно расскажете, что именно вы пытаетесь сделать, мы сможем указать вам путь...
«Я не пошел по пути «имеет» вместо «есть» — я никогда не говорил, что вам следует это делать. Я сказал, расскажите нам, что вы пытаетесь сделать, потому что я думаю, что вы совершенно сбились с пути.
^^ dotnetfiddle.net/2t8WCL (особенно обратите внимание на последний пример)
Не вкладывайте ответ в вопрос. Вы не можете ответить на свой вопрос ниже.
@Charlieface Я включил в вопрос решение, которое использовал, потому что ответ ниже был не совсем тем, что я искал (хотя и достаточно близко, чтобы направить меня в нужное русло). Не уверен, какой будет рекомендуемая процедура в этих местах, если я не смогу ответить на свой вопрос...
Может ли кто-нибудь объяснить, почему мой вопрос получил два отрицательных голоса?
Вы можете ответить на свой вопрос. Просто перейдите в поле ниже, введите свой ответ и нажмите «Опубликовать свой ответ». Вопрос должен быть просто вопросом, а не тем и другим.
В List<T>
есть конструктор, который принимает IEnumerable<T>
. Вы можете вызвать это из своего подкласса:
public class MyList<T> : List<T>
{
public MyList()
{
}
private MyList(IEnumerable<T> items)
: base(items)
{
}
public MyList<T> GenerateSubList()
{
return new MyList<T>(this.Where(t => t.SomeCriteria));
}
}
Конечно, вы также можете сделать конструктор общедоступным, если хотите использовать его вне класса.
Онлайн-демо: https://dotnetfiddle.net/RxBMr0
Интересно, что такое SomeCriteria
без ограничения типа T
. Все требование неясно. Кроме того, вам следует связать это: Почему бы не наследовать от List<T>
Спасибо @SomeBody, введение дополнительного конструктора (и общедоступного значения по умолчанию, позволяющего создавать экземпляр ListOfT) помогло.
Я не понимаю отрицательных голосов по этому ответу. Единственная причина, о которой я могу думать, это то, что в этом примере используется public class MyList<T> : List<T>
вместо public class MyList : List<T>
(как я это сделал в решении, опубликованном в моем отредактированном ответе). Было бы неплохо, если бы отрицательный голос прокомментировал, почему они считают этот ответ неправильным.
вы можете использовать «Имеет отношение», а не «Есть».
public class ListOfT : IList<string>
{
private readonly List<string> Strings = new();
public ListOfT(List<string> list)
{
Strings = list;
}
#region IList implemnetions
//.....
#endregion
public ListOfT GenerateSubList()
{
return new ListOfT(this.Where(t => t.Contains(".")).ToList());
}
}
Но зачем тогда наследовать от IList<string>
? Ваш класс по-прежнему является списком
это часть переоборудования, позволяющая использовать список<T> или строку в этом примере.
Список, возвращаемый ToList(), представляет собой новый экземпляр, который извлекает определенное значение из экземпляра ListOfT и сохраняет его в новом буфере, а в памяти этот список не имеет экземпляра ListOfT, который является подклассом List. Вам необходимо создать новый экземпляр List, реализующий ListOfT, и правильно сохранить значение.
Даже если вы думаете, что это не для вас, я хотел бы предложить другой путь возможным читателям со схожими требованиями:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
// List of instances, some of which have the property, some do not.
List<SomeBaseType> list = [new SomeSubType(),
new SomeSubTypeWP(){SomeProperty = "One"},
new SomeSubType(),
new SomeSubTypeWP(){SomeProperty = "Two"},
new SomeSubType(),
new SomeSubTypeWP(){SomeProperty = "Three"}
];
// Filter and cast all instances that do have the desired property
// and perform some action on them.
list.OfType<IHasSomeProperty>().Print();
}
}
// The property for which to filter is defined in an interface
public interface IHasSomeProperty
{
string SomeProperty {get; set;}
}
public abstract class SomeBaseType
{}
public sealed class SomeSubType : SomeBaseType
{}
// The Subtypes that have the Filter-Property implement above mentioned interface
public sealed class SomeSubTypeWP : SomeBaseType, IHasSomeProperty
{
public string SomeProperty {get; set;}
}
// We can define extension methods for collections of types having the
// filtered-for property
public static class ListOfTypesWithSomePropertyExtensions
{
public static void Print(this IEnumerable<IHasSomeProperty> list)
{
foreach(var item in list) Console.WriteLine(item.SomeProperty);
}
}
Рабочий пример: https://dotnetfiddle.net/KP3l5c
И это лишь один из многих способов справиться с этим без необходимости реализации IList<T>
или наследования от List<T>
.
Возможно, вам захочется взглянуть на Методы расширения. Вы не можете сделать такое приведение, поскольку List не является ListOfT. Вам нужно реализовать свой собственный метод для создания ListOfT на основе того, что возвращается функцией Where.