在 C# 中使用 Yield,就像在 Ruby 中一样
除了在 Ruby 中对迭代器使用 yield
之外,我还使用它在恢复被调用方法中的控制之前将控制权短暂地传递回调用者。我想在 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
}
...
这在 C# 中不起作用; ReSharper 告诉我 getThing
的主体不能是迭代器块,因为 MyThing
不是迭代器接口类型。这绝对是真的,但我不想遍历某些列表。我猜如果我不使用迭代器,我不应该使用 yield
。知道如何在 C# 中实现此块/闭包,这样我就不必将代码包装在 MyTest1
、MyTest2
... 中,并将代码放在 < code>getThing() 的主体?
Besides just using yield
for iterators in Ruby, I also use it to pass control briefly back to the caller before resuming control in the called method. What I want to do in C# is similar. In a test class, I want to get a connection instance, create another variable instance that uses that connection, then pass the variable to the calling method so it can be fiddled with. I then want control to return to the called method so that the connection can be disposed. I guess I'm wanting a block/closure like in Ruby. Here's the general idea:
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
}
...
This doesn't work in C#; ReSharper tells me that the body of getThing
cannot be an iterator block because MyThing
is not an iterator interface type. That's definitely true, but I don't want to iterate through some list. I'm guessing I shouldn't use yield
if I'm not working with iterators. Any idea how I can achieve this block/closure thing in C# so I don't have to wrap my code in MyTest1
, MyTest2
, ... with the code in getThing()
's body?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您想要的是 lambda 表达式,类似于:
这捕获
t
的方式与 Ruby 中yield
的方式相同。 C#yield
不同,它构造可以迭代的生成器。What you want are lambda expressions, something like:
This captures
t
the same wayyield
does in Ruby. The C#yield
is different, it constructs generators that can be iterated over.您说您想要使用 C# 的
yield
关键字,就像使用 Ruby 的yield
关键字一样。您似乎对两者实际上所做的事情有点困惑:两者绝对没有任何关系,您所要求的根本不可能。C#
yield
关键字不是相当于 Rubyyield
关键字的 C# 关键字。事实上,没有相当于 C# 中的 Rubyyield
关键字。 Ruby 中相当于 C# 的yield
关键字的不是yield
关键字,它是Enumerator:: Yielder#yield
方法(也别名为Enumerator::Yielder#<<
)。IOW,它用于返回迭代器的下一个元素。下面是官方 MSDN 文档中的一个删节示例:
像这样使用它:
Ruby 等效项类似于:
在 C# 中,
yield
用于生成 值 到 < em>调用者,在 Ruby 中,yield
用于将控制交给块参数事实上,在 Ruby 中,
>yield
只是Proc#call
的快捷方式。想象一下,如果
yield
不存在。如何在 Ruby 中编写if
方法?它看起来像这样:这有点麻烦。在 Ruby 1.9 中,我们获得了 proc 文字和
Proc#call
的快捷语法,这使得它变得更好一点:但是,Yukihiro Matsumoto 注意到,绝大多数高阶过程仅采用一个过程参数。 (特别是因为 Ruby 在语言中内置了多个控制流结构,否则需要多个过程参数,例如
if-then-else
需要两个和case-when
这将需要 n 个参数。)因此,他创建了一种专门的方法来传递恰好一个过程参数:块。 (事实上,我们一开始就已经看到了这样的例子,因为Kernel#lambda
实际上只是一个普通的方法,它接受一个块并返回一个Proc
。)现在,由于我们只能将一个块传递到方法中,因此我们实际上不需要显式命名该变量,因为无论如何都不会存在歧义:
但是,由于我们现在不再有可以发送的名称消息,我们需要其他方式。再次,我们得到了 Ruby 典型的 80/20 解决方案之一:人们可能想要对块做很多事情:转换它,将它存储在属性中,将它传递给另一个方法,检查它,打印它......但是,到目前为止,最常见的事情是调用它。因此,matz 针对这种常见情况添加了另一种专门的快捷语法:yield 表示“调用传递给方法的块”。因此,我们不需要名称:
那么,C# 中相当于 Ruby 的
yield
关键字的是什么?好吧,让我们回到第一个 Ruby 示例,其中我们显式地将过程作为参数传递:C# 等效项完全相同:
You say you want to use C#'s
yield
keyword the same way you would use Ruby'syield
keyword. You seem to be a little confused about what the two actually do: the two have absolutely nothing to do with each other, what you are asking for, is simply not possible.The C#
yield
keyword is not the C# equivalent of the Rubyyield
keyword. In fact, there is no equivalent to the Rubyyield
keyword in C#. And the Ruby equivalent to C#'syield
keyword is not theyield
keyword, it's theEnumerator::Yielder#yield
method (also aliased asEnumerator::Yielder#<<
).IOW, it's for returning the next element of an iterator. Here's an abridged example from the official MSDN documentation:
Use it like so:
The Ruby equivalent would be something like:
In C#,
yield
is used to yield a value to the caller and in Ruby,yield
is used to yield control to a block argumentIn fact, in Ruby,
yield
is just a shortcut forProc#call
.Imagine, if
yield
didn't exist. How would you write anif
method in Ruby? It would look like this:This is kind of cumbersome. In Ruby 1.9, we get proc literals and a shortcut syntax for
Proc#call
, which make it a little bit nicer:However, Yukihiro Matsumoto noticed, that the vast majority of higher-order procedures only take one procedure argument. (Especially since Ruby has several control-flow constructs built into the language, which would otherwise require multiple procedure arguments, like
if-then-else
which would require two andcase-when
which would require n arguments.) So, he created a specialized way to pass exactly one procedural argument: the block. (In fact, we already saw an example of this at the very beginning, becauseKernel#lambda
is actually just a normal method which takes a block and returns aProc
.)Now, since we can only ever pass exactly one block into a method, we really don't need to explicitly name the variable, since there can never be an ambiguity anyway:
However, since we now no longer have a name that we can send messages to, we need some other way. And again, we get one of those 80/20 solutions that are so typical for Ruby: there are tons of things that one might want to do with a block: transform it, store it in an attribute, pass it to another method, inspect it, print it … However, by far the most common thing to do is to call it. So, matz added another specialized shortcut syntax for exactly this common case:
yield
means "call
the block that was passed to the method". Therefore, we don't need a name:So, what is the C# equivalent to Ruby's
yield
keyword? Well, let's go back to the first Ruby example, where we explicitly passed the procedure as an argument:The C# equivalent is exactly the same:
我可能会将委托传递给迭代器。
I might pass a delegate into the iterator.
C# 中的
yield
专门用于返回迭代集合的位。具体来说,您的函数必须返回IEnumerable
或IEnumerable
才能使yield
正常工作,并且它应该在内部使用。代码>foreach
循环。它是 C# 中非常具体的构造,不能按照您尝试的方式使用。我不确定是否还有其他可以使用的构造,可能是带有 lambda 表达式的构造。
yield
in C# is specifically for returning bits of an iterated collection. Specifically, your function has to returnIEnumerable<Thing>
orIEnumerable
foryield
to work, and it's meant to be used from inside of aforeach
loop. It is a very specific construct in c#, and it can't be used in the way you're trying.I'm not sure off the top of my head if there's another construct that you could use or not, possibly something with lambda expressions.
您可以让
GetThing
获取包含要执行的代码的委托,然后从其他函数传递匿名方法。You can have
GetThing
take a delegate containing the code to execute, then pass anonymous methods from other functions.