List Casting algorithms and performance conside

2019-07-30 08:46发布

问题:

I have the following code

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

}

I originally tried passing a List but that doesn't work. After a lot of help from SO I found that passing IEnumerable is the only way to get the objects across as .ofType(IB).

Unfortunately for me, in my code the following line will be executed thousands of times:

aList.Cast<IB>();

I was wondering if anyone knew how it was implemented algorithmically (in IL) and what its time order is.

That is to say, is it faster than a foreach loop that just casts each item, or is that exactly what it does?

EDIT The main class needs to maintain a list of the actual objects. But the reader is only allowed to touch them through the interface.

回答1:

You should change Go to:

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

Then you'll be fine with no need to call Cast. I suspect the source code implementation of Cast is pretty simple, although I believe it changed between 3.5 and 3.5SP1. However, it probably needs to set up a new state machine etc in the normal iterator block fashion. Better to avoid it if possible.

Even though the new method is generic, type inference should usually take care of it for you so you don't need to specify T explicitly.



回答2:

Why not just declare the list like:

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

Is there something in particular that requires you to have a list of the concrete classes?


So in that case I'd make the list part of the domain. Have an interface like IIBCollection (for example), expose the methods on that you want the reader to be able to access. For example:

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


回答3:

It's implemented internally as a CastIterator which is slightly slower than a foreach that casts each item.



回答4:

The Cast method will just loop through the list and cast each item.

If you are going to use the list thousands of time, just store the result of the casting as a list.

If that is not possible (i.e. you change the list each time), consider working with a List<IB> instead of a List<A>.



回答5:

Isn't this what covariance in C# is for? I don't see what you're trying to do, so I can't comment on why it's exeuted thousands and thousands of times.



回答6:

It would be a fairly simple matter just to benchmark the two methods (Cast extension method and a for loop with a cast). But given that Cast is an extension method of the Enumerable class and deals generically with IEnumerables, I'd imagine this is precisely it's implementation. If you want the greatest speed, it may be best to implement your own extension method that works specifically on List (gets each element by its index), which should be slightly faster given the overhead of iterators. Still, both methods should take O(n) time, so the difference ought not be huge. It's something worth benchmarking, nonetheless...