C#中如何从 IEnumerable<T> 中取值?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#中如何从 IEnumerable<T> 中取值?相关的知识,希望对你有一定的参考价值。

实际项目开发中遇到的问题,因为核心已经写好了的,现在是基于核心提供的各种东西做扩展的插件,所以核心里的那些东西,作为一个初级程序员我也不明白
流程大概是,软件会接收到机器发送来的消息,这个消息的各种格式内容啥的被封装在Message里,我需要用到Message的一个属性Arguments,这个属性在定义的地方能看到是个 IEnumerable<T> 类型的。
然后我在Debug的时候,获得了这个Message的值,找到了Arguments里面包含了5个元素,我想要的就是第五个元素的值,这第五个元素里面好像有个String类型的值,我需要从这个值里面截取出来两个关于坐标的数字X和Y。
总之这个Arguments似乎并不像我理解的那么简单,像个list一样的里面存着 101,102,103这些东西,然后我只要 list[1] 就能取第二个元素了。
所以这个 IEnumerable<T> 到底要怎么操作?

软糖来说一下。

这个IEnumerable<T>接口表示可枚举的对象。

只要某个对象能一个个的数过去就符合这个接口,

它提供一个枚举器,通过GetEnumerator()获得

然后调用枚举器.MoveNext()可以访问下一个对象。

所以ElementAt(int)方法就等价于下面这个。

static T ElementAt<T>(IEnumerable<T> items, int index)
    using(IEnumerator<T> iter = items.GetEnumerator())
    
        for (int i = 0; i <= index; i++, iter.MoveNext()) ;
        return iter.Current;
    

所以你取第5号元素,实际上调用了4次移动"游标"或"指针"类似的东西以指向该元素。

参考技术A foreach(T具体类型 item in Arguments)

  。。。

追问

foreach的话不是把里面的全部元素都遍历出来了吗?
我好像找到了个方法就是Arguments.ElementAt(4),就能得到第5个元素了
然后再把这个元素里面我需要的那个属性比如 Content找到,就可对它进行字符串分割了
是吧?

追答

对的。只要编译通过就可以

本回答被提问者和网友采纳
参考技术B 先看它的数据格式,怎么组合的,找到规律,做数据转换,再截取。

在 C# 中将多个 IEnumerable<T> 提要混合为一个

【中文标题】在 C# 中将多个 IEnumerable<T> 提要混合为一个【英文标题】:Mix several IEnumerable<T> feeds into one in C# 【发布时间】:2014-10-16 07:35:49 【问题描述】:

我经常需要在有IEnumerable&lt;T&gt; AIEnumerable&lt;T&gt; B 的地方执行操作,并且我想创建IEnumerable&lt;T&gt; C,其中 C 将从 A 获取 2T,然后从 B 获取 1T,然后再次从 A 获取 2T,然后从 B 获取 1T等等 - AABAABAAB...

C# 中是否有一些语言结构,也许是一些 LINQ 表达式可以轻松完成。

现在我编写了一个小助手类,使我能够做到这一点:

var C = EnumerableMixer(new int[]2, 1, A, B):

    public class EnumerableMixer<T> : IEnumerable<T>
    
        int[] Quantity;
        IEnumerable<T>[] Args;

        public EnumerableMixer(int[] quantity, params IEnumerable<T>[] args)
        
            this.Quantity = quantity;
            this.Args = args;
            if (quantity.Length != args.Length)
                throw new NotImplementedException("Quantity must have same length as number of args!");
        

        IEnumerable<T> Mix
        
            get
            
                var available = new List<int>(Quantity.Length);
                var enumerators = new List<IEnumerator<T>>(Quantity.Length);
                for (int i = 0; i < Quantity.Length; ++i)
                
                    available.Add(i);
                    enumerators.Add(Args[i].GetEnumerator());
                
                while (available.Count > 0)
                
                    for (int i = 0; i < available.Count; ++i)
                    
                        var id = available[i];
                        for (int j = 0; j < Quantity[id]; ++j)
                        
                            if (enumerators[id].MoveNext())
                                yield return enumerators[id].Current;
                            else
                            
                                available.RemoveAt(i);
                                i--;
                                break;
                            
                        
                    
                
            
        

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        
            return Mix.GetEnumerator();
        

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        
            return this.GetEnumerator();
        

        #endregion
    

【问题讨论】:

如果一个枚举在另一个枚举之前完全枚举,会发生什么?例如:A= 1,4,8,9,B=0,1,2,3,4,5,6。应该是 1,4,0​​,8,9,1,2,3,4,5,6 还是 1,4,0​​,8,9,1 甚至是 1,4,0​​,8 ,9 还是抛出异常? 如果一个枚举不包含混合数量的多个元素倍数会发生什么?例如:A= 1,4,8,B=0,1,2,3,4,5,6。应该是 1,4,0​​,8,1,2,3,4,5,6 还是 1,4,0​​,8 还是抛出异常? 你知道Reactive Extensions吗?您谈论混合各种“提要”的方式对我来说感觉如此 Rx。在 Rx 中,有各种运算符可以将 IObservable 序列无缝地组合在一起,这对您来说真的很有用。我个人会认真考虑你的情况。 @CédricBignon 你的问题很好,我认为对于一般解决方案,我们必须有几个选项来处理枚举结束的枚举。 @julealgon 我认为 rx 在这里没有太大帮助 【参考方案1】:

版本 1

这是基于 adhie 的解决方案。它会考虑该比率并在退出该方法之前完全完成这两个序列。 flag 用于跟踪最后一个循环是否产生了任何东西。当flag 为假时,两个序列都被耗尽。

public IEnumerable<T> Mix<T>(IEnumerable<T> sequenceA, IEnumerable<T> sequenceB, int ratioA, int ratioB)

    var etorA = sequenceA.GetEnumerator();
    var etorB = sequenceB.GetEnumerator();
    bool flag = true;

    while(flag)
    
        flag = false;

        for(int i = 0; i < ratioA && (flag |= etorA.MoveNext()); i++)
            yield return etorA.Current;

        for(int i = 0; i < ratioB && (flag |= etorB.MoveNext()); i++)
            yield return etorB.Current;
    

版本 2

与版本 1 几乎相同,但采用可变数量的序列。

public IEnumerable<T> Mix<T>(params KeyValuePair<IEnumerable<T>, uint>[] quantifiedSequences)

    var sequences = quantifiedSequences.Select(x => new  Etor = x.Key.GetEnumerator(), Quantity = x.Value );
    bool flag = true;

    while(flag)
    
        foreach (var sequence in sequences)
            for(int i = 0; i < sequence.Quantity && (flag |= sequence.Etor.MoveNext()); i++)
                yield return sequence.Etor.Current;
    

【讨论】:

没有必要使用flag |= etorA.MoveNext()。你可以写etorA.MoveNext() 您需要跟踪最后一个循环是否返回任何内容,否则我们无法确定两个序列何时完成以退出 while 循环。 当 flag 初始化为 true 时,flag |= x 将始终为 true。修复了次要标志问题 = 清晰优雅的解决方案。【参考方案2】:

稍长一点的解决方案是:

    public IEnumerable<TValue> Mix<TValue>(IEnumerable<TValue> a, IEnumerable<TValue> b)
    
        var aEnumerator = a.GetEnumerator();
        var bEnumerator = b.GetEnumerator();

        while (true)
        
            if (!aEnumerator.MoveNext())
            
                yield break;
            

            yield return aEnumerator.Current;

            if (!aEnumerator.MoveNext())
            
                yield break;
            

            yield return aEnumerator.Current;

            if (!bEnumerator.MoveNext())
            
                yield break;
            

            yield return bEnumerator.Current;
        
    

此解决方案不会创建不必要的对象。如果你对 yield 关键字不熟悉:http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx

【讨论】:

不知道为什么这被否决(除非我错过了什么),这是我在看到 OP 实现了一个更复杂的版本之前要建议的。 我没有回答你的答案,但它没有选择将 2-1 比率设置为函数参数,这就是它更简单的原因。当其中一个完成时它也会中断,而我的将完成所有这些,不确定哪个更好。请注意,我的解决方案支持超过 2 个可枚举的组合。 @watbywbarif 那么你应该在你的要求中解释这一点,而不是仅仅说你想要有一个枚举,其中一个为每个对象迭代 2 个对象。 对不起,但这只是一个例子,如果你看问题,它会清楚地说明几个,而且从我的助手类中你可以看到我启用了几个枚举和每个自定义的样本数量。跨度> 我也没有说这个解决方案不好,它适用于我的情况,但最后当你比较两个解决方案时,如果两者都正常工作,你会采取一些额外的考虑喜欢哪个会表现得更好,哪个更通用,或者......我同意这里不需要像我在我的解决方案中那样使用 OP,因为静态函数会表现得更好,代码更短更简单,但这很容易改变。【参考方案3】:

这是我的 Linq 解决方案:

为每个元素分配一个索引,然后以正确的顺序获取元素。

// for example. You can use with any type
IEnumerable<Int32> listA = new List<Int32>()  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 ;
IEnumerable<Int32> listB = new List<Int32>()  101, 102, 103, 104, 105, 106, 107, 108, 109, 110;

// you want 2 of list A and 1 of list B. You can change the values
Int16[] quantity = new Int16[]  2, 1 ;

IEnumerable<Int32> listC = listA.Select((e, i) => new   element = e, index = (i / (quantity[0]) * (quantity[0] + quantity[1]) + (i%quantity[0])))
                                .Concat(listB.Select((e, i) => new   element = e, index = (i / (quantity[1]) * (quantity[0] + quantity[1]) + (i%quantity[1]) + quantity[0])))
                                .OrderBy(e => e.index)
                                .Select(e => e.element);

结果是IEnumerable&lt;Int32&gt;:1、2、101、3、4、102、5、6、103、7、8、104 ...

困难的是确定索引。它适用于您想要的(2,1、3,2、...)

对于第一个列表:

index = i/(quantity[0]) * (quantity[0]+quantity[1]) + (i%quantity[0]) + 0

对于第二个列表:

index = i/(quantity[1]) * (quantity[0]+quantity[1]) + (i%quantity[1]) + quantity[0]

【讨论】:

很酷的索引转换!就可用性而言,混合两个以上的可枚举项是不够通用的。无限枚举的问题。我没有dw。

以上是关于C#中如何从 IEnumerable<T> 中取值?的主要内容,如果未能解决你的问题,请参考以下文章

IEnumerable<T> 使用 c# 在函数内访问 T 的属性

如何在 C# 中将 IEnumerable<T> 转换为 List<T>?

在 C# 中将多个 IEnumerable<T> 提要混合为一个

C# 常用接口学习 IEnumerable<T;

c# IEnumerable<T>

C#定义一个泛型集合类。要求:实现Ienumerable<T>接口,T是值类型,并T取2个类型分别测试。