C# 在foreach循环中访问枚举器?
我有一个List类,我想重写C# 在foreach循环中访问枚举器?,c#,foreach,ienumerator,C#,Foreach,Ienumerator,我有一个List类,我想重写GetEnumerator()以返回我自己的枚举器类。此枚举器类将具有两个附加属性,这些属性将在使用枚举器时更新 为简单起见(这不是确切的业务案例),假设这些属性是CurrentIndex和RunningTotal 我可以在foreach循环中手动管理这些属性,但我更愿意封装此功能以供重用,而枚举器似乎是正确的位置 问题:foreach隐藏了所有枚举器业务,那么是否有办法在foreach语句中访问当前枚举器,以便检索我的属性?或者我必须使用foreach,使用一个讨厌
GetEnumerator()
以返回我自己的枚举器类。此枚举器类将具有两个附加属性,这些属性将在使用枚举器时更新
为简单起见(这不是确切的业务案例),假设这些属性是CurrentIndex
和RunningTotal
我可以在foreach循环中手动管理这些属性,但我更愿意封装此功能以供重用,而枚举器似乎是正确的位置
问题:foreach隐藏了所有枚举器业务,那么是否有办法在foreach语句中访问当前枚举器,以便检索我的属性?或者我必须使用foreach,使用一个讨厌的旧while循环,自己操作枚举数?使用
foreach
你确实无法获得枚举数-但是,你可以让枚举数返回(yield
)一个包含该数据的元组;事实上,你可以用LINQ来帮你做
(我无法使用LINQ干净地获取索引-但是可以通过聚合获取总值和当前值;所以这里是元组方法)
使用系统集合;
使用System.Collections.Generic;
使用制度;
类MyTuple
{
公共int值{get;private set;}
公共int索引{get;private set;}
public int RunningTotal{get;private set;}
公共MyTuple(int值、int索引、int runningTotal)
{
值=值;索引=索引;RunningTotal=RunningTotal;
}
静态IEnumerable方法(IEnumerable数据)
{
整数指数=0,总计=0;
foreach(数据中的int值)
{
返回新的MyTuple(值,索引+++,
总数=总数+价值);
}
}
静态void Main()
{
int[]数据={1,2,3};
foreach(SomeMethod(数据)中的变量元组)
{
Console.WriteLine(“{0}:{1};{2}”),tuple.Index,
tuple.Value,tuple.RunningTotal);
}
}
}
严格地说,我想说,如果你想完全按照你说的去做,那么是的,你需要调用GetEnumerator并用while循环自己控制枚举器
在不太了解业务需求的情况下,您可能能够利用迭代器函数,例如:
public static IEnumerable<decimal> IgnoreSmallValues(List<decimal> list)
{
decimal runningTotal = 0M;
foreach (decimal value in list)
{
// if the value is less than 1% of the running total, then ignore it
if (runningTotal == 0M || value >= 0.01M * runningTotal)
{
runningTotal += value;
yield return value;
}
}
}
List<decimal> payments = new List<decimal>() {
123.45M,
.01M,
345.67M,
234.56M,
1.23M,
456.78M
};
FishingHook hook = new FishingHook();
decimal min = 0;
hook.Criteria = x => x > min; // exclude any values that are less than/equal to the defined minimum
foreach (decimal value in FishingHookIteration(payments, hook))
{
// update the minimum
if (value > min) min = value;
Console.WriteLine("Index: {0}, Value: {1}, Running Total: {2}", hook.Index, value, hook.RunningTotal);
}
// Resultint output is:
//Index: 0, Value: 123.45, Running Total: 123.45
//Index: 1, Value: 345.67, Running Total: 469.12
//Index: 2, Value: 456.78, Running Total: 925.90
// we've skipped the values .01, 234.56, and 1.23
var ZippedSequence = ValueList.Zip(Indexes, (value, index) => new {value, index}).Zip(RunningTotals, (temp, total) => new {temp.value, temp.index, total});
public静态IEnumerable IgnoreSmallValues(列表)
{
十进制运行总计=0M;
foreach(列表中的十进制值)
{
//如果该值小于运行总数的1%,则忽略它
如果(运行总计==0M | |值>=0.01M*运行总计)
{
runningTotal+=值;
收益回报值;
}
}
}
然后你可以这样做:
List<decimal> payments = new List<decimal>() {
123.45M,
234.56M,
.01M,
345.67M,
1.23M,
456.78M
};
foreach (decimal largePayment in IgnoreSmallValues(payments))
{
// handle the large payments so that I can divert all the small payments to my own bank account. Mwahaha!
}
列表付款=新列表(){
123.45米,
234.56米,
.01米,
345.67米,
1.23米,
456.78米
};
foreach(十进制大额付款,金额较小(付款))
{
//处理大额付款,这样我就可以把所有小额付款转移到我自己的银行账户上!
}
更新:
好的,下面是我所谓的“钓鱼钩”解决方案的后续内容。现在,让我添加一个免责声明,我真的想不出这样做的好理由,但您的情况可能会有所不同
其思想是,您只需创建一个传递给迭代器函数的“钓鱼钩”对象(引用类型)。迭代器函数操纵您的钓鱼钩对象,并且由于您在外部代码中仍然有对它的引用,因此您可以看到正在发生的事情:
public class FishingHook
{
public int Index { get; set; }
public decimal RunningTotal { get; set; }
public Func<decimal, bool> Criteria { get; set; }
}
public static IEnumerable<decimal> FishingHookIteration(IEnumerable<decimal> list, FishingHook hook)
{
hook.Index = 0;
hook.RunningTotal = 0;
foreach(decimal value in list)
{
// the hook object may define a Criteria delegate that
// determines whether to skip the current value
if (hook.Criteria == null || hook.Criteria(value))
{
hook.RunningTotal += value;
yield return value;
hook.Index++;
}
}
}
public-class-FishingHook
{
公共int索引{get;set;}
公共十进制运行总数{get;set;}
公共函数条件{get;set;}
}
公共静态IEnumerable FishingHookiOperation(IEnumerable列表,FishingHook)
{
指数=0;
hook.RunningTotal=0;
foreach(列表中的十进制值)
{
//钩子对象可以定义一个条件委托
//确定是否跳过当前值
if(hook.Criteria==null | | hook.Criteria(value))
{
hook.RunningTotal+=值;
收益回报值;
hook.Index++;
}
}
}
您可以这样使用它:
public static IEnumerable<decimal> IgnoreSmallValues(List<decimal> list)
{
decimal runningTotal = 0M;
foreach (decimal value in list)
{
// if the value is less than 1% of the running total, then ignore it
if (runningTotal == 0M || value >= 0.01M * runningTotal)
{
runningTotal += value;
yield return value;
}
}
}
List<decimal> payments = new List<decimal>() {
123.45M,
.01M,
345.67M,
234.56M,
1.23M,
456.78M
};
FishingHook hook = new FishingHook();
decimal min = 0;
hook.Criteria = x => x > min; // exclude any values that are less than/equal to the defined minimum
foreach (decimal value in FishingHookIteration(payments, hook))
{
// update the minimum
if (value > min) min = value;
Console.WriteLine("Index: {0}, Value: {1}, Running Total: {2}", hook.Index, value, hook.RunningTotal);
}
// Resultint output is:
//Index: 0, Value: 123.45, Running Total: 123.45
//Index: 1, Value: 345.67, Running Total: 469.12
//Index: 2, Value: 456.78, Running Total: 925.90
// we've skipped the values .01, 234.56, and 1.23
var ZippedSequence = ValueList.Zip(Indexes, (value, index) => new {value, index}).Zip(RunningTotals, (temp, total) => new {temp.value, temp.index, total});
列表付款=新列表(){
123.45米,
.01米,
345.67米,
234.56米,
1.23米,
456.78米
};
FishingHook hook=新的FishingHook();
十进制最小值=0;
hook.Criteria=x=>x>min;//排除小于/等于定义的最小值的任何值
foreach(捕鱼作业(付款、钩)中的十进制值)
{
//更新最小值
如果(值>最小值)最小值=值;
WriteLine(“索引:{0},值:{1},运行总数:{2}”,hook.Index,值,hook.RunningTotal);
}
//结果输出为:
//索引:0,值:123.45,运行总数:123.45
//指数:1,数值:345.67,运行总数:469.12
//指数:2,数值:456.78,运行总数:925.90
//我们跳过了值.01、234.56和1.23
本质上,FishingHook对象为您提供了对迭代器执行方式的一些控制。我从这个问题中得到的印象是,你需要一些方法来访问迭代器的内部操作,这样你就可以在迭代过程中操纵它如何迭代,但是如果不是这样,那么这个解决方案可能对你所需要的东西来说是多余的。