Как мне преобразовать список в список ?

#c# #generics #interface

#c# #общие сведения #интерфейс

Вопрос:

У меня есть интерфейс, определенный как:

 public interface MyInterface {
     object foo { get; set; };
}
  

и класс, который реализует этот интерфейс:

 public class MyClass : MyInterface {
     object foo { get; set; }
}
  

Затем я создаю функцию, которая возвращает ICollection следующим образом:

 public ICollection<MyClass> Classes() {
    List<MyClass> value;

    List<MyInterface> list = new List<MyInterface>(
        new MyInterface[] {
            new MyClass {
                ID = 1
            },
            new MyClass {
                ID = 1
            },
            new MyClass {
                ID = 1
            }
        });

    value = new List<MyClass>((IEnumerable<MyClass>) list);

    return value;
}
  

Это скомпилировалось бы, но выдало бы

Невозможно привести объект типа ‘System.Коллекции.Generic.List 1[MyInterface]'
to type
'System.Collections.Generic.IEnumerable
1[MyClass]’.

исключение. Что я делаю не так?

Ответ №1:

List<MyInterface> Не может быть преобразован в List<MyClass> в целом, потому что первый список может содержать объекты, которые реализуют, MyInterface но которые на самом деле не являются объектами типа MyClass .

Однако, поскольку в вашем случае вы знаете, как вы создали список, и можете быть уверены, что он содержит только MyClass объекты, вы можете сделать это с помощью Linq:

 return list.ConvertAll(o => (MyClass)o);
  

Комментарии:

1. Это будет работать в .Net 2.0 , а не только в LINQ как системе. Коллекции. Generic.List<> напрямую поддерживает ConvertAll<T>().

2. Спасибо, @Ritch. Я думал, что это ConvertAll метод Linq, но я не удосужился проверить.

3. На самом деле, если вы уверены, что ваш список содержит только MyClass объекты, вам следует использовать Enumerable.Cast<TResult> метод, и вы получите, InvalidCastException если элемент в последовательности не может быть приведен к MyClass .

4. @PaoloMoretti: Спасибо — ваш метод сэкономил много усилий. Я использую компоненты telerik wpf, и в RadDataFilter возвращаемое значение представляет собой список интерфейсов, и мне приходится выполнять длинный предварительный поиск с приведением типов и переключателями сопоставления строк и if else. Теперь я могу использовать linq. 🙂

Ответ №2:

Но a List<MyInterface> решительно не является List<MyClass> .

Подумайте:

 interface IAnimal { }

class Cat : IAnimal { }
class Dog : IAnimal { }

var list = new List<IAnimal> { new Cat(), new Dog() };
  

Затем

 var cats = (List<Cat>)list;
  

Абсурд!

Также,

 var cats = list.Cast<Cat>();
  

Абсурд!

Далее

 var cats = list.ConvertAll(x => (Cat)x);
  

Абсурд!

Вместо этого вы могли бы сказать

 var cats = list.OfType<Cat>();
  

Комментарии:

1. Я понимаю. Я был сбит с толку, потому что вы можете привести интерфейс к его конкретному классу. Спасибо.

Ответ №3:

Вы могли бы использовать Cast<> метод расширения:

 return list.Cast<MyClass>();
  

Комментарии:

1. Можете ли вы добавить некоторые детали?

Ответ №4:

Я нахожу Automapper очень полезным для преобразования интерфейсов в конкретные классы.

Ответ №5:

Это возможно, и именно в этом преимущество дженериков! Вот простой пример:

 public interface ICountable
{
     int Count { get; set; }
}

public class PopularName : ICountable
{
    public string Name { get; set; }
    public int Count { get; set; }
}

public class PopularSize : ICountable
{
     public int Size { get; set; }
     public int Count { get; set; }
}
  

И теперь вам нужно объявить ваш метод (или ваш класс) универсальным следующим образом:

 public bool HasAnyValue<T>(List<T> countableModel) where T : ICountable
{
    return countableModel.Count > 0;
}