您说您想使用 C# 的 yield 关键字,就像使用 Ruby 的 yield 关键字一样。您似乎对两者的实际操作有些困惑:两者绝对没有彼此有任何关系,您所要求的,根本不可能。
C# yield 关键字不是 Ruby yield 关键字的 C# 等效项。事实上,没有相当于 C# 中的 Ruby yield 关键字。而 Ruby 等价于 C# 的 yield 关键字 not yield keyword,它是 Enumerator::Yielder#yield method(也别名为 @ 987654329@)。
IOW,它用于返回迭代器的下一个元素。这是官方 MSDN 文档中的一个节选示例:
public static IEnumerable Power(int number, int exponent) {
var counter = 0;
var result = 1;
while (counter++ < exponent) {
result *= number;
yield return result; }}
像这样使用它:
foreach (int i in Power(2, 8)) { Console.Write("{0} ", i); }
Ruby 等价物类似于:
def power(number, exponent)
Enumerator.new do |yielder|
result = 1
1.upto(exponent-1) { yielder.yield result *= number } end end
puts power(2, 8).to_a
在 C# 中,yield 用于为 调用者 产生一个 值,而在 Ruby 中,yield 用于产生控制 到一个块参数
实际上,在 Ruby 中,yield 只是Proc#call 的一个快捷方式。
想象一下,如果yield 不存在。您将如何在 Ruby 中编写 if 方法?它看起来像这样:
class TrueClass
def if(code)
code.call
end
end
class FalseClass
def if(_); end
end
true.if(lambda { puts "It's true!" })
这有点麻烦。在 Ruby 1.9 中,我们获得了 proc 字面量和 Proc#call 的快捷语法,这使它更好一点:
class TrueClass
def if(code)
code.()
end
end
true.if(->{ puts "It's true!' })
然而,Yukihiro Matsumoto 注意到,绝大多数 高阶过程只接受一个 过程参数。 (特别是因为 Ruby 在语言中内置了多个控制流结构,否则需要多个过程参数,例如需要两个参数的 if-then-else 和需要 n 个参数的 case-when。)因此,他创建了一种专门的方法来传递一个程序参数:块。 (事实上,我们一开始就已经看到了这样的例子,因为Kernel#lambda实际上只是一个普通的方法,它接受一个块并返回一个Proc。)
class TrueClass
def if(&code)
code.()
end
end
true.if { puts "It's true!" }
现在,因为我们只能将一个块传递给一个方法,所以我们真的不需要显式地命名变量,因为无论如何都不会有歧义:
def if
???.() # But what do we put here? We don't have a name to call #call on!
end
但是,由于我们现在不再有可以向其发送消息的名称,因此我们需要其他方式。再一次,我们得到了 Ruby 非常典型的 80/20 解决方案之一:人们可能想要对块做很多事情:转换它,将它存储在一个属性中,将它传递给另一个方法,检查它,打印它……但是,far 最常见的做法是调用它。因此,matz 为这种常见情况添加了另一种专门的快捷语法:yield 表示“call 传递给方法的块”。因此,我们不需要名称:
def if; yield end
那么,什么是 C# 相当于 Ruby 的 yield 关键字?好吧,让我们回到第一个 Ruby 示例,我们显式地将过程作为参数传递:
def foo(bar)
bar.('StackOverflow')
end
foo ->name { puts "Higher-order Hello World from #{name}!" }
C# 等价物一模一样:
void Foo(Action<string> bar) => bar("StackOverflow")
Foo(name => { Console.WriteLine("Higher-order Hello World from {0]!", name); })