C#, objectCollection.OfType<T>() and foreach (T item in objectCollection), IEnumerable to IEnumerable<T>
Asked Answered
C

4

6

To my personal coding style belongs Enumerable.OfType<T>(). I use it everywhere it makes a little bit of sense. Especially IEnumerable<T> allows mighty linqToObject functionallity. I hate the "type-unsafe" looping of ObjectCollections such as the samples below. Now I have some questions about looping across these "UnGenericCollections".

Question 1: If I convert this ArrayList to Enumerable<T> how big is the additional loop in comparison with the simple foreach/if-checks?

var ark = new ArrayList();
ark.Add(new Human());
ark.Add(new Human());
ark.Add(new Animal());

Instead of:

foreach (object passenger in ark)
{
    if (passanger is Human) { }
    if (passanger is Animal) { }
}

I use:

foreach (var human in ark.OfType<Human>())
{
}

foreach (var animal in ark.OfType<Animal>())
{
}

Question 2: During a foreach loop into a different typed variable, which way of casting/converting will be used? Is this a language feature or does that work out of the box?

foreach (Human human in ark) { }

Thanks for enduring my awful English. Best regards,Benjamin

Cowbird answered 19/5, 2011 at 18:50 Comment(1)
Editor note: The awful English has been corrected.Xanthe
H
4

Ans 1:

In your sample - you may actually be iterating over the FULL enumerable twice.

// i use
foreach (var human in ark.OfType<Human>())
{
}

foreach (var animal in ark.OfType<Animal>())
{
}

Ans 2:

It would throw an InvalidCastException exception if there are any non-human in ark.

I would personally prefer ark.OfTypes<T>(), in case I know I only want to deal with Human and Animals but would be ignoring Elves. This way code is much more cleaner and you are dealing with strongly typed object in your foreach loop.

But again in case I do not want to ignore Elves, I would take rather iterate thru full ArrayList and use casts.

Heim answered 19/5, 2011 at 18:57 Comment(2)
ArrayList to generic IEnumerable < T >Cowbird
@ben - now with your edit it makes sense. I'll update the answer.Heim
D
2

You'll find no difference since ArrayList implements IEnumerable. Actually, anything that implements this interface may be used in a foreach statement.

Casting the object instead of setting up a var will build (since it's an explicit cast) but if you have an animal inside the enumeration, you'll end up having to deal with casting exceptions.

foreach (Animal animal in ark) { } // this will blow up an exception if the object is Human
Dneprodzerzhinsk answered 19/5, 2011 at 19:5 Comment(0)
L
2

To prevent the collection from being iterated two times and to keep some kind of sweet syntax, I'd come with a extension method that allows chaining foreachs for specified types. I thought it would be funny:

    static void Main(string[] args)
    {
        var ugly = new ArrayList();
        ugly.Add("strings are evil");
        ugly.Add("yeah");
        ugly.Add(1);
        ugly.Add(3);
        ugly.Add(1234);
        ugly.WithType<int>(x => Console.WriteLine("im a lousy int! " + x))
            .AndType<string>(x => Console.WriteLine("and im dangerous string: " + x))
            .Execute();
        Console.ReadKey();
    }

    static TypedCollectionView WithType<T>(this IEnumerable x, Action<T> action)
    {
        return new TypedCollectionView(x).AndType(action);
    }

    class TypedCollectionView
    {
        private readonly IEnumerable collection;
        private readonly Dictionary<Type, Action<object>> actions = new Dictionary<Type, Action<object>>();

        public TypedCollectionView(IEnumerable collection)
        {
            this.collection = collection;
        }

        public TypedCollectionView AndType<T>(Action<T> action)
        {
            actions.Add(typeof(T), o => action((T)o));
            return this;
        }

        public void Execute()
        {
            foreach (var item in collection)
            {
                var itemType = item.GetType();
                foreach (var action in actions.Where(kv.Key.IsAssignableFrom(itemType)).Select(kv.Value))
                {
                    action(item);
                }
            }
        }
    }

so.. now how to make this suck less? :D

Legato answered 19/5, 2011 at 20:22 Comment(0)
D
0

Enumerable.OfType will go over all elements and do the is T check for You. So every OfType call will iterate over all elements in collection. That must be slower. But it's other thing if it's slower to the point You shouldn't do it in Your program.

I would think If there is a point in putting objects of classes that don't have same parent in one collection. Maybe You could find some abstraction and then in foreach cast all objects to base class and use polymorphic calls.

Dryasdust answered 19/5, 2011 at 19:13 Comment(1)
this arraylist sample isn't my code. generics with covariant are much modern. i will able to have a better handling of the "ObjectCollections" in old-school parts of .NET framework.Cowbird

© 2022 - 2024 — McMap. All rights reserved.