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

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

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

}

Первоначально я пытался передать список, но это .aspx" rel="nofollow noreferrer">не работает. После большой помощи от SO я обнаружил, что передача IEnumerable - это единственный способ передать объекты как .ofType(IB).

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

aList.Cast<IB>();

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

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

EDIT Основной класс должен поддерживать список реальных объектов. Но читателю разрешается прикасаться к ним только через интерфейс.


person DevinB    schedule 03.03.2009    source источник


Ответы (6)


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

person Jon Skeet    schedule 03.03.2009
comment
Это может полностью изменить вопрос, но что, если A должен иметь внутренний список IB, который точно отражает основной список A? - person DevinB; 03.03.2009
comment
@devinb: я не совсем понимаю, что ты имеешь в виду. Вы имеете в виду как переменную-член? Вам придется сделать универсальный; тогда вы можете использовать тот же трюк. Если этой информации недостаточно, я предлагаю вам задать новый вопрос для этого бита с более подробной информацией. - person Jon Skeet; 03.03.2009

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

person Jeff Moser    schedule 03.03.2009

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

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

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

person Guffa    schedule 03.03.2009

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

person Dave Van den Eynde    schedule 03.03.2009

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

person Noldorin    schedule 03.03.2009

person    schedule
comment
Да, потому что основной класс должен поддерживать список реальных объектов, а классу чтения разрешен доступ только к интерфейсу. - person DevinB; 03.03.2009
comment
Извините, если я немного медленно. Но вы говорите, что основной класс (в моем случае EnvironmentClass) должен реализовывать IIBCollection. Я просто не уверен, к чему должен применяться интерфейс IIBCollection. - person DevinB; 03.03.2009
comment
У вас будет что-то вроде Environment, EnvironmentCollection, IEnvironment и IEnvironmentCollection. Метод Go() будет жить в коллекции. - person jonnii; 03.03.2009