#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]'
1[MyClass]’.
to type
'System.Collections.Generic.IEnumerable
исключение. Что я делаю не так?
Ответ №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;
}