9

The yield keyword documentation says:

The yield keyword signals to the compiler that the method in which it appears is an iterator block.

I have encountered code using the yield keyword outside any iterator block. Should this be considered as a programing mistake or is it just fine?

EDIT Sorry forgot to post my code:

int yield = previousVal/actualVal;
return yield; // Should this be allowed!!!???

Thanks.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
GETah
  • 20,922
  • 7
  • 61
  • 103
  • 4
    Whoever came up with the line "return yield;" is an evil genius of epic proportions. – Marc Gravell Nov 25 '11 at 20:42
  • We have some very nice code base man! I asked this question cuz I was arguing against this kind of coding... I wanted to hear what the community thinks about it – GETah Nov 25 '11 at 20:51
  • There's nothing inherently wrong with it - except maybe you could argue that the fact that it made you ask this question means it had potential to be confusing. The word `quotient` or `ratio` would have saved a lot of head-scratching :p – Marc Gravell Nov 25 '11 at 20:54
  • @GETah: there is no `yield` keyword in your example. In your example, "yield" is an identifier. – phoog Nov 25 '11 at 20:56
  • Well, may be this code was written before the yield keyword was introduced :-) or the author didn't know it was a keyword – MBen Nov 25 '11 at 21:11
  • I don't see why this should be closed. It wasn't a keyword, but then that's the real question. – John Saunders Nov 25 '11 at 21:17
  • I don't have a compiler in front of me but am surprised it doesn't have to be declared `int @yield`. I guess because it's not a contextual keyword unto itself. – John K Nov 26 '11 at 05:32
  • @JohnK it's precisely *because* it is a contextual keyword that you don't need to type `@yield`. Contextual keywords only function as keywords in a certain context. Non-contextual keywords like `class` are always keywords in every context, and therefore need to have an @ sign prepended if you want to use them as identifiers. – phoog Nov 30 '11 at 22:05

3 Answers3

22

It's okay to use yield outside an iterator block - it just means it isn't being used as a contextual keyword.

For example:

// No idea whether this is financially correct, but imagine it is :)
decimal yield = amountReturned / amountInvested;

At that point it's not a contextual keyword (it's never a "full" keyword), it's just an identifier. Unless it's clearly the best choice in terms of normal clarity, I'd try to avoid using it anyway, but sometimes it might be.

You can only use it as a contextual keyword for yield return and yield break, which are only ever valid in an iterator block. (They're what turn a method into an iterator.)

EDIT: To answer your "should this be allowed" question... yes, it should. Otherwise all the existing C# 1 code which used yield as an identifier would have become invalid when C# 2 was released. It should be used with care, and the C# team have made sure it's never actually ambiguous, but it makes sense for it to be valid.

The same goes for many other contextual keywords - "from", "select", "where" etc. Would you want to prevent those from ever being identifiers?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Yes, this is exactly my point – GETah Nov 25 '11 at 20:29
  • 1
    @GETah: Well it's not really a *keyword* in the context you've given... – Jon Skeet Nov 25 '11 at 20:30
  • Jon, just testing my understanding here... Is the `yield` as used in the method such as my `GetNotVeryRandomNumbers` in [my answer](http://stackoverflow.com/q/8273864/593627) not in an iterator block? (i thought `yield` creates one, but doesn't have to exist within one). This seems to contradict your last statement. – George Duckett Nov 25 '11 at 20:35
  • Re-reading, i think you mean it's `yield break` that has to be in an iterator block. I took you to mean both had to be in an iterator block. – George Duckett Nov 25 '11 at 20:37
  • @GeorgeDuckett: *Either* of them turns a method into an iterator block. Have edited for clarity. – Jon Skeet Nov 25 '11 at 20:41
  • Ahh ok, so the method its self becomes an iterator block, thanks for teaching. :) – George Duckett Nov 25 '11 at 20:43
  • 2
    +1 to the "return yield" could have existed prior to the introduction to the yield contextual keyword. – Peter Ritchie Nov 25 '11 at 20:53
9

What that is saying is that if you use yield in a method the method becomes an iterator block.

Also, as yield is a contextual keyword it can freely be used elsewhere, where full keywords can't (e.g. variable names).

void AnotherMethod()
{
    foreach(var NotRandomNumber in GetNotVeryRandomNumbers())
    {
        Console.WriteLine("This is not random! {0}", NotRandomNumber);
    }
}

IEnumerable<int> GetNotVeryRandomNumbers()
{
    yield return 1;
    yield return 53;
    yield return 79;
    yield return 1;
    yield return 789;
}

// Perhaps more useful function below: (untested but you get the idea)
IEnumerable<Node> DepthFirstIteration(Node)
{
    foreach(var Child in Node.Children)
    {
        foreach(var FurtherChildren in DepthFirstIteration(Child))
        {
            yield return FurtherChildren;
        }
    }
    yield return Node;
}

See also: This answer, which shows using one yield after another. Clever Uses of Iterator Blocks

Community
  • 1
  • 1
George Duckett
  • 31,770
  • 9
  • 95
  • 162
  • I might be wrong, but I think this misses the point of the question. The question isn't "are iterator blocks useful?" - but rather: when used ***outside of*** an iterator block, is "yield" useful? – Marc Gravell Nov 25 '11 at 20:34
  • In the `GetNotVeryRandomNumbers` method it isn't in an iterator block (it creates one), or am i misunderstanding iterator blocks? – George Duckett Nov 25 '11 at 20:39
  • With the edit, it's clear i definitely did miss the point of the question! (oops) – George Duckett Nov 25 '11 at 20:41
  • @GeorgeDuckett I believe it is more accurate to say that the compiler treats the method as an iterator block than that the calling code does so. To the calling code, the method is just a method that returns an IEnumerable or and IEnumerator, or their generic counterparts. The calling code is unaware of the implementation detail (whether the method is coded as an iterator block or simply as returning an object implementing one of those interfaces). In other words, the compiler compiles the iterator block into a method returning an object implementing the appropriate interface. – phoog Nov 25 '11 at 21:02
  • @GeorgeDuckett now if only I could edit that comment to fix the monstrosity that is the last sentence. – phoog Nov 25 '11 at 21:18
5

yield is a contextual keyword.
When used in yield return or yield break, it is a keyword and creates an iterator.
When used elsewhere, it's a normal identifier.

yield can be extremely useful for more than ordinary collections; it can also be used to implement primitive coroutines.

Community
  • 1
  • 1
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964