Sar*_*els 12 c# closures yield dry
除了yield在Ruby中使用迭代器之外,我还使用它将控制权简单地传递回调用者,然后在被调用的方法中恢复控制.我想在C#中做的事情是类似的.在测试类中,我想获取一个连接实例,创建另一个使用该连接的变量实例,然后将该变量传递给调用方法,以便可以进行调整.然后,我希望控制返回到被调用的方法,以便可以处理连接.我想我想要像Ruby一样的块/闭包.这是一般的想法:
private static MyThing getThing()
{
using (var connection = new Connection())
{
yield return new MyThing(connection);
}
}
[TestMethod]
public void MyTest1()
{
// call getThing(), use yielded MyThing, control returns to getThing()
// for disposal
}
[TestMethod]
public void MyTest2()
{
// call getThing(), use yielded MyThing, control returns to getThing()
// for disposal
}
...
Run Code Online (Sandbox Code Playgroud)
这在C#中不起作用; ReSharper告诉我,body getThing不能是迭代器块,因为MyThing它不是迭代器接口类型.这绝对是正确的,但我不想迭代一些列表.我猜我不应该使用,yield如果我不使用迭代器.任何想法,我怎样才能实现在C#此块/关闭的事情,所以我没有给缠上了我的代码MyTest1,MyTest2......与代码getThing()的身上?
Bli*_*ndy 19
你想要的是lambda表达式,如:
// not named GetThing because it doesn't return anything
private static void Thing(Action<MyThing> thing)
{
using (var connection = new Connection())
{
thing(new MyThing(connection));
}
}
// ...
// you call it like this
Thing(t=>{
t.Read();
t.Sing();
t.Laugh();
});
Run Code Online (Sandbox Code Playgroud)
这t与yieldRuby中的方式相同.C#yield是不同的,它构造了可以迭代的生成器.
你说你想使用C#的yield关键字就像使用Ruby的yield关键字一样.你似乎对这两者实际上做了什么有点困惑:两者完全无关,你要求的是,根本不可能.
C#yield关键字不是 Ruby yield关键字的C#等价物.实际上,C#中没有与Ruby yield关键字相同的东西.和红宝石等同于C#的yield关键字是没有的yield 关键字,它的Enumerator::Yielder#yield 方法(也别名为Enumerator::Yielder#<<).
IOW,它用于返回迭代器的下一个元素.以下是官方MSDN文档中的简略示例:
public static IEnumerable Power(int number, int exponent) {
var counter = 0;
var result = 1;
while (counter++ < exponent) {
result *= number;
yield return result; }}
Run Code Online (Sandbox Code Playgroud)
像这样使用它:
foreach (int i in Power(2, 8)) { Console.Write("{0} ", i); }
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
在C#,yield用来产生一个值给呼叫者和Ruby中,yield用于产生控制到一个块参数
事实上,在Ruby中,yield只是一个捷径Proc#call.
想象一下,如果yield不存在的话.你会如何if在Ruby中编写方法?它看起来像这样:
class TrueClass
def if(code)
code.call
end
end
class FalseClass
def if(_); end
end
true.if(lambda { puts "It's true!" })
Run Code Online (Sandbox Code Playgroud)
这有点麻烦.在Ruby 1.9中,我们获得了proc文字和快捷语法Proc#call,这使它更好一些:
class TrueClass
def if(code)
code.()
end
end
true.if(->{ puts "It's true!' })
Run Code Online (Sandbox Code Playgroud)
然而,Yukihiro Matsumoto注意到,绝大多数高阶程序只采用一个程序参数.(特别是因为Ruby在语言中内置了几个控制流构造,否则需要多个过程参数,比如if-then-else需要两个参数,case-when这需要n个参数.)因此,他创建了一种专门的方法来传递一个过程参数:块.(事实上,我们在一开始就已经看到了这个例子,因为Kernel#lambda它实际上只是一个普通的方法,它接受一个块并返回一个Proc.)
class TrueClass
def if(&code)
code.()
end
end
true.if { puts "It's true!" }
Run Code Online (Sandbox Code Playgroud)
现在,因为我们只能将一个块传递给一个方法,所以我们真的不需要显式地命名变量,因为无论如何都不会有歧义:
def if
???.() # But what do we put here? We don't have a name to call #call on!
end
Run Code Online (Sandbox Code Playgroud)
但是,由于我们现在不再拥有可以发送消息的名称,我们还需要其他方式.再次,我们得到那些80/20解决方案是如此典型的红宝石之一:有吨人们可能希望与块做的事情:改造它,它保存在一个属性,它传递给另一种方法,检查它,打印它...然而,到目前为止,最常见的事情是调用它.因此,matz为这种常见情况添加了另一种专用的快捷语法:yield意思是" call传递给方法的块".因此,我们不需要名称:
def if; yield end
Run Code Online (Sandbox Code Playgroud)
那么,什么是 C#等同于Ruby的yield关键字?好吧,让我们回到第一个Ruby示例,我们显式地将该过程作为参数传递:
def foo(bar)
bar.('StackOverflow')
end
foo ->name { puts "Higher-order Hello World from #{name}!" }
Run Code Online (Sandbox Code Playgroud)
C#等价物完全相同:
void Foo(Action<string> bar) => bar("StackOverflow")
Foo(name => { Console.WriteLine("Higher-order Hello World from {0]!", name); })
Run Code Online (Sandbox Code Playgroud)