ienumerable到ienumerable<;t>;没有损失

本文关键字:ienumerable 损失 gt lt | 更新日期: 2023-09-27 18:26:47

我有一个只接受ienumerable<t>参数的方法,但我的局部值是一个简单的整数。

我无法改变这些条件。

现在我的问题是如何在不丢失项的对象引用的情况下获得对ienumerable<t>的枚举?我不想复制数据或其他什么。

ienumerable.Cast<object>()能胜任这项工作吗?我不想要新的项目参考,我只想尽快从ienumerable获得ienumerable<t>

ienumerable到ienumerable<;t>;没有损失

Linq语句只是一组规则,当有人从Linq语句生成的IEnumerable中请求项时执行这些规则。foreach就是这样一个消费者,但IEnumerable的任何其他消费者在获得物品之前都会启动相同的规则,所以你总是可以把foreach想象成一个类比。

foreach(object item in myEnumerable)
{
    ((MyClass)item).DoSomething();
}

在概念上与相同

foreach(MyClass item in myEnumerable.Cast<MyClass>())
{
    item.DoSomething();
}

所以没有,没有复制。但是,如果您尝试强制转换错误类型的项,它应该会抛出错误。如果你想要更多这样的东西:

foreach(object item in myEnumerable)
{
    MyClass myItem = item as MyClass;
    if(myItem != null)
        myItem.DoSomething();
}

那么您将使用OfType():

foreach(MyClass item in myEnumerable.OfType<MyClass>())
{
    item.DoSomething();
}

请注意,如果您对可枚举对象调用ToList或ToArray,它将由您自己进行求值(发生的情况与调用foreach时相同),并将其保存到新集合中。如果您的原始IEnumerable包含值类型(如int、float、structs等),而不是引用类型(如您自己的任何类),那么您实际上会在新数组中拥有项的副本。因此,只有当您真正想尽早评估linq查询时,才可以使用这些方法。

有两种linq方法可用于此目的:Enumerable.Cast<T>,如果源中的任何项无法强制转换为T,它将抛出异常;Enumerable.OfType<T>,它将跳过无法强制转换到T而不抛出的元素,但速度稍慢,因为它检查每个元素的类型(Enumerable.Cast<T>只是将其强制转换为T)。

作为一个补充答案,除了Cast<T>OfType<T>扩展方法外,您还可以手动实现它(对于较旧的框架或像unity3d这样的嵌入式框架更有用)

using System;
using System.Collections;
using System.Collections.Generic;
namespace ConsoleApplication1
{
    public class WrapperEnumerable<T> : IEnumerable<T>
    {
        private readonly IEnumerable _enumerable;
        public IEnumerator<T> GetEnumerator()
        {
            return new WrapperEnumerator<T>(_enumerable.GetEnumerator());
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return _enumerable.GetEnumerator();
        }
        public WrapperEnumerable(IEnumerable enumerable)
        {
            _enumerable = enumerable;
        } 
    }
    public class WrapperEnumerator<T>:IEnumerator<T>
    {
        private readonly IEnumerator _enumerator;
        public T Current
        {
            get { return (T)Convert.ChangeType(_enumerator.Current,typeof(T)); }
        }
        public void Dispose()
        {           
        }
        object IEnumerator.Current
        {
            get { return _enumerator.Current; }
        }
        public bool MoveNext()
        {
            return _enumerator.MoveNext();
        }
        public void Reset()
        {
            _enumerator.Reset();
        }
        public WrapperEnumerator(IEnumerator enumerator)
        {
            _enumerator = enumerator;
        }
    }
}

使用情况类似

    var list = new ArrayList {1, 2, 3};
    foreach(var n in new WrapperEnumerable<int>(list))
        Console.WriteLine(n);
    //using Cast method extension
    foreach (var n in list.Cast<int>())
        Console.WriteLine(n);
    //using OfType method extension
    foreach(var n in list.OfType<int>())
        Console.WriteLine(n);