List<T> Алгоритмы приведения и соображения производительности

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

Вопрос

У меня есть следующий код

class Program
    {
        static void Main(string[] args)
        {
            List<A> aList = new List<A>();

            var aObj = new A();

            aObj.Go(aList.Cast<IB>());
        }
    }

    class A : IB
    {
        public void Go(IEnumerable<IB> interfaceList)
        {
            foreach (IB ibby in interfaceList)
            {
                Console.WriteLine("Here");
            }
        }
    }

    interface IB
    {
        void Go(IEnumerable<IB> interfaceList);
    }

}

Изначально я пытался передать список, но это не работает.После большой помощи со стороны ТАК Я обнаружил, что передача IEnumerable — единственный способ передать объекты как .ofType(IB).

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

aList.Cast<IB>();

Мне было интересно, знает ли кто-нибудь, как это реализовано алгоритмически (в IL) и каков его временной порядок.

То есть, быстрее ли он, чем цикл foreach, который просто приводит каждый элемент, или именно это он и делает?

РЕДАКТИРОВАТЬ Основной класс должен поддерживать список реальных объектов.Но читателю разрешено прикасаться к ним только через интерфейс.

Это было полезно?

Решение

Вы должны изменить Go к:

public void Go<T>(IEnumerable<T> interfaceList)
    where T : IB
{
    foreach (IB ibby in interfaceList)
    {
        Console.WriteLine("Here");
    }
}

Тогда все будет в порядке, без необходимости звонить Cast.Я подозреваю, что реализация Cast в исходном коде довольно проста, хотя я считаю, что она изменилась между 3.5 и 3.5SP1.Однако, вероятно, потребуется настроить новый конечный автомат и т. д. в обычном режиме блока итератора.Лучше избегать этого, если это возможно.

Несмотря на то, что новый метод является универсальным, вывод типа обычно позаботится об этом за вас, поэтому вам не нужно указывать T явно.

Другие советы

Почему бы просто не объявить список так:

List<IB> aList = new List<IB>();

Есть ли что-то особенное, требующее от вас наличия списка конкретных классов?


Поэтому в этом случае я бы сделал список частью домена.Создайте интерфейс, такой как IIBCollection (например), предоставьте методы, к которым вы хотите, чтобы читатель мог получить доступ.Например:

interface IIBCollection{
    IEnumerable<IB> IBs { get; }
}

// and in your implementation you can do

IEnumerable<IB> IBs { 
    get { 
        foreach(IB ib in innerList) yield return ib; 
}}

Он реализован внутри как CastIterator, который немного медленнее, чем foreach, который приводит каждый элемент.

Метод Cast просто просматривает список и приводит каждый элемент.

Если вы собираетесь использовать список тысячи раз, просто сохраните результат приведения в виде списка.

Если это невозможно (т.вы меняете список каждый раз), рассмотрите возможность работы с List<IB> вместо List<A>.

Разве не для этого нужна ковариация в C#?Я не понимаю, что вы пытаетесь сделать, поэтому не могу комментировать, почему это выполнялось тысячи и тысячи раз.

Было бы довольно просто протестировать два метода (метод расширения Cast и цикл for с приведением).Но учитывая, что Cast является методом расширения класса Enumerable и в целом работает с IEnumerables, я предполагаю, что это именно его реализация.Если вам нужна наибольшая скорость, возможно, лучше всего реализовать собственный метод расширения, который работает конкретно со списком (получает каждый элемент по его индексу), что должно быть немного быстрее, учитывая накладные расходы на итераторы.Тем не менее, оба метода должны занимать время O(n), поэтому разница не должна быть огромной.Тем не менее, это то, что стоит сравнить...

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