Невозможно привести List<T> к классу, производному от List<T>

Я получил класс «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 упомянул методы расширения как еще один способ «улучшить» конкретный тип списка. Я использовал их в некоторых местах, где у меня была иерархия классов, где каждый потомок должен был быть «перечислен». В этом случае статический класс, содержащий расширения, был хорошим способом собрать все функции моего зоопарка классов, связанные со списками, в одном месте.

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

Ralf 06.05.2024 12:37

«Я получил класс «ListOfT» от List<T>» — это вообще не рекомендуется. И вам это тоже не нужно. С помощью этого простого фильтра вы не сможете получить ничего, кроме T-Generic (List<T>, IList<T> или IEnumerable<T>... или T).

Fildor 06.05.2024 15:40

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

Fildor 06.05.2024 15:48

«Я не пошел по пути «имеет» вместо «есть» — я никогда не говорил, что вам следует это делать. Я сказал, расскажите нам, что вы пытаетесь сделать, потому что я думаю, что вы совершенно сбились с пути.

Fildor 07.05.2024 08:46

^^ dotnetfiddle.net/2t8WCL (особенно обратите внимание на последний пример)

Fildor 07.05.2024 09:22

Не вкладывайте ответ в вопрос. Вы не можете ответить на свой вопрос ниже.

Charlieface 07.05.2024 13:27

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

AstaDev 07.05.2024 13:41

Может ли кто-нибудь объяснить, почему мой вопрос получил два отрицательных голоса?

AstaDev 07.05.2024 13:46

Вы можете ответить на свой вопрос. Просто перейдите в поле ниже, введите свой ответ и нажмите «Опубликовать свой ответ». Вопрос должен быть просто вопросом, а не тем и другим.

Charlieface 07.05.2024 13:47
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
9
116
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

В 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>

Tim Schmelter 06.05.2024 12:43

Спасибо @SomeBody, введение дополнительного конструктора (и общедоступного значения по умолчанию, позволяющего создавать экземпляр ListOfT) помогло.

AstaDev 06.05.2024 17:28

Я не понимаю отрицательных голосов по этому ответу. Единственная причина, о которой я могу думать, это то, что в этом примере используется public class MyList<T> : List<T> вместо public class MyList : List<T> (как я это сделал в решении, опубликованном в моем отредактированном ответе). Было бы неплохо, если бы отрицательный голос прокомментировал, почему они считают этот ответ неправильным.

AstaDev 07.05.2024 13:11

вы можете использовать «Имеет отношение», а не «Есть».

   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>? Ваш класс по-прежнему является списком

Tim Schmelter 06.05.2024 16:02

это часть переоборудования, позволяющая использовать список<T> или строку в этом примере.

Oren Tamam 07.05.2024 09:49

Список, возвращаемый 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>.

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