Iterator blocks have an interesting property: they defer execution. When the method (or property) is called, none of your code is executed – it only starts running when MoveNext()
is first called.
Deferred execution is a great thing in many ways, but it’s a pain when it comes to parameter checking. If you check parameters within an iterator block, you’ve effectively left a timebomb – the error will be potentially reported a long way from the original source of the problem. (Side-note: this is why I prefer using a cast to as
when I know what type something really should be – I’d rather get an InvalidCastException
immediately than a NullReferenceException
later on.)
One solution to this is to check the parameters in the public method and then call a private method which is implemented with an iterator block. That’s a bit ugly though. It would be nice to be able to specify the parameter checking in an initial block which could only occur at the start of the method – something like this simple implementation of Repeat
:
{
yield do
{
if (count < 0)
{
throw new ArgumentException(count);
}
}
for (int i=0; i < count; i++)
{
yield return element;
}
}
I’ve chosen yield do
for two reasons: it fits in with the rest of the yield
pattern, and by using an existing keyword I suspect it reduces/eliminates the chance of this being a breaking change. It’s not ideal from the point of self-description, but do
is the closest keyword I could find when I looked at the spec. In a language with begin
as a keyword, that would probably be a better choice – or initial
. fixed
appeals in that the code is fixed in the method rather than being shunted off into the compiler-generated type, but the normal use of fixed
is far too different to make this an appealing choice. Any other suggestions are very welcome :)
Is this an important enough change to make it worth including in C# 4? I’m not sure. The negative side is that I suspect relatively few people use iterator blocks much, so it may not have a very wide benefit. The positive side is that it only actually makes any difference to those who do use iterator blocks, and I believe almost all of them would find it a welcome addition. It’s simple to understand, and it makes iterator blocks much easier to use in a “correct” manner when any form of initial checking is involved.
Note, mostly to myself and Marc Gravell: I haven’t checked, but I suspect most of Push LINQ is broken in this respect.
Further note to self – I really need to fix my code formatter to treat yield
as a contextual keyword.