Список <T>Алгоритмы литья и соображения производительности - PullRequest
1 голос
/ 03 марта 2009

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

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);
    }

}

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

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

aList.Cast<IB>();

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

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

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

Ответы [ 6 ]

10 голосов
/ 03 марта 2009

Вы должны изменить 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 явно.

5 голосов
/ 03 марта 2009

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

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; 
}}
1 голос
/ 03 марта 2009

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

0 голосов
/ 03 марта 2009

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

0 голосов
/ 03 марта 2009

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

0 голосов
/ 03 марта 2009

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

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

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

...