Есть ли способ привести общие списки в списки типов интерфейса / базового класса?
-
06-07-2019 - |
Вопрос
Я пытаюсь показать кому-то использование интерфейсов в безумной ситуации, которую они создали. У них есть несколько несвязанных объектов в списках, и им нужно выполнить операцию с двумя строковыми свойствами в каждом объекте. Я отмечаю, что если они определяют свойства как часть интерфейса, они могут использовать объект интерфейса в качестве типа для параметра метода, который воздействует на него; например:
void PrintProperties(IEnumerable<ISpecialProperties> list)
{
foreach (var item in list)
{
Console.WriteLine("{0} {1}", item.Prop1, item.Prop2);
}
}
Кажется, это все хорошо, но списки, над которыми нужно работать, не должны (и не должны) объявляться с интерфейсом в качестве параметра типа. Однако не похоже, что вы можете привести к другому параметру типа. Например, это терпит неудачу, и я не могу понять, почему:
using System;
using System.Collections.Generic;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
List<Test> myList = new List<Test>();
for (int i = 0; i < 5; i++)
{
myList.Add(new Test());
}
PrintList((IEnumerable<IDoSomething>)myList);
}
static void PrintList(IEnumerable<IDoSomething> list)
{
foreach (IDoSomething item in list)
{
item.DoSomething();
}
}
}
interface IDoSomething
{
void DoSomething();
}
public class Test : IDoSomething
{
public void DoSomething()
{
Console.WriteLine("Test did it!");
}
}
}
Я могу использовать член Enumerable.Cast<T>
для этого, но я искал метод, который мог бы работать и в .NET 2.0. Кажется, это должно быть возможно; что мне не хватает? Р>
Решение
Проблема в методе, а не в том, как он называется .....
void PrintProperties<SP>(IEnumerable<SP> list) where SP: ISpecialProperties
{
foreach (var item in list)
{
Console.WriteLine("{0} {1}", item.Prop1, item.Prop2);
}
}
Другие советы
Причина, по которой он не работает, заключается в том, что дженерики не демонстрируют различия в C # (пока).
Что касается исправления для IEnumerable < T > однако попробуйте следующее:
public static IEnumerable<TBase> SafeConvert<TBase, TDerived>(IEnumerable<TDerived> source)
where TDerived : TBase
{
foreach (TDerived element in source)
{
yield return element; // Implicit conversion to TBase
}
}
РЕДАКТИРОВАТЬ: другой существующий ответ лучше, чем приведенный выше, для этого конкретного случая, но я оставлю это здесь, как правило, полезную вещь, если вам действительно нужно " convert " последовательность.
Вы можете просто использовать foreach
в своих списках. <=> делает встроенный актерский состав. Так что если вы возьмете цикл из функции, вы можете написать что-то вроде
List<Test> myList = new List<Test>();
for (int i = 0; i < 5; i++)
{
myList.Add(new Test());
}
foreach (IDoSomething item in myList)
{
item.DoSomething();
}
То, что вы хотите, называется " ковариация интерфейса " и не поддерживается C # на данный момент. Вы можете прочитать об этом в блоге Эрика Липперта . р>
Это не отвечает на ваш вопрос (или, я полагаю, смысл упражнения), но я бы просто использовал отражение в этом случае, добавив специальные атрибуты к интересующим свойствам.