C#中的objectCollection.OfType<T>()和foreach(T item in objectCollection),以及IEnumerable到IEnumerable<T>的转换。

6

我的个人编程风格包括 Enumerable.OfType<T>()。只要有一点点意义,我就无处不用它。特别是 IEnumerable<T> 提供了强大的 LinqToObject 功能。我讨厌 ObjectCollections 的“类型不安全”循环,比如下面的示例。现在我有一些关于遍历这些“非泛型集合”的问题。

问题1: 如果我将这个 ArrayList 转换为 Enumerable<T>,与简单的 foreach/if-checks 相比,额外循环的开销有多大?

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

改为:

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

我使用:

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

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

问题2:在使用foreach循环时,如何将不同类型的变量进行转换?这是语言特性还是可以直接使用的功能?

foreach (Human human in ark) { }

感谢您忍受我可怕的英语。最好的问候,本杰明。


1
编辑注:糟糕的英语已经被纠正。 - user47589
4个回答

4

答案 1:

在你的示例中 - 你实际上可能会两次迭代完整的可枚举对象。

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

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

答案2:
如果方舟中有任何非人类物种,它将抛出InvalidCastException异常。
个人建议使用 ark.OfTypes<T>(),如果我知道只想处理HumanAnimals但是忽略Elves。这样代码更加简洁,在foreach循环中你处理的是强类型对象。
但是如果我不想忽略Elves,我会遍历整个ArrayList并使用强制转换。

ArrayList 转换为通用的 IEnumerable <T>。 - benwasd
1
@ben - 现在你的编辑让它有意义了。我会更新答案。 - YetAnotherUser

2
为了避免集合被迭代两次,并保持一些优美的语法,我想出了一个扩展方法,可以为指定类型链接foreach。我认为这很有趣:
    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);
                }
            }
        }
    }

所以..现在如何让这个更好?:D

2

由于ArrayList实现了IEnumerable,因此您不会发现任何区别。实际上,任何实现此接口的内容都可以在foreach语句中使用。

如果设置var而不是转换对象,则构建将失败(因为它是显式转换)。但是,如果枚举中有动物,则最终必须处理转换异常。

foreach (Animal animal in ark) { } // this will blow up an exception if the object is Human

0

Enumerable.OfType 会遍历所有元素并进行 is T 检查。因此,每次调用 OfType 都会遍历集合中的所有元素。这可能会更慢。但如果在您的程序中不应该这样做,则是另一回事。

我认为,如果将没有相同父类的类的对象放入一个集合中有意义的话,那么您可以找到一些抽象,并在 foreach 中将所有对象转换为基类,并使用多态调用。


这个ArrayList示例不是我的代码。具有协变性的泛型更加现代化。在.NET框架的老派部分中,我将能够更好地处理“ObjectCollections”。 - benwasd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接