Implementing IEnumerable<T>10:29 with Jeremy McLain
All collections implement IEnumerable and it's what makes it possible to loop through a collection using foreach.
In the previous video, we decided that it would be nice to be able to work 0:00 with multiple collections of objects as if they're a single collection. 0:03 We'll encapsulate this functionality in a class named EnumerableCompositor. 0:07 This may sound like a funny name right now, but in a moment, 0:12 you'll see why I've decided to name it that. 0:15 We'll add a new file to our project. 0:17 Now, because this class is meant to be reusable across any project, 0:20 I'd normally put this in a separate class library. 0:25 But because this is just a demonstration on how to use generics to write 0:28 collections, we'll keep all of our code in the generics demo project for now. 0:31 We can always move it to a different library later if we want it. 0:36 So I would just right-click on GenericsDemo > Add > New Item. 0:40 Chang the class name to EnumerableCompositor. 0:46 Now before we start coding this class, 0:56 let's think a bit about what we'd like to be able to do first. 0:59 The EnumerableCompositor will be composed of many collections. 1:02 But we'll be able to loop through the items contained in them 1:08 as if they're one collection. 1:11 Collections that can be looped through like this are said to be enumerable. 1:13 That's why we've named the class EnumerableCompositor, 1:17 it's composed of enumerables. 1:20 So let's see how we'd create a new enumerable compositor over here in Main. 1:23 Here we'd say var ec = new 1:29 EnumberableCompositor. 1:33 We'd specify the type of item we want to store in the collection here. 1:39 In this case it's int. 1:45 And now we can list all of the collections here. 1:47 So we'll just say list1, list2, set1 and array1. 1:50 We'll also want to be able to loop through all of the items 1:57 in the enumerable compositor. 2:00 So just like we had our for loops before, 2:02 we'll have an integer to keep track of all of the odd numbers that we encounter. 2:05 So we'll say init numodd = 0. 2:10 And we'll write a foreach loop. 2:15 Say var value in and this time we're just gonna loop 2:19 through the enumerable compositor. 2:23 And if the value is odd, 2:27 Then we will increment numOdd. 2:36 Or even better, 2:42 we'd like to be able to write this as a single link statement like this. 2:44 We can change all of these to just EC.count. 2:49 Notice that we have these red squiggly lines here. 2:56 This is because we haven't actually implemented the enumerable compositor yet. 2:59 We're just showing here what we'd like to be able to do. 3:03 By the end of this workshop, 3:07 we'll even see how we can make this code even simpler. 3:08 Notice that enumerable compositor takes a generic type parameter here. 3:12 Here we're specifying that we want in enumerable compositor to be a collection 3:17 of integers. 3:21 Let's see how to turn enumerable compositor into a generic class. 3:23 For now, let's comment out this code since it doesn't compile yet. 3:27 We can make enumerable compositor a generic class by adding opening and 3:34 closing angle brackets here after the class name. 3:38 Now we need to decide what to name the generic type parameter. 3:43 By convention, generic parameter start with a capital letter. 3:47 In most cases, if there's only one parameter, the capital letter T is used. 3:51 We can have as many type parameters as we'd like though. 3:57 It's actually very rare to have more than three or four. 4:00 If there are two or more parameters, then we should have more descriptive names. 4:04 They typically start with a capital T though. 4:08 For example, the type parameter for the key of a dictionary is named TKey. 4:11 And the type parameter for the value is named TValue. 4:16 Because we only have a single parameter, we'll just use T here. 4:21 This T is a placeholder for 4:26 the actual type that will be specified when the class is instantiated. 4:29 We can now use T anywhere we need to refer to the type of the item 4:33 contained in the collection. 4:37 The rule of T will become more clear as we continue. 4:38 Now, we get to the part that makes this class a collection. 4:41 All collection types in .NET implement the I Enumerable interface. 4:45 In C#, for each loops can only be used to loop through classes that implement 4:50 the I Enumerable interface. 4:54 We want to be able to loop through all the items in our collection. 4:56 So, we'll need to implement the IEnumerable interface here to. 5:00 The IEnumerable interface is a generic interface. 5:04 Yes, interfaces can be generic too. 5:08 The type parameter of the I enumerable interface is the type of the object 5:12 contained in the collection. 5:16 In our case, that will also be T. 5:18 So we can just type T here. 5:21 To make some room, 5:24 I'll delete all of these using directives that we're not using. 5:25 Now we need to implement the IEnumerable interface. 5:29 In fact, that's what this red squiggly line is telling us. 5:33 We haven't yet implemented the interface. 5:37 So if we tried to compile this we'd get a compilation error. 5:39 We can have Visual Studio generate the code to implement the interface for us. 5:44 So if we hit control with our cursor on the red squiggly area, 5:49 we'll see some options for how to fix this compilation error. 5:56 As you can see here, there are two options. 6:00 If I move the cursor over them, it shows me the code it will generate. 6:03 Notice that the second option is to implement the interface explicitly. 6:07 The names of the methods and properties of an explicitly implemented interface 6:13 are prefixed by the name of the interface. 6:17 Those methods and properties are only accessible. 6:23 When the object is explicitly cast to that specific interface. 6:26 There are a number of reasons to do this, but in our case we want the first option. 6:31 We'll talk more about explicitly implemented interfaces in a bit, to have 6:37 the visual studio generate the code, I'll just click, Implement interface. 6:41 If you're following along in a different editor just pause the video here and 6:46 copy the code from my screen. 6:51 Two methods were added for us both of them are named GetEnumerator. 6:53 One returns IEnumerator of T, and the other just returns IEnumerator. 6:58 Also notice that the second one is an explicitly implemented interface method. 7:05 We know that because it's prefixed by a IEnumerable dot. 7:12 And it doesn't have an access modifier such as public. 7:18 So what's going on here? 7:23 Well, actually, we've just implemented two interfaces, 7:25 one for IEnumerable of T and the other for IEnumerable. 7:30 One is generic and the other is not. 7:35 After clicking on IEnumerable of T, 7:37 we can hit Alt+F12 on the keyboard to peek at its definition. 7:40 Here's the definition for IEnumerable of T and here's the GetEnumerator method. 7:48 This is the one that returns IEnumerator of T, but 7:54 this Interface also inherits from the non-generic IEnumerable. 7:59 Which is a completely different interface. 8:05 We can peek at its definition by clicking on it and 8:08 then hitting Alt + F12 on the keyboard. 8:12 Now we're looking at the interface definition for 8:16 the non-generic IEnumerable. 8:18 It also specifies a method named GetEnumerator 8:21 only this one returns the non-generic IEnumerator. 8:25 When we implement an interface, 8:31 we must also implement any interfaces that it inherits. 8:33 It's as if we had both IEnumerable of T and I enumerator up here 8:37 even though these two interfaces are named the same they're not the same interface. 8:54 They both specify that we must have a method name to get in numerator. 8:59 However, they have different return types. 9:03 In C#, a class can't have two methods with the same name 9:06 if they have different return types. 9:11 So we need to differentiate them. 9:13 We can do this by adding IEnumerable dot to the GetEnumerator method 9:15 that's specified by the non-generic IEnumerable interface. 9:19 This essentially hides this method from users of the class. 9:24 And that's why there's no public access modifier here. 9:28 The explicitly implemented GetEnumerator method can still be accessed if the object 9:31 is explicitly cast to the non-generic IEnumerable interface first. 9:36 This is only one reason to explicitly implement an interface method. 9:42 Explicitly implementing interfaces is actually a fairly advanced and 9:46 rare use of interfaces. 9:51 So we won't go much more into it right now. 9:53 It just so happens that in order to implement the IEnumerable of T interface, 9:56 you must also explicitly implement the get numerator method 10:01 of the non-generic I numerable interface. 10:04 I've included more information about explicit interface implementations 10:07 in the teacher's notes of this video if you'd like to learn more. 10:11 We still need to write the code for these methods. 10:15 But this is this far as will go with implementing these right now. 10:17 Before we can finish writing them we need to have some way to add and 10:21 store the items contained in the collection. 10:25 We'll do that next. 10:27
You need to sign up for Treehouse in order to download course files.Sign up