Есть ли способ привести общие списки в списки типов интерфейса / базового класса?

StackOverflow https://stackoverflow.com/questions/188769

  •  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 # на данный момент. Вы можете прочитать об этом в блоге Эрика Липперта .

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top