C#中是否有“upto”方法?

22

这是一段代码,它打印出从0到9的数字的平方:

for (int i = 0; i < 10; i++)
    Console.WriteLine(i*i);

使用for循环从0到N以1为步长进行某些操作是一种非常常见的习惯用语。

这里有一个表达这个意思的UpTo方法:

class MathUtil
{
    public static void UpTo(int n, Action<int> proc)
    {
        for (int i = 0; i < n; i++)
            proc(i);
    }
}

上面的squares示例现在是:

MathUtil.UpTo(10, (i) => Console.WriteLine(i * i));
我的问题是,标准的C#库是否有类似上述UpTo的东西?
理想情况下,我希望所有整数对象都可以使用'UpTo'方法。所以我可以这样做:
var n = 10;

n.UpTo(...);

在C#中是否有可能实现这个功能?


13
可能是个人口味问题,但我认为 for 循环比 UpTo 更易读。此外,在 for 循环中修改起始数字(0)或增量方法(i++)非常容易。我认为 Eric Lippert 在 ForEach 方法上的评论 也适用于这里。 - Heinzi
1
@Heinzi 感谢您提供Eric文章的链接! - dharmatech
1
.NET扩展方法(http://dnpextensions.codeplex.com/)有一个针对此的扩展方法。`5.times(...);` - Jalal
3
这让我想起关于新的“前往”(-->)和“被接近”(<--)运算符的优秀文章,它们被引入到C# 4.0中-请查看Eric Lippert的博客以获取更多详细信息:http://blogs.msdn.com/b/ericlippert/archive/2010/04/01/somelastminutefeatures.aspx - yas4891
@yas4891:有趣的东西 :) - Merlyn Morgan-Graham
如果您在VS中使用代码片段,那么这件事就非常容易了。只需要输入“for”,然后按两次Tab键即可。而且,“Times”是这种扩展方法的更好名称。将10.Times(DoStuff)10.Upto(DoStuff)进行比较。Ruby有类似的关键字times - nawfal
6个回答

49

把它变成一个扩展方法(请注意,在n参数前面有this,它定义了此方法所作用的类型):

static class MathUtil
{
    public static void UpTo(this int n, Action<int> proc)
    {
        for (int i = 0; i < n; i++)
            proc(i);
    }
}

使用方法:

10.UpTo((i) => Console.WriteLine(i * i));

注意: 上述方法调用并不特别直观。请记住,代码只需编写一次,但需要多次阅读。

也许允许以下类似的内容会稍微好一点,但说实话,我仍然只会写一个 foreach 循环。

0.UpTo(10 /*or 9 maybe*/, (i) => Console.WriteLine(i * i));
如果您想要这样做,那么您可以编写一个扩展方法,如下所示:
public static void UpTo(this int start, int end, Action<int> proc)
{
    for (int i = start; i < end; i++)
        proc(i);
}

如果您想要一个包含上限的范围,请将<更改为<=


6
我知道这是原帖所要求的,但语法太烂了:“10.UpTo(i => Console.WriteLine(i * i));”。除了我知道扩展方法和它们提供的“脏技巧”之外,否则这对我来说毫无意义 :)。"我知道这是原帖所要求的,但语法太差了:10.UpTo(i => Console.WriteLine(i * i));。如果不是因为我了解扩展方法及其提供的特殊技巧,我可能无法理解这句话的意思 :)" - Merlyn Morgan-Graham
我们有一个获胜者!谢谢乔治! - dharmatech
@GeorgeDuckett:修改后的答案解决了我所有的疑虑。干得好! - Merlyn Morgan-Graham
3
我更喜欢0.UpTo(10, /*blah*/)这个版本,它更直观易懂,更易读。 - Doctor Jones
Ruby有一个可以处理整数的“times”方法。因此,这是另一个选项的名称。Common Lisp有一个“dotimes”宏。 - dharmatech

24

看一下LINQ TakeWhile 或者针对你的整数特定情况,使用Enumerable.Range

Enumerable.Range(1, 10).Select(i => ...);

可以说在那里不应该在结尾加上一个Action,请参见ForEach的评论


9

试试这个:

public static class IntExtensions
{
    public static void UpTo(this int n, Action<int> proc)
    {
        for (int i = 0; i < n; i++)
            proc(i);
    }
}

通过这个,您可以编写

10.UpTo(i => Console.WriteLine(i * i));

我编写的函数被称为“扩展方法”。 在设计时,您会注意到它不是本地功能,因为它具有不同的图标。 扩展方法是静态方法或函数,包含在静态类中,并且它们所使用的类型是第一个参数,您必须在其中使用“this”关键字。 在“IntExtensions”类中,您可以编写所有所需的方法;将它们分组放在同一个静态类中使您能够更轻松地管理它们。

8

想要在一行中完成吗?这是方法:

Enumerable.Range(0, 9).Select(i => i * i).ToList().ForEach(j=>Console.WriteLine("%d",j));

5
需要使用'ToList()'来调用'ForEach',因为'ForEach'只对列表和数组公开。 - Michael Arnell

6
尝试使用Enumerable.Range,可能与TakeTakeWhile结合使用:
IEnumerable<int> values = Enumerable.Range(0, 20)
    .Take(10); // Not necessary in this example

foreach(var value in values)
{
    Console.WriteLine(value);
}

// or ...

foreach(var i in Enumerable.Range(0, 10))
{
    Console.WriteLine(i * i);
}

List<T> 上有一个 ForEach 方法,你可以使用它来更接近你想要的语法,但我认为这是一种不好的形式。它采用了纯查询/筛选/转换语法,以有效不可变的方式工作,并引入了副作用。

为了你未来的娱乐,你可能想查看 扩展方法IEnumerable<T>yield return。当你将这三个东西结合起来使用时,许多生成器类型的功能和有趣的语法就变得可能了。尽管我会认为这个特定的例子并不是使用它们的最佳场所,因为结果的语法变得混乱。


2
例如,在一个静态类“Extensions”中,将您的方法编写如下所示:
public static void UpTo(this int n, Action<int> proc)
{
    for (var i = 0; i < n; i++)
        proc(i);
}

使用方法:

var n = 10;
n.UpTo(i => Console.WriteLine(i * i));

希望这可以帮到您! :)

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