为什么迭代器方法不能采用“ref”或“ref”? 或“出” 参数?
我今天早些时候尝试过:
public interface IFoo
{
IEnumerable<int> GetItems_A( ref int somethingElse );
IEnumerable<int> GetItems_B( ref int somethingElse );
}
public class Bar : IFoo
{
public IEnumerable<int> GetItems_A( ref int somethingElse )
{
// Ok...
}
public IEnumerable<int> GetItems_B( ref int somethingElse )
{
yield return 7; // CS1623: Iterators cannot have ref or out parameters
}
}
这背后的理由是什么?
I tried this earlier today:
public interface IFoo
{
IEnumerable<int> GetItems_A( ref int somethingElse );
IEnumerable<int> GetItems_B( ref int somethingElse );
}
public class Bar : IFoo
{
public IEnumerable<int> GetItems_A( ref int somethingElse )
{
// Ok...
}
public IEnumerable<int> GetItems_B( ref int somethingElse )
{
yield return 7; // CS1623: Iterators cannot have ref or out parameters
}
}
What's the rationale behind this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
C# 迭代器内部是状态机。 每次你
yield return
某些东西时,你离开的地方应该与局部变量的状态一起保存,以便你可以返回并从那里继续。为了保持这种状态,C# 编译器创建一个类来保存局部变量及其应继续的位置。 不可能将
ref
或out
值作为类中的字段。 因此,如果允许您将参数声明为ref
或out
,则无法保留我们停止时函数的完整快照。编辑:从技术上讲,并非所有返回
IEnumerable
的方法都被视为迭代器。 只有那些使用yield
直接生成序列的才被视为迭代器。 因此,虽然将迭代器拆分为两个方法是一个很好且常见的解决方法,但它与我刚才所说的并不矛盾。 外部方法(不直接使用yield
)不被视为迭代器。C# iterators are state machines internally. Every time you
yield return
something, the place where you left off should be saved along with the state of local variables so that you could get back and continue from there.To hold this state, C# compiler creates a class to hold local variables and the place it should continue from. It's not possible to have a
ref
orout
value as a field in a class. Consequently, if you were allowed to declare a parameter asref
orout
, there would be no way to keep the complete snapshot of the function at the time we had left off.EDIT: Technically, not all methods that return
IEnumerable<T>
are considered iterators. Just those that useyield
to produce a sequence directly are considered iterators. Therefore, while the splitting the iterator into two methods is a nice and common workaround, it doesn't contradict with what I just said. The outer method (that doesn't useyield
directly) is not considered an iterator.如果您想从方法中返回迭代器和 int,解决方法是:
您应该注意迭代器方法中没有任何代码(即基本上包含
yield return
或 < code>yield break) 会一直执行,直到调用 Enumerator 中的MoveNext()
方法。 ,如果您能够在迭代器方法中使用out
或ref
,您会得到如下令人惊讶的行为:这是一个常见的陷阱,一个相关的问题是:
因此 一个好的模式是将迭代器方法分为两部分:一部分立即执行,另一部分包含应延迟执行的代码。
编辑:
如果您确实想要移动迭代器会修改 ref 参数的行为,您可以执行以下操作:
If you want to return both an iterator and an int from your method, a workaround is this:
You should note that none of the code inside an iterator method (i.e. basically a method that contains
yield return
oryield break
) is executed until theMoveNext()
method in the Enumerator is called. So if you were able to useout
orref
in your iterator method, you would get surprising behavior like this:This is a common pitfall, a related issue is this:
So a good pattern is to separate iterator methods into two parts: one to execute immediately and one that contains the code that should be lazily executed.
EDIT:
If you really want the behavior where moving the iterator would modify the
ref
-parameter, you could do something like this:在较高级别上, ref 变量可以指向许多位置,包括堆栈上的值类型。 最初通过调用 iterator 方法创建迭代器的时间和分配 ref 变量的时间是两个截然不同的时间。 无法保证最初通过引用传递的变量在迭代器实际执行时仍然存在。 因此这是不允许的(或可验证的)
At a highish level, A ref variable can point to many locations including to value types that are on the stack. The time at which the iterator is initially created by calling the iterator method and when the ref variable would be assigned are two very different times. It is not possible to guarantee that the variable which originally was passed by reference is still around when the iterator actually executes. Hence it is not allowed (or verifiable)
其他人已经解释了为什么迭代器不能有 ref 参数。 这是一个简单的替代方案:
如果您有多个项目要传入和传出,请定义一个类来保存它们。
Others have explained why your iterator can't have a ref parameter. Here's a simple alternative:
If you have several items to pass in and out, define a class to hold them.
当我需要返回的值来自迭代项时,我已经使用函数解决了这个问题:
I've gotten around this problem using functions, when the value that I need to return is derived from the iterated items:
您可以轻松地将 ref 参数替换为类似的东西
,这是我在我们不太关心性能的环境中所做的事情,而是主要针对可维护性进行优化(前提是您不能只将参数更改为引用类型)。
You can easily replace the ref param with something like
Which is something I've done in environments where we don't care so much about performance and instead are optimizing mostly for maintainability (provided you can't just change the param to a reference type).