Как привести «список классов» к «списку его интерфейса»? - PullRequest
2 голосов
/ 09 мая 2019

Мне нужно привести список классов к собственному списку интерфейсов.

Итак, у меня есть интерфейс Demo_Interface и два класса на основе Demo_Interface, теперь я создаю список классов, таких как List<Test_Class1>

И у меня есть функция с параметром List<Demo_Interface>.

Вот интерфейс:

       interface Demo_Interface
        {
            int test_int { get; set; }
        }

Вот весь код:

using System;
using System.Collections.Generic;

namespace ConsoleApp3
{
    class Program
    {
        ///// Main Interface
        interface Demo_Interface
        {
            int test_int { get; set; }
        }

        //// Class 1 Based On Demo_Interface
        class Test_Class1 : Demo_Interface
        {
            public int test_int { get; set; }
            public string test_string { get; set; }

        }
        ///// Class 2 Based On Demo_Interface
        class Test_Class2 : Demo_Interface
        {
            public int test_int { get; set; }
            public string test_string { get; set; }

        }

        //// And Main Class
        class Main_Class
        {
            public List<Test_Class1> class_list_1 { get; set; }

            public List<Test_Class2> class_list_2 { get; set; }

            public Main_Class()
            {
                class_list_1 = new List<Test_Class1>() { };
                class_list_2 = new List<Test_Class2>() { };
            }
        }

        //// Console Main
        static void Main(string[] args)
        {
            var new_main_class = new Main_Class();

            Output_Class(new_main_class.class_list_1); ///// ==> ERROR

            Console.ReadKey();
        }

        //// Simple Function for do something with interface
        static void Output_Class(List<Demo_Interface> inter_input)
        {
            for (int i = 0; i < inter_input.Count; i++)
            {
                Console.WriteLine("{0} - {1}",i, inter_input[i].test_int);
            }
        }
    }
}

Как я могу разыграть List<Test_Class1> на List<Demo_Interface>, когда Test_Class1 использует Demo_Interface ?

Ответы [ 3 ]

2 голосов
/ 09 мая 2019

Если вам нужно только перечислить через List<Demo_Interface>, как показано в примере, вам не нужно выполнять явное приведение. List<T> реализует IEnumerable<T>, что является ковариантным универсальным типом.

Ковариация для коллекций обеспечивает неявное преобразование коллекции более производного типа в коллекцию менее производного типа

В вашем случае, List<Test_Class1> реализует IEnumerable<Test_Class1>, но, поскольку Test_Class1 реализует Demo_Interface, вы можете воспользоваться преимуществами универсальных дисперсий и написать, например, что-то вроде этого:

IEnumerable<Test_Class1> col = new List<Test_Class1>();
IEnumerable<Demo_Interface> colImplicit = col;

Это в основном означает, что ваш метод Output_Class может принимать аргумент IEnumerable<Demo_Interface>, и вы сможете передавать оба списка без явного приведения их с помощью Cast<T> или создания новой коллекции с помощью ToList<T>.

private void Output_Class(IEnumerable<Demo_Interface> inter_input)
{
    // do your thing
}

// Method invocation
Output_Class(new_main_class.class_list_1);
2 голосов
/ 09 мая 2019

Вы можете попробовать

List<Test_Class1> testDemo = new List<Test_Class1>(); //list of Test_Class1 instances
List<Demo_Interface> result = testDemo.ToList<Demo_Interface>();

Это безопасно, потому что мы не напрямую приводим testDemo к его интерфейсу. Мы сохраняем testDemo как есть и создаем result, который является списком Demo_Interface

1 голос
/ 09 мая 2019

Вы не можете разыграть List<ClassThatImplementsInterface> как List<IInterfaceItImplements>.

Если бы вы могли, и вы сделали это:

var classList = new List<ClassThatImplementsInterface>();
var interfaceList = (List<IInterfaceItImplements>)classList;

... тогда вы сможете сделатьthis:

interfaceList.Add(new SomeOtherClassThatImplementsTheInterface);

Но приведение списка не создает новый список.В приведенном выше примере нет двух списков.Есть две переменные со ссылками на один и тот же список.Если бы вы могли приводить, как показано выше, вы могли бы определить список одного типа и добавить к нему совершенно другой тип.Компилятор предотвращает это.

Вы можете

  • создать новый List<IDemoInterface> и добавить к нему элементы.(Или массив, IEnumerable и т. Д.)
  • Оставьте список как есть и просто разыгрывайте отдельные элементы, когда / если вам нужно.В большинстве случаев нам не нужно приводить что-либо как интерфейс, который он реализует.

Если нам нужно привести целую коллекцию как другой тип, это скорее всего потому, что мы передаем это в качестве аргумента.,

Это действительно хорошая причина , а не , чтобы определять аргумент метода как тип коллекции, такой как List<T>, который может быть изменен, если только мы не намерены изменять коллекцию.

Это одна из причин, по которой мы передаем менее специфичные типы коллекций, например IEnumerable<T>.

Предположим, что аргумент метода выглядит следующим образом:

void MethodINeedToPassTheArgumentTo(IEnumerable<IDemoInterface> items)

Теперь мы можем взятьнаши List<TestClass> и сделайте это:

MethodINeedToPassTheArgumentTo(testClassList.Cast<IDemoInterface>);

Мы не создаем новую коллекцию.Мы передаем ссылку, которая позволяет другому методу просматривать элементы в списке, каждый в отдельности , приведенный к IDemoInterface.Для практических целей выглядит для другого метода как коллекция IDemoInterface, и это нормально, потому что другой элемент не может изменить коллекцию.Он не может пытаться добавить другие типы в List<TestClass>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...