[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"sanity-1bE5mH9eXGYVA_Lp5nWFwC4ucPy33kCfaC-rUhHJNNU":3,"sanity-yfSKGZYHixhNKNOnu5h2BwkJezFeZaQRv5j6O091O8Y":663},{"data":4,"sourceMap":-1},{"latestPodcast":5,"latestReleases":14,"post":39,"recent":638},[6],{"_id":7,"publishedAt":8,"slug":9,"sponsored":12,"title":13},"d53f9358-3bb2-4f69-aebe-d31d19522cd4","2026-07-10T07:40:00.000Z",{"_type":10,"current":11},"slug","building-more-than-just-an-agent-harness",null,"Building more than just an agent harness",[15,21,27,33],{"_id":16,"publishedAt":17,"slug":18,"title":20},"eb5b66eb-9410-4329-83bb-22bbff39402a","2026-04-28T13:00:00.000Z",{"_type":10,"current":19},"turn-scattered-knowledge-into-trusted-intelligence","Turning scattered knowledge into trusted intelligence: Stack Internal 2026.3",{"_id":22,"publishedAt":23,"slug":24,"title":26},"369c2401-b62e-4a37-8ff8-bf603023ecad","2026-03-02T15:03:00.988Z",{"_type":10,"current":25},"what-s-new-at-stack-overflow-march-2026","What’s new at Stack Overflow: March 2026",{"_id":28,"publishedAt":29,"slug":30,"title":32},"5e9053a4-07ea-447c-91ea-29e0b6228537","2026-02-02T15:00:00.000Z",{"_type":10,"current":31},"what-s-new-at-stack-overflow-february-2026","What’s new at Stack Overflow: February 2026",{"_id":34,"publishedAt":35,"slug":36,"title":38},"a1b538eb-a8a6-46d0-80a1-ac70ec9bb935","2026-01-05T10:00:00.000-05:00",{"_type":10,"current":37},"what-s-new-at-stack-overflow-january-2026","What’s new at Stack Overflow: January 2026",{"_createdAt":40,"_id":41,"_rev":42,"_system":43,"_type":46,"_updatedAt":47,"author":48,"body":65,"comments":602,"dateUrl":603,"excerpt":604,"image":605,"legacyBody":608,"product":12,"publishedAt":611,"slug":612,"sponsored":12,"tags":614,"title":637,"visible":602},"2023-05-24T12:51:00Z","wp-post-20269","nvZcks16ldBXxsacAElHou",{"base":44},{"id":41,"rev":45},"Soh4zwnVSgD0IUkr59liGd","blogPost","2026-05-11T18:35:41Z",[49],{"_createdAt":50,"_id":51,"_rev":52,"_type":53,"_updatedAt":54,"avatar":55,"bio":60,"employee":61,"name":62,"slug":63},"2023-05-23T16:27:18Z","wp-author-cap-17067","ZP7627ZkmfDRGke9Ik3iT9","blogAuthor","2024-01-24T16:45:49Z",{"_type":56,"asset":57},"image",{"_ref":58,"_type":59},"image-015bff0f4bd16f5ba1974cf3119766c6fd6d9fd4-2211x1969-jpg","reference","","none","Isaac Lyman",{"current":64},"isaac-lyman",[66,86,136,209,212,236,239,262,297,333,336,405,450,458,461,496,526,541,558],{"_key":67,"_type":68,"children":69,"markDefs":84,"style":85},"5f94603727ea","block",[70,75,80],{"_key":71,"_type":72,"marks":73,"text":74},"5f94603727ea0","span",[],"Let’s talk about one of my favorite .NET features: ",{"_key":76,"_type":72,"marks":77,"text":79},"5f94603727ea1",[78],"code","IEnumerable",{"_key":81,"_type":72,"marks":82,"text":83},"5f94603727ea2",[],".",[],"normal",{"_key":87,"_type":68,"children":88,"markDefs":132,"style":85},"6d6f0bbd7d7d",[89,93,96,100,104,108,113,117,121,125,128],{"_key":90,"_type":72,"marks":91,"text":92},"6d6f0bbd7d7d0",[],"This interface enables iterating over a collection. In other words, if something is an ",{"_key":94,"_type":72,"marks":95,"text":79},"6d6f0bbd7d7d1",[78],{"_key":97,"_type":72,"marks":98,"text":99},"6d6f0bbd7d7d2",[],", you can mostly think of it like an array or a list. You can use a ",{"_key":101,"_type":72,"marks":102,"text":103},"6d6f0bbd7d7d3",[78],"foreach",{"_key":105,"_type":72,"marks":106,"text":107},"6d6f0bbd7d7d4",[]," statement to loop through it, you can use ",{"_key":109,"_type":72,"marks":110,"text":112},"6d6f0bbd7d7d5",[111],"7f57064f7858","LINQ",{"_key":114,"_type":72,"marks":115,"text":116},"6d6f0bbd7d7d6",[]," to map or reduce it in a hundred different ways, or you can explicitly cast it to an array with ",{"_key":118,"_type":72,"marks":119,"text":120},"6d6f0bbd7d7d7",[78],".ToArray()",{"_key":122,"_type":72,"marks":123,"text":124},"6d6f0bbd7d7d8",[]," and access elements by index. But there are a few things that make ",{"_key":126,"_type":72,"marks":127,"text":79},"6d6f0bbd7d7d9",[78],{"_key":129,"_type":72,"marks":130,"text":131},"6d6f0bbd7d7d10",[]," special—and a few things that make it tricky.",[133],{"_key":111,"_type":134,"href":135,"reference":12},"link","https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fdotnet\u002Fcsharp\u002Fprogramming-guide\u002Fconcepts\u002Flinq\u002F",{"_key":137,"_type":68,"children":138,"markDefs":208,"style":85},"80ef7579112c",[139,142,146,151,155,159,163,166,170,174,178,182,186,189,193,196,200,204],{"_key":140,"_type":72,"marks":141,"text":79},"80ef7579112c0",[78],{"_key":143,"_type":72,"marks":144,"text":145},"80ef7579112c1",[]," is the return type from an ",{"_key":147,"_type":72,"marks":148,"text":150},"80ef7579112c2",[149],"em","iterator",{"_key":152,"_type":72,"marks":153,"text":154},"80ef7579112c3",[],". An iterator is a method that uses the ",{"_key":156,"_type":72,"marks":157,"text":158},"80ef7579112c4",[78],"yield return",{"_key":160,"_type":72,"marks":161,"text":162},"80ef7579112c5",[]," keywords. ",{"_key":164,"_type":72,"marks":165,"text":158},"80ef7579112c6",[78],{"_key":167,"_type":72,"marks":168,"text":169},"80ef7579112c7",[]," is different from a normal ",{"_key":171,"_type":72,"marks":172,"text":173},"80ef7579112c8",[78],"return",{"_key":175,"_type":72,"marks":176,"text":177},"80ef7579112c9",[]," statement because, while it does return a value from the function, it doesn’t “close the book” on that function. The next time a value is expected, the function will continue executing statements ",{"_key":179,"_type":72,"marks":180,"text":181},"80ef7579112c10",[149],"after",{"_key":183,"_type":72,"marks":184,"text":185},"80ef7579112c11",[]," the ",{"_key":187,"_type":72,"marks":188,"text":158},"80ef7579112c12",[78],{"_key":190,"_type":72,"marks":191,"text":192},"80ef7579112c13",[]," until it hits another ",{"_key":194,"_type":72,"marks":195,"text":158},"80ef7579112c14",[78],{"_key":197,"_type":72,"marks":198,"text":199},"80ef7579112c15",[]," (or a ",{"_key":201,"_type":72,"marks":202,"text":203},"80ef7579112c16",[78],"yield break",{"_key":205,"_type":72,"marks":206,"text":207},"80ef7579112c17",[],", or the end of the function block). In other words, you could write an iterator that looks like this:",[],{"_key":210,"_type":78,"code":211,"markDefs":12},"7b75ca0833af","IEnumerable\u003Cint> GetOneTwoThree() {\n  yield return 1;\n  yield return 2;\n  yield return 3;\n  \u002F\u002F We could put \"yield break;\" here but there's no need, the end of the function signals the same thing.\n}\n",{"_key":213,"_type":68,"children":214,"markDefs":235,"style":85},"d6e857b06b67",[215,219,223,227,231],{"_key":216,"_type":72,"marks":217,"text":218},"d6e857b06b670",[],"When you call ",{"_key":220,"_type":72,"marks":221,"text":222},"d6e857b06b671",[78],"GetOneTwoThree()",{"_key":224,"_type":72,"marks":225,"text":226},"d6e857b06b672",[]," it will return an ",{"_key":228,"_type":72,"marks":229,"text":230},"d6e857b06b673",[78],"IEnumerable\u003Cint>",{"_key":232,"_type":72,"marks":233,"text":234},"d6e857b06b674",[],". As mentioned above, there are a few things you can do with this:",[],{"_key":237,"_type":78,"code":238,"markDefs":12},"84e3472b687c","var numbers = GetOneTwoThree();\n\nforeach (var number in numbers) {\n  Console.WriteLine(number);\n  \u002F\u002F Output:\n  \u002F\u002F 1\n  \u002F\u002F 2\n  \u002F\u002F 3\n}\n\nvar doubledNumbers = numbers.Select(num => num * 2);\n\nforeach (var number in doubledNumbers) {\n  Console.WriteLine(number);\n  \u002F\u002F Output:\n  \u002F\u002F 2\n  \u002F\u002F 4\n  \u002F\u002F 6\n}\n\nvar numberArray = numbers.ToArray();\nConsole.WriteLine(numberArray[0]); \u002F\u002F Output: 1\n",{"_key":240,"_type":68,"children":241,"markDefs":261,"style":85},"09d1386ba2a7",[242,246,250,254,257],{"_key":243,"_type":72,"marks":244,"text":245},"09d1386ba2a70",[],"You may notice that we’re iterating over ",{"_key":247,"_type":72,"marks":248,"text":249},"09d1386ba2a71",[78],"numbers",{"_key":251,"_type":72,"marks":252,"text":253},"09d1386ba2a72",[]," multiple times. For a simple iterator like the one I’ve written, that’s technically okay to do. Every time we iterate over ",{"_key":255,"_type":72,"marks":256,"text":249},"09d1386ba2a73",[78],{"_key":258,"_type":72,"marks":259,"text":260},"09d1386ba2a74",[],", it will start over at the beginning of the iterator method and yield all the same values over again. (You can’t expect this from every iterator; more on that later.)",[],{"_key":263,"_type":68,"children":264,"markDefs":296,"style":85},"e3173421c98c",[265,269,273,277,281,284,288,292],{"_key":266,"_type":72,"marks":267,"text":268},"e3173421c98c0",[149],"Lazy evaluation",{"_key":270,"_type":72,"marks":271,"text":272},"e3173421c98c1",[]," (the opposite of ",{"_key":274,"_type":72,"marks":275,"text":276},"e3173421c98c2",[149],"eager evaluation",{"_key":278,"_type":72,"marks":279,"text":280},"e3173421c98c3",[],") is when we wait to execute a piece of code until we absolutely, positively have to. When you call ",{"_key":282,"_type":72,"marks":283,"text":222},"e3173421c98c4",[78],{"_key":285,"_type":72,"marks":286,"text":287},"e3173421c98c5",[],", you’ll get a return value despite the fact that ",{"_key":289,"_type":72,"marks":290,"text":291},"e3173421c98c6",[149],"none of the code in the function has actually been executed yet!",{"_key":293,"_type":72,"marks":294,"text":295},"e3173421c98c7",[]," To prove it, run the following code in LINQPad.",[],{"_key":298,"_type":68,"children":299,"markDefs":330,"style":85},"570245f83451",[300,304,309,313,317,321,326],{"_key":301,"_type":72,"marks":302,"text":303},"570245f834510",[],"(If you’re not familiar with ",{"_key":305,"_type":72,"marks":306,"text":308},"570245f834511",[307],"0a0c55a6a917","LINQPad",{"_key":310,"_type":72,"marks":311,"text":312},"570245f834512",[],", you should check it out—it’s a powerful, portable C# playground. But the only thing you need to know about it here is that it provides the magic ",{"_key":314,"_type":72,"marks":315,"text":316},"570245f834513",[78],".Dump()",{"_key":318,"_type":72,"marks":319,"text":320},"570245f834514",[]," method, which outputs any value to the ",{"_key":322,"_type":72,"marks":323,"text":325},"570245f834515",[324],"strong","Results",{"_key":327,"_type":72,"marks":328,"text":329},"570245f834516",[]," pane.)",[331],{"_key":307,"_type":134,"href":332,"reference":12},"https:\u002F\u002Fwww.linqpad.net\u002F",{"_key":334,"_type":78,"code":335,"markDefs":12},"e517f64291be","bool didTheCodeRun = false;\n\nvoid Main() {\n  var results = RunTheCode();\n  didTheCodeRun.Dump();\n}\n\nIEnumerable\u003Cbool> RunTheCode() {\n  didTheCodeRun = true;\n  yield return true;\n}\n",{"_key":337,"_type":68,"children":338,"markDefs":404,"style":85},"8084dfd84fae",[339,343,347,351,355,359,363,367,371,375,379,383,386,390,393,397,400],{"_key":340,"_type":72,"marks":341,"text":342},"8084dfd84fae0",[],"The output of running ",{"_key":344,"_type":72,"marks":345,"text":346},"8084dfd84fae1",[78],"Main()",{"_key":348,"_type":72,"marks":349,"text":350},"8084dfd84fae2",[]," in the above snippet is ",{"_key":352,"_type":72,"marks":353,"text":354},"8084dfd84fae3",[78],"false",{"_key":356,"_type":72,"marks":357,"text":358},"8084dfd84fae4",[],". That’s right, after we run ",{"_key":360,"_type":72,"marks":361,"text":362},"8084dfd84fae5",[78],"RunTheCode()",{"_key":364,"_type":72,"marks":365,"text":366},"8084dfd84fae6",[],", which explicitly sets ",{"_key":368,"_type":72,"marks":369,"text":370},"8084dfd84fae7",[78],"didTheCodeRun",{"_key":372,"_type":72,"marks":373,"text":374},"8084dfd84fae8",[]," to ",{"_key":376,"_type":72,"marks":377,"text":378},"8084dfd84fae9",[78],"true",{"_key":380,"_type":72,"marks":381,"text":382},"8084dfd84fae10",[],", the value of ",{"_key":384,"_type":72,"marks":385,"text":370},"8084dfd84fae11",[78],{"_key":387,"_type":72,"marks":388,"text":389},"8084dfd84fae12",[]," is still ",{"_key":391,"_type":72,"marks":392,"text":354},"8084dfd84fae13",[78],{"_key":394,"_type":72,"marks":395,"text":396},"8084dfd84fae14",[],". None of the code in our iterator runs until we start iterating through the ",{"_key":398,"_type":72,"marks":399,"text":79},"8084dfd84fae15",[78],{"_key":401,"_type":72,"marks":402,"text":403},"8084dfd84fae16",[],". It’s lazily evaluated!",[],{"_key":406,"_type":68,"children":407,"markDefs":449,"style":85},"f17e64bb950a",[408,412,415,419,423,427,431,435,438,442,445],{"_key":409,"_type":72,"marks":410,"text":411},"f17e64bb950a0",[],"This may seem counterintuitive, but in a lot of cases it’s a good thing. What if you never end up iterating through the ",{"_key":413,"_type":72,"marks":414,"text":79},"f17e64bb950a1",[78],{"_key":416,"_type":72,"marks":417,"text":418},"f17e64bb950a2",[]," at all? Well, that’s a bunch of code the computer didn’t have to execute. Or what if you’re using a LINQ method like ",{"_key":420,"_type":72,"marks":421,"text":422},"f17e64bb950a3",[78],".First()",{"_key":424,"_type":72,"marks":425,"text":426},"f17e64bb950a4",[]," to try to find a specific item in the collection? You may not need to run ",{"_key":428,"_type":72,"marks":429,"text":430},"f17e64bb950a5",[149],"all",{"_key":432,"_type":72,"marks":433,"text":434},"f17e64bb950a6",[]," the code in the iterator to get the value you’re looking for–and you won’t. Once ",{"_key":436,"_type":72,"marks":437,"text":422},"f17e64bb950a7",[78],{"_key":439,"_type":72,"marks":440,"text":441},"f17e64bb950a8",[]," finds a value that matches the predicate, it will stop iterating. If you’re working with an ",{"_key":443,"_type":72,"marks":444,"text":79},"f17e64bb950a9",[78],{"_key":446,"_type":72,"marks":447,"text":448},"f17e64bb950a10",[]," that potentially has thousands of values (or more), you can save a lot of CPU cycles by only iterating as far as you need to.",[],{"_key":451,"_type":68,"children":452,"markDefs":457,"style":85},"12f1d5772128",[453],{"_key":454,"_type":72,"marks":455,"text":456},"12f1d57721280",[],"Of course, it’s all up to you. You can iterate as much or as little as you want. Let’s take a look at some of the ways to do that.",[],{"_key":459,"_type":78,"code":460,"markDefs":12},"75b033848a20","\u002F\u002F Here's a variable to track execution of code in an iterator\nint lastYielded = -1;\n\n\u002F\u002F Here's an iterator for us to play with\nIEnumerable\u003Cint> GetOneToTen() {\n  for (var num = 1; num \u003C= 10; num++) {\n    lastYielded = num;\n    yield return num;\n  }\n}\n\nvoid Main() {\n  var numbers = GetOneToTen();\n  lastYielded.Dump(); \u002F\u002F Output: -1\n\n  \u002F\u002F This gives us an 'instance' of the iteration\n  var enumerator = numbers.GetEnumerator();\n\n  \u002F\u002F This executes the iterator until the first yield return is reached\n  enumerator.MoveNext();\n\n  \u002F\u002F This gives us the current (most recently yielded) value of the iterator\n  enumerator.Current.Dump(); \u002F\u002F Output: 1\n  lastYielded.Dump(); \u002F\u002F Output: 1\n\n  \u002F\u002F This will iterate from 1 to 4, then stop\n  foreach (var num in numbers) {\n    if (num >= 4) {\n      break;\n    }\n  }\n\n  lastYielded.Dump(); \u002F\u002F Output: 4\n\n  \u002F\u002F This will not execute any code in the iterator.\n  \u002F\u002F  LINQ methods are lazily evaluated as well\n  var numbersTimesTwo = numbers.Select(num => num * 2);\n  lastYielded.Dump(); \u002F\u002F Output: 4\n\n  \u002F\u002F This will force the entire iterator to run, yielding all values\n  var arr = numbers.ToArray();\n  lastYielded.Dump(); \u002F\u002F Output: 10\n}\n",{"_key":462,"_type":68,"children":463,"markDefs":493,"style":85},"13efcd6309d6",[464,468,473,477,481,485,489],{"_key":465,"_type":72,"marks":466,"text":467},"13efcd6309d60",[],"It’s important to point out that many iterators are not as simple as the ones we’ve been using here. An iterator could query a database, for example—including the unfortunate possibility that it might alter data, or that iterating through it twice might yield completely different results! Some tools (like ",{"_key":469,"_type":72,"marks":470,"text":472},"13efcd6309d61",[471],"4c5dd79899bf","ReSharper",{"_key":474,"_type":72,"marks":475,"text":476},"13efcd6309d62",[],") will warn you against multiple enumeration for this reason. An iterator is, from one perspective, nothing more than a synchronous method that may not execute its code right away (or at all). And to muddy the waters just a little, not all iterators are synchronous; there’s also an ",{"_key":478,"_type":72,"marks":479,"text":480},"13efcd6309d63",[78],"IAsyncEnumerable",{"_key":482,"_type":72,"marks":483,"text":484},"13efcd6309d64",[]," interface (you can loop through it with ",{"_key":486,"_type":72,"marks":487,"text":488},"13efcd6309d65",[78],"await foreach",{"_key":490,"_type":72,"marks":491,"text":492},"13efcd6309d66",[],").",[494],{"_key":471,"_type":134,"href":495,"reference":12},"https:\u002F\u002Fwww.jetbrains.com\u002Fresharper\u002F",{"_key":497,"_type":68,"children":498,"markDefs":525,"style":85},"b8710450e0c9",[499,503,506,510,514,518,521],{"_key":500,"_type":72,"marks":501,"text":502},"b8710450e0c90",[],"Most of the time none of this is a problem. Almost all the time you can treat an ",{"_key":504,"_type":72,"marks":505,"text":79},"b8710450e0c91",[78],{"_key":507,"_type":72,"marks":508,"text":509},"b8710450e0c92",[]," like a list. But I’ve learned the hard way not to rely on this—I once used ",{"_key":511,"_type":72,"marks":512,"text":513},"b8710450e0c93",[78],".Select()",{"_key":515,"_type":72,"marks":516,"text":517},"b8710450e0c94",[]," to transform a collection while using an external variable to track a small piece of state on each iteration, then got very confused when the external variable wasn’t updated later in the method. In the end, I fixed the problem by forcing the iteration to complete with ",{"_key":519,"_type":72,"marks":520,"text":120},"b8710450e0c95",[78],{"_key":522,"_type":72,"marks":523,"text":524},"b8710450e0c96",[],". (There are multiple ways to approach something like this, depending on the expected size of the collection and what you’re trying to track.)",[],{"_key":527,"_type":68,"children":528,"markDefs":540,"style":85},"3decd42a0f9f",[529,533,536],{"_key":530,"_type":72,"marks":531,"text":532},"3decd42a0f9f0",[],"Hopefully this overview helps you avoid running into issues when you create or consume ",{"_key":534,"_type":72,"marks":535,"text":79},"3decd42a0f9f1",[78],{"_key":537,"_type":72,"marks":538,"text":539},"3decd42a0f9f2",[]," in your own code (which is extremely likely, as they’re everywhere in C#). Here are a couple of rules to remember:",[],{"_key":542,"_type":68,"children":543,"level":555,"listItem":556,"markDefs":557,"style":85},"c98e0320ed81",[544,548,551],{"_key":545,"_type":72,"marks":546,"text":547},"c98e0320ed810",[],"Try to avoid side effects when writing an iterator method. Anyone who uses the method should be able to treat it as though it synchronously returns an array. If an iterator changes or executes anything outside of itself, the caller may end up confused. This applies to functions passed to LINQ methods as well, since many of them return ",{"_key":549,"_type":72,"marks":550,"text":79},"c98e0320ed811",[78],{"_key":552,"_type":72,"marks":553,"text":554},"c98e0320ed812",[]," and are lazily evaluated.",1,"number",[],{"_key":559,"_type":68,"children":560,"level":555,"listItem":556,"markDefs":601,"style":85},"cc3b5feb6e57",[561,565,568,572,575,579,583,587,590,594,597],{"_key":562,"_type":72,"marks":563,"text":564},"cc3b5feb6e570",[],"You should avoid iterating over the same ",{"_key":566,"_type":72,"marks":567,"text":79},"cc3b5feb6e571",[78],{"_key":569,"_type":72,"marks":570,"text":571},"cc3b5feb6e572",[]," multiple times. If you know you’re going to access every value—for example, if you’re iterating it using a ",{"_key":573,"_type":72,"marks":574,"text":103},"cc3b5feb6e573",[78],{"_key":576,"_type":72,"marks":577,"text":578},"cc3b5feb6e574",[]," with no ",{"_key":580,"_type":72,"marks":581,"text":582},"cc3b5feb6e575",[78],"break",{"_key":584,"_type":72,"marks":585,"text":586},"cc3b5feb6e576",[]," or ",{"_key":588,"_type":72,"marks":589,"text":173},"cc3b5feb6e577",[78],{"_key":591,"_type":72,"marks":592,"text":593},"cc3b5feb6e578",[],"—enumerate it sooner rather than later by coercing the ",{"_key":595,"_type":72,"marks":596,"text":79},"cc3b5feb6e579",[78],{"_key":598,"_type":72,"marks":599,"text":600},"cc3b5feb6e5710",[]," to a list or array type. Then use that list or array for all future operations.",[],true,"2022\u002F06\u002F15","Don't hate, iterate. ",{"_type":56,"asset":606},{"_ref":607,"_type":59},"image-1460aa136a6969a2ad477834ab06e18617a0fdea-2560x1344-jpg",{"code":609,"language":610},"\u003C!-- wp:paragraph -->\n\u003Cp>Let’s talk about one of my favorite .NET features: \u003Ccode>IEnumerable\u003C\u002Fcode>.&nbsp;\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>This&nbsp; interface enables iterating over a collection. In other words, if something is an \u003Ccode>IEnumerable\u003C\u002Fcode>, you can mostly think of it like an array or a list. You can use a \u003Ccode>foreach\u003C\u002Fcode> statement to loop through it, you can use \u003Ca href=\"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fdotnet\u002Fcsharp\u002Fprogramming-guide\u002Fconcepts\u002Flinq\u002F\">LINQ\u003C\u002Fa> to map or reduce it in a hundred different ways, or you can explicitly cast it to an array with \u003Ccode>.ToArray()\u003C\u002Fcode> and access elements by index. But there are a few things that make \u003Ccode>IEnumerable\u003C\u002Fcode> special—and a few things that make it tricky.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>\u003Ccode>IEnumerable\u003C\u002Fcode> is the return type from an \u003Cem>iterator\u003C\u002Fem>. An iterator is a method that uses the \u003Ccode>yield return\u003C\u002Fcode> keywords. \u003Ccode>yield return\u003C\u002Fcode> is different from a normal \u003Ccode>return\u003C\u002Fcode> statement because, while it does return a value from the function, it doesn’t “close the book” on that function. The next time a value is expected, the function will continue executing statements \u003Cem>after\u003C\u002Fem> the \u003Ccode>yield return\u003C\u002Fcode> until it hits another \u003Ccode>yield return\u003C\u002Fcode> (or a \u003Ccode>yield break\u003C\u002Fcode>, or the end of the function block). In other words, you could write an iterator that looks like this:\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:code -->\n\u003Cpre class=\"wp-block-code\">\u003Ccode>IEnumerable&lt;int&gt; GetOneTwoThree() {\n  yield return 1;\n  yield return 2;\n  yield return 3;\n  \u002F\u002F We could put \"yield break;\" here but there's no need, the end of the function signals the same thing.\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C!-- \u002Fwp:code -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>When you call \u003Ccode>GetOneTwoThree()\u003C\u002Fcode> it will return an \u003Ccode>IEnumerable&lt;int&gt;\u003C\u002Fcode>. As mentioned above, there are a few things you can do with this:\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:code -->\n\u003Cpre class=\"wp-block-code\">\u003Ccode>var numbers = GetOneTwoThree();\n\nforeach (var number in numbers) {\n  Console.WriteLine(number);\n  \u002F\u002F Output:\n  \u002F\u002F 1\n  \u002F\u002F 2\n  \u002F\u002F 3\n}\n\nvar doubledNumbers = numbers.Select(num =&gt; num * 2);\n\nforeach (var number in doubledNumbers) {\n  Console.WriteLine(number);\n  \u002F\u002F Output:\n  \u002F\u002F 2\n  \u002F\u002F 4\n  \u002F\u002F 6\n}\n\nvar numberArray = numbers.ToArray();\nConsole.WriteLine(numberArray&#91;0]); \u002F\u002F Output: 1\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C!-- \u002Fwp:code -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>You may notice that we’re iterating over \u003Ccode>numbers\u003C\u002Fcode> multiple times. For a simple iterator like the one I’ve written, that’s technically okay to do. Every time we iterate over \u003Ccode>numbers\u003C\u002Fcode>, it will start over at the beginning of the iterator method and yield all the same values over again. (You can’t expect this from every iterator; more on that later.)\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>\u003Cem>Lazy evaluation\u003C\u002Fem> (the opposite of \u003Cem>eager evaluation\u003C\u002Fem>) is when we wait to execute a piece of code until we absolutely, positively have to. When you call \u003Ccode>GetOneTwoThree()\u003C\u002Fcode>, you’ll get a return value despite the fact that \u003Cem>none of the code in the function has actually been executed yet!\u003C\u002Fem> To prove it, run the following code in LINQPad.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>(If you’re not familiar with \u003Ca href=\"https:\u002F\u002Fwww.linqpad.net\u002F\">LINQPad\u003C\u002Fa>, you should check it out—it’s a powerful, portable C# playground. But the only thing you need to know about it here is that it provides the magic \u003Ccode>.Dump()\u003C\u002Fcode> method, which outputs any value to the \u003Cstrong>Results\u003C\u002Fstrong> pane.)\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:code -->\n\u003Cpre class=\"wp-block-code\">\u003Ccode>bool didTheCodeRun = false;\n\nvoid Main() {\n  var results = RunTheCode();\n  didTheCodeRun.Dump();\n}\n\nIEnumerable&lt;bool&gt; RunTheCode() {\n  didTheCodeRun = true;\n  yield return true;\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C!-- \u002Fwp:code -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>The output of running \u003Ccode>Main()\u003C\u002Fcode> in the above snippet is \u003Ccode>false\u003C\u002Fcode>. That’s right, after we run \u003Ccode>RunTheCode()\u003C\u002Fcode>, which explicitly sets \u003Ccode>didTheCodeRun\u003C\u002Fcode> to \u003Ccode>true\u003C\u002Fcode>, the value of \u003Ccode>didTheCodeRun\u003C\u002Fcode> is still \u003Ccode>false\u003C\u002Fcode>. None of the code in our iterator runs until we start iterating through the \u003Ccode>IEnumerable\u003C\u002Fcode>. It’s lazily evaluated!\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>This may seem counterintuitive, but in a lot of cases it’s a good thing. What if you never end up iterating through the \u003Ccode>IEnumerable\u003C\u002Fcode> at all? Well, that’s a bunch of code the computer didn’t have to execute. Or what if you’re using a LINQ method like \u003Ccode>.First()\u003C\u002Fcode> to try to find a specific item in the collection? You may not need to run \u003Cem>all\u003C\u002Fem> the code in the iterator to get the value you’re looking for–and you won’t. Once \u003Ccode>.First()\u003C\u002Fcode> finds a value that matches the predicate, it will stop iterating. If you’re working with an \u003Ccode>IEnumerable\u003C\u002Fcode> that potentially has thousands of values (or more), you can save a lot of CPU cycles by only iterating as far as you need to.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Of course, it’s all up to you. You can iterate as much or as little as you want. Let’s take a look at some of the ways to do that.\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:code -->\n\u003Cpre class=\"wp-block-code\">\u003Ccode>\u002F\u002F Here's a variable to track execution of code in an iterator\nint lastYielded = -1;\n\n\u002F\u002F Here's an iterator for us to play with\nIEnumerable&lt;int&gt; GetOneToTen() {\n  for (var num = 1; num &lt;= 10; num++) {\n    lastYielded = num;\n    yield return num;\n  }\n}\n\nvoid Main() {\n  var numbers = GetOneToTen();\n  lastYielded.Dump(); \u002F\u002F Output: -1\n\n  \u002F\u002F This gives us an 'instance' of the iteration\n  var enumerator = numbers.GetEnumerator();\n\n  \u002F\u002F This executes the iterator until the first yield return is reached\n  enumerator.MoveNext();\n\n  \u002F\u002F This gives us the current (most recently yielded) value of the iterator\n  enumerator.Current.Dump(); \u002F\u002F Output: 1\n  lastYielded.Dump(); \u002F\u002F Output: 1\n\n  \u002F\u002F This will iterate from 1 to 4, then stop\n  foreach (var num in numbers) {\n    if (num &gt;= 4) {\n      break;\n    }\n  }\n\n  lastYielded.Dump(); \u002F\u002F Output: 4\n\n  \u002F\u002F This will not execute any code in the iterator.\n  \u002F\u002F  LINQ methods are lazily evaluated as well\n  var numbersTimesTwo = numbers.Select(num =&gt; num * 2);\n  lastYielded.Dump(); \u002F\u002F Output: 4\n\n  \u002F\u002F This will force the entire iterator to run, yielding all values\n  var arr = numbers.ToArray();\n  lastYielded.Dump(); \u002F\u002F Output: 10\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C!-- \u002Fwp:code -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>It’s important to point out that many iterators are not as simple as the ones we’ve been using here. An iterator could query a database, for example—including the unfortunate possibility that it might alter data, or that iterating through it twice might yield completely different results! Some tools (like \u003Ca href=\"https:\u002F\u002Fwww.jetbrains.com\u002Fresharper\u002F\">ReSharper\u003C\u002Fa>) will warn you against multiple enumeration for this reason. An iterator is, from one perspective, nothing more than a synchronous method that may not execute its code right away (or at all). And to muddy the waters just a little, not all iterators are synchronous; there’s also an \u003Ccode>IAsyncEnumerable\u003C\u002Fcode> interface (you can loop through it with \u003Ccode>await foreach\u003C\u002Fcode>).\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Most of the time none of this is a problem. Almost all the time you can treat an \u003Ccode>IEnumerable\u003C\u002Fcode> like a list. But I’ve learned the hard way not to rely on this—I once used \u003Ccode>.Select()\u003C\u002Fcode> to transform a collection while using an external variable to track a small piece of state on each iteration, then got very confused when the external variable wasn’t updated later in the method. In the end, I fixed the problem by forcing the iteration to complete with \u003Ccode>.ToArray()\u003C\u002Fcode>. (There are multiple ways to approach something like this, depending on the expected size of the collection and what you’re trying to track.)\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Hopefully this overview helps you avoid running into issues when you create or consume \u003Ccode>IEnumerable\u003C\u002Fcode> in your own code (which is extremely likely, as they’re everywhere in C#). Here are a couple of rules to remember:\u003C\u002Fp>\n\u003C!-- \u002Fwp:paragraph -->\n\n\u003C!-- wp:list {\"ordered\":true} -->\n\u003Col>\u003Cli>Try to avoid side effects when writing an iterator method. Anyone who uses the method should be able to treat it as though it synchronously returns an array. If an iterator changes or executes anything outside of itself, the caller may end up confused. This applies to functions passed to LINQ methods as well, since many of them return \u003Ccode>IEnumerable\u003C\u002Fcode> and are lazily evaluated.\u003C\u002Fli>\u003Cli>You should avoid iterating over the same \u003Ccode>IEnumerable\u003C\u002Fcode> multiple times. If you know you’re going to access every value—for example, if you’re iterating it using a \u003Ccode>foreach\u003C\u002Fcode> with no \u003Ccode>break\u003C\u002Fcode> or \u003Ccode>return\u003C\u002Fcode>—enumerate it sooner rather than later by coercing the \u003Ccode>IEnumerable\u003C\u002Fcode> to a list or array type. Then use that list or array for all future operations.\u003C\u002Fli>\u003C\u002Fol>\n\u003C!-- \u002Fwp:list -->","html","2022-06-15T14:14:15.000Z",{"current":613},"c-ienumerable-yield-return-and-lazy-evaluation",[615,623,628,633],{"_createdAt":616,"_id":617,"_rev":618,"_type":619,"_updatedAt":616,"slug":620,"title":622},"2023-05-23T16:43:21Z","wp-tagcat-net","9HpbCsT2tq0xwozQfkc4ih","blogTag",{"current":621},"net",".net",{"_createdAt":616,"_id":624,"_rev":618,"_type":619,"_updatedAt":616,"slug":625,"title":627},"wp-tagcat-c-3",{"current":626},"c-3","C#",{"_createdAt":616,"_id":629,"_rev":618,"_type":619,"_updatedAt":616,"slug":630,"title":632},"wp-tagcat-code-for-a-living",{"current":631},"code-for-a-living","Code for a Living",{"_createdAt":616,"_id":634,"_rev":618,"_type":619,"_updatedAt":616,"slug":635,"title":79},"wp-tagcat-ienumerable",{"current":636},"ienumerable","C#: IEnumerable, yield return, and lazy evaluation",[639,645,651,657],{"_id":640,"publishedAt":641,"slug":642,"sponsored":12,"title":644},"76c9771b-34e6-4d98-8641-ecefc711f0ef","2026-07-06T15:23:34.559Z",{"_type":10,"current":643},"when-the-sensor-starts-thinking-snortml-agentic-ai-and-the-evolving-architecture-of-intrusion-detection","When the sensor starts thinking: SnortML, agentic AI, and the evolving architecture of intrusion detection",{"_id":646,"publishedAt":647,"slug":648,"sponsored":12,"title":650},"28e560af-f0aa-4d46-bd90-f435ad604aa7","2026-06-26T14:00:27.102Z",{"_type":10,"current":649},"paging-charity-how-can-engineering-leaders-avoid-becoming-bond-villains","Paging Charity! How can engineering leaders avoid becoming Bond villains?",{"_id":652,"publishedAt":653,"slug":654,"sponsored":12,"title":656},"4b22c2a3-3779-4966-93eb-5230391dbdce","2026-06-23T14:08:58.595Z",{"_type":10,"current":655},"your-ai-shipped-a-backend-that-boots-that-is-the-whole-problem","Your AI shipped a backend that boots. That is the whole problem.",{"_id":658,"publishedAt":659,"slug":660,"sponsored":12,"title":662},"5cf362e1-fe7b-45af-b69c-914731c6a052","2026-06-23T14:00:00.000Z",{"_type":10,"current":661},"the-2026-developer-survey-is-now-open-for-human-developers-only","The 2026 Developer Survey is now open (for human developers only)!",{"data":664,"sourceMap":-1},{"count":665,"lastTimestamp":666},13,"2024-01-23T22:08:11Z"]