C#中作为方法参数的运算符

8

我不认为在C# 3.0中可以将运算符用作方法的参数,但是否有一种模拟方法或某种语法糖使其看起来像是正在进行的操作呢?

我之所以问这个问题是因为我最近实现了the thrush combinator in C#,但在翻译Raganwald's Ruby example时遇到了问题。

(1..100).select(&:odd?).inject(&:+).into { |x| x * x }

将1到100之间的数字取出奇数,求和后再求这个数的平方。

我对Symbol#to_proc的内容不太了解。它在上面的select(&:odd?)inject(&:+)中使用了&:。

2个回答

8

嗯,简单来说,你可以使用lambda:

public void DoSomething(Func<int, int, int> op)
{
    Console.WriteLine(op(5, 2));
}

DoSomething((x, y) => x + y);
DoSomething((x, y) => x * y);
// etc

不过,这并不是很令人兴奋。如果我们能够预先构建所有这些代理就好了。当然,您可以使用静态类来实现这一点:

public static class Operator<T>
{
     public static readonly Func<T, T, T> Plus;
     public static readonly Func<T, T, T> Minus;
     // etc

     static Operator()
     {
         // Build the delegates using expression trees, probably
     }
}

确实,如果你想查看,Marc Gravell在MiscUtil中已经做了非常相似的事情。然后你可以调用:

DoSomething(Operator<int>.Plus);

虽然不太美观,但我相信这是目前最接近的支持方式。

很抱歉,我真的不太了解Ruby相关的内容,所以无法发表评论...


非常好的答案,Operator类似乎正是我正在寻找的。不过稍后我会试一下。 - Jonas Elfström

2
以下是直接、尽可能字面的C#翻译:
(Func<int>)(x => x * x)(
    Enumerable.Range(1, 100)
        .Where(x => x % 2 == 1)
        .Aggregate((x, y) => x + y))

具体来说:

  • 块:{||} - 变成了lambda表达式:=>
  • select 变成了 Where
  • inject 变成了 Aggregate
  • into 成为lambda实例的直接调用

非常好,除了它忽略了Thrush组合器的重点,即在这种情况下将x*x移到末尾以提高可读性。无论如何值得一加。 - Jonas Elfström
没有意识到那是这种情况-我只看了Scala的翻译,它做了我所做的事情。话虽如此,如果想要,.Into()可以轻松地编写为扩展方法。 - Pavel Minaev
Ghosh的Scala实现非常好:(1 to 100) .filter(_ % 2 != 0) .foldLeft(0)(_ + _) .into((x: Int) => x * x) - Jonas Elfström
是的,占位符可以简化一些事情。实际上,想起来了,C#有运算符重载,所以我认为可以定义 _ 并重载它的运算符,使其产生 lambda...虽然它不会像 Scala 那样让你写 _.foo(),但它会让你写 _ + 1 或者甚至是 _ + _。我需要再考虑一下。 - Pavel Minaev

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