C# 像这样优雅地重构代码(避免标记)
我有一个运行在可枚举项上的函数,但对于第一项,该函数应该有点不同,例如:C# 像这样优雅地重构代码(避免标记),c#,.net,.net-4.0,C#,.net,.net 4.0,我有一个运行在可枚举项上的函数,但对于第一项,该函数应该有点不同,例如: void start() { List<string> a = ... a.ForEach(DoWork); } bool isFirst = true; private void DoWork(string s) { // do something if(isFirst) isFirst = false; else print("first stu
void start() {
List<string> a = ...
a.ForEach(DoWork);
}
bool isFirst = true;
private void DoWork(string s) {
// do something
if(isFirst)
isFirst = false;
else
print("first stuff");
// do something
}
void start(){
列表a=。。。
a、 ForEach(DoWork);
}
bool isFirst=true;
专用空道具(字符串s){
//做点什么
如果(isFirst)
isFirst=false;
其他的
打印(“第一件事”);
//做点什么
}
你将如何重构它以避免那个丑陋的标志?阐述吉米·霍法的答案,如果你真的想对第一项做些什么,你可以这样做
using System.Linq; // reference to System.Core.dll
List<string> list = ..
list.Skip(1).ForEach(DoWork) // if you use List<T>.ForEeach()
DoFirstWork(a[0])
a.Skip(1).ForEach(DoWork)
如果重点是它在逻辑上与列表的其余部分是分开的,那么您应该使用单独的函数。查看Jon Skeet的
他们是他的的一部分,这可能有点过于严厉,但我不久前从另一个问题中得出了这个结论
public static void IterateWithSpecialFirst<T>(this IEnumerable<T> source,
Action<T> firstAction,
Action<T> subsequentActions)
{
using (IEnumerator<T> iterator = source.GetEnumerator())
{
if (iterator.MoveNext())
{
firstAction(iterator.Current);
}
while (iterator.MoveNext())
{
subsequentActions(iterator.Current);
}
}
}
public static void IterateWithSpecialFirst(此IEnumerable源代码,
行动第一行动,
行动(后续行动)
{
使用(IEnumerator迭代器=source.GetEnumerator())
{
if(iterator.MoveNext())
{
firstAction(iterator.Current);
}
while(iterator.MoveNext())
{
后续操作(迭代器当前);
}
}
}
在不知道为什么需要对第一个元素进行不同处理的情况下,很难说以不同方式处理第一个元素的“最佳”方式是什么
如果将序列的元素输入到框架的ForEach方法中,则无法优雅地向动作委托提供确定元素参数在源序列中的位置所需的信息,因此我认为需要额外的步骤。如果在循环之后不需要对序列执行任何操作,则始终可以使用队列(或堆栈),通过Dequeue()(或Pop())方法调用将第一个元素传递给正在使用的任何处理程序,然后得到剩余的“同构”序列。对于所有闪亮的Linq材料来说,这似乎是最基本的,但对于loop来说,总是有旧的时尚
var yourList = new List<int>{1,1,2,3,5,8,13,21};
for(int i = 0; i < yourList.Count; i++)
{
if(i == 0)
DoFirstElementStuff(yourList[i]);
else
DoNonFirstElementStuff(yourList[i]);
}
var yourList=新列表{1,1,2,3,5,8,13,21};
for(int i=0;i
如果您不想在循环中更改列表,这将是很好的。否则,您可能需要显式地使用迭代器。在这一点上,你不得不怀疑,仅仅去掉IsFirst标志是否真的值得。编辑:添加了用法示例,添加了ForFirst方法,对我的段落重新排序 下面是一个完整的解决方案 使用方法如下所示:
list.ForFirst(DoWorkForFirst).ForRemainder(DoWork);
// or
list.ForNext(1, DoWorkForFirst).ForRemainder(DoWork);
关键是ForNext
方法,它对集合中指定的下一组项执行操作,并返回其余项。我还实现了一个ForFirst
方法,该方法只需使用count:1调用ForNext
class Program
{
static void Main(string[] args)
{
List<string> list = new List<string>();
// ...
list.ForNext(1, DoWorkForFirst).ForRemainder(DoWork);
}
static void DoWorkForFirst(string s)
{
// do work for first item
}
static void DoWork(string s)
{
// do work for remaining items
}
}
public static class EnumerableExtensions
{
public static IEnumerable<T> ForFirst<T>(this IEnumerable<T> enumerable, Action<T> action)
{
return enumerable.ForNext(1, action);
}
public static IEnumerable<T> ForNext<T>(this IEnumerable<T> enumerable, int count, Action<T> action)
{
if (enumerable == null)
throw new ArgumentNullException("enumerable");
using (var enumerator = enumerable.GetEnumerator())
{
// perform the action for the first <count> items of the collection
while (count > 0)
{
if (!enumerator.MoveNext())
throw new ArgumentOutOfRangeException(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Unexpected end of collection reached. Expected {0} more items in the collection.", count));
action(enumerator.Current);
count--;
}
// return the remainder of the collection via an iterator
while (enumerator.MoveNext())
{
yield return enumerator.Current;
}
}
}
public static void ForRemainder<T>(this IEnumerable<T> enumerable, Action<T> action)
{
if (enumerable == null)
throw new ArgumentNullException("enumerable");
foreach (var item in enumerable)
{
action(item);
}
}
}
类程序
{
静态void Main(字符串[]参数)
{
列表=新列表();
// ...
列表。下一个为1,第一个为DoWork,其余为DoWork;
}
静态无效DoWorkForFirst(字符串s)
{
//做第一件事
}
静态空位定位销(串s)
{
//完成剩余项目的工作
}
}
公共静态类EnumerableExtensions
{
公共静态IEnumerable ForFirst(此IEnumerable可枚举,操作)
{
返回可枚举的ForNext(1,操作);
}
公共静态IEnumerable ForNext(此IEnumerable可枚举、整数计数、操作)
{
if(可枚举==null)
抛出新ArgumentNullException(“可枚举”);
使用(var enumerator=enumerable.GetEnumerator())
{
//对集合的第一个项目执行操作
而(计数>0)
{
如果(!enumerator.MoveNext())
抛出新ArgumentOutOfRangeException(string.Format(System.Globalization.CultureInfo.InvariantCulture),“达到集合的意外结尾。集合中应有{0}个项目。”,count));
操作(枚举器。当前);
计数--;
}
//通过迭代器返回集合的其余部分
while(枚举数.MoveNext())
{
产生返回枚举数。当前;
}
}
}
publicstaticvoidforrestine(此IEnumerable可枚举,Action)
{
if(可枚举==null)
抛出新ArgumentNullException(“可枚举”);
foreach(可枚举中的变量项)
{
行动(项目);
}
}
}
我觉得使用forrestins
方法有点可笑;我可以发誓我正在用它重新实现一个内置函数,但我没有想到它,而且我在浏览了一下之后也找不到一个等价的函数。更新:在阅读了其他答案后,我发现Linq中显然没有一个等效的内置版本。我现在感觉不那么糟糕。取决于你是如何“以不同方式处理它”的。如果您需要做一些完全不同的事情,那么我建议您在循环之外处理第一个元素。如果除了常规元素处理之外,还需要做一些事情,然后考虑对附加处理的结果进行检查。在代码中可能更容易理解,因此以下是一些:
string randomState = null; // My alma mater!
foreach(var ele in someEnumerable) {
if(randomState == null) randomState = setState(ele);
// handle additional processing here.
}
这样,您的“标志”实际上是一个您(大概)无论如何都需要的外部变量,因此您没有创建专用变量。如果您不想像枚举的其余部分那样处理第一个元素,也可以将其包装在
if/else
中。是否要忽略第一项或以不同的方式处理它?我想以不同的方式处理它。跳过它太容易了…可能是@Martin的重复:这个问题的答案很简单
class Program
{
static void Main(string[] args)
{
List<string> list = new List<string>();
// ...
list.ForNext(1, DoWorkForFirst).ForRemainder(DoWork);
}
static void DoWorkForFirst(string s)
{
// do work for first item
}
static void DoWork(string s)
{
// do work for remaining items
}
}
public static class EnumerableExtensions
{
public static IEnumerable<T> ForFirst<T>(this IEnumerable<T> enumerable, Action<T> action)
{
return enumerable.ForNext(1, action);
}
public static IEnumerable<T> ForNext<T>(this IEnumerable<T> enumerable, int count, Action<T> action)
{
if (enumerable == null)
throw new ArgumentNullException("enumerable");
using (var enumerator = enumerable.GetEnumerator())
{
// perform the action for the first <count> items of the collection
while (count > 0)
{
if (!enumerator.MoveNext())
throw new ArgumentOutOfRangeException(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Unexpected end of collection reached. Expected {0} more items in the collection.", count));
action(enumerator.Current);
count--;
}
// return the remainder of the collection via an iterator
while (enumerator.MoveNext())
{
yield return enumerator.Current;
}
}
}
public static void ForRemainder<T>(this IEnumerable<T> enumerable, Action<T> action)
{
if (enumerable == null)
throw new ArgumentNullException("enumerable");
foreach (var item in enumerable)
{
action(item);
}
}
}
string randomState = null; // My alma mater!
foreach(var ele in someEnumerable) {
if(randomState == null) randomState = setState(ele);
// handle additional processing here.
}