Saturday, April 28, 2012

Behavior flexability of IEnumerable<T>

One of the ways to de-couple your code from specific types, what I'll call hard types, is to use the IEnumerable. I'll call this a soft type, and it can be used many places you might otherwise find a read-only version of IList. Rather than pass around an IList, which is an object with state and memory, if you can pass around an IEnumerable, you should.

I want to read strings from a file, or records from a database table. Rather that loop through and create a list of strings/records, I’ll just return an IEnumerable.

First, create a text file, and save it UTF-8 encoded.

Copy this code (sans line numbers)

1   using System;
2 usingSystem.Collections.Generic;
3 usingSystem.Text;
4 using System.IO;
5
6 namespace DonCode
7 {
8 public delegate IEnumerable<string> GetStrings(StreamReader sr);
9 public class Program
10 {
11 static void Main(string[] args)
12 {
13 Program p = new Program();
14 using (StreamReader sr = new StreamReader("file.txt", Encoding.UTF8)) {
15 GetStrings ReadFileStrings = null;
16 ReadFileStrings = p.ReadFileList;
17 // ReadFileStrings = p.ReadFileNoList;
18 IEnumerable<string> items = ReadFileStrings(sr);
19 foreach (string line in items) {
20 Console.WriteLine(line);
21 }
22 }
23 Console.ReadLine();
24 }
25 public IEnumerable<string> ReadFileNoList(StreamReader r)
26 {
27 string line = r.ReadLine();
28 while (!String.IsNullOrEmpty(line)) {
29 yield return line;
30 line = r.ReadLine();
31 }
32 }
33 public IEnumerable<string> ReadFileList(StreamReader r)
34 {
35 IList<string> list = new List<string>();
36 string line = r.ReadLine();
37 while (!String.IsNullOrEmpty(line)) {
38 list.Add(line);
39 line = r.ReadLine();
40 }
41 return list;
42 }
43 }
44 }


To see this work, put a break point here:


15                   GetStrings ReadFileStrings = null;



Run your code in the debugger (F5) and step through it using F11.



Switch comments:




16                   //ReadFileStrings = p.ReadFileList;
17 ReadFileStrings = p.ReadFileNoList;



Run the code in the debugger (F5) and step through it with F11.



The results are the same but the behavior is different. We went from creating a list and running a pair of loops, to running a single loop, and creating no list. The client code was not changed, nor was the contract altered. By using IEnumerable, we were able to be more flexible in our implementation but still deliver on our contract.