Question

From what I understand on interfaces, is that in order to use them, you must declare that a class is implementing it by adding the name of the interface after a colon and then, implement the methods.

I'm currently learning about Enumerators, IEnumerable etc. and this got me confused. Here's an example of what I mean:

static IEnumerable<int> Fibs(int fibCount)
{
    for (int i = 0, prevFib = 1, curFib = 1; i < fibCount; i++) {
        yield return prevFib;
        int newFib = prevFib + curFib;
        prevFib = curFib;
        curFib = newFib;
    }
}

IEnumerable seems to be a normal interface as any other, I even checked the method definition and that's what it pretty much seems like.

How is it possible that I can use an interface as a type/return type in the method definition and when/how do I know I should use certain interfaces as types like in this example?

EDIT: I really doubt it has anything to do with the yield keyword since a lot of interfaces are used as properties this way for example in MVC in Models and passed like it to Views. Example:

public IEnumerable<Category> Categories {get;set;}
Was it helpful?

Solution 2

IEnumerable is a special case. The yield return statement instructs the compiler to add the code that implements IEnumerable.
As for your edit:
If an interface is used a the type of a property, any object of a class that implements this interface can be assigned and the property will return an object that implements this interface. In your example, any collection of categories that implements IEnumerable<Category> can be assigned to the property, e.g. a List<Category>. In comparison to using just List<Category>, using the interface allows a wider range of objects to be assigned. The interface defines the abstract requirements that are relevant for the property.

OTHER TIPS

There is extra magic when you use yield keyword, i.e. create an iterator block. The compiler makes a state machine for you.

So C# has a special feature here, and it only has this feature with IEnumerable<>. So it is the C# language which is magical.

The interface IEnumerable<> in itself is a boring ordinary type. No magic in it.

Note: Technically, the yield magic works when the "formal" return type is either IEnumerable<>, IEnumerator<>, IEnumerable, or IEnumerator, but usually you use the first of these. Do not go non-generic, of course.

In C#, interfaces are a special "kind" of type that differs from a class in a few key ways:

  • You cannot include any implementation code of their methods.
  • A single class can "inherit from" (called "implementing") as many as it wants.
  • You cannot instantiate a new instance of an interface.

(There is also some language features associated specifically with interfaces, such as explicit implementations, but those aren't important for this discussion.)

Beyond that, interfaces can be use pretty much anywhere you can use any other reference type. That includes defining fields, properties, or local variables with interface types, or using them as parameter types or return types on methods.

The trick is, if you define a property as, say, an IEnumerable<int>, and you want to set it's value, you cannot do this:

public IEnumerable<int> Numbers { get; set; }
...
this.Numbers = new IEnumerable<int>();

That's an error. You can't create a new instance of an interface, because it's just a "template" -- there's nothing "behind" it to actually do anything. However, you can do this:

public IEnumerable<int> Numbers { get; set; }
...
this.Numbers = new List<int>();

Because List<T> implements IEnumerable<T>, the compiler will automatically do the type conversion to make the assignment work. Any concrete class that implements IEnumerable<> can be assigned to a property of type IEnumerable<>, which is why you see interface property types so often. It allows you to change the underlying concrete type (maybe you want to change List<T> to ObservableCollection<T>, but the users of your class neither know nor care when you do.

The same goes for methods with an interface return type, except there's an extra option here that C# throws in as a bonus:

public IEnumerable<string> GetName()
{
    // this fails.
    return new IEnumerable<string>();

    // this works.
    return new List<String>();

    // this also works because magic!~
    yield return "hello";
    yield return "there";
    yield return "!";
}

That last case is a special form of "syntactic sugar" that C# provides because it's such a common requirement. As other people have mentioned, the compiler specifically looks for yield return statements on methods that return IEnumerable or IEnumerator (both generic and non-generic versions) and does some hefty rewriting of the code.

Behind the scenes, C# is creating a hidden class, which implements IEnumerable<string>, and implementing it's GetEnumerator method to return an IEnumerator<string> object that provides those three string values. That would be a lot of boilerplate code for you to write, although you could certainly write it yourself. In previous versions of C#, there was no yield and you did have to write it yourself.

If you really want to know, you can find the C# equivalent here, among other places. In essence, it takes the method that contains your yield statements and creates a IEnumerator<>.MoveNext method out of it, but turning it into a state machine. It uses the equivalent of labels and goto statements to jump back to the correct place each time a consumer calls MoveNext on the same instance. Also, as I understand it, it does things that you can't actually do in C# (it jumps into and out of loops) but that are legal in IL code, so it's implementation is more efficient that what you could write yourself.

But once you get past the secret cause of the yield keyword, you're still doing the same thing. You're still creating a class, that implements IEnumerable<>, and using that as the return value for your method.

For your specific example, even though the method return type is IEnumerable<int>, the actual return type will be a type that implements IEnumerable<int>. As others have mentioned, the ‘yield return’ results in a type which implements IEnumerable<int>. In this specific case, you don’t know what that type is. When I run this through the debugger and do a result.GetType() (where result is returned from the Fibs method), I see that result is of type <Fibs>d__0, which sounds a little strange to me. So instead of worrying about that strange-sounding type, we can just treat it like an IEnumerable<int>, because all we really want to be able to do is to iterate over it, which is the behavior that IEnumerable<int> exposes.

That’s for your specific example, but the idea is the same elsewhere. By using an interface, you are saying that you don’t care exactly what type is used/returned, as long as it exposes certain behavior or properties. For example if I have an interface IFoo that exposes a method DoSomething(), and I have a method that returns IFoo, then I can return anything that implements IFoo, but no matter what I return, the caller of the method is sure that it can DoSomething() with the object. Similarly, if I have a method that takes an IFoo, then the method is sure that it will be able to DoSomething() with that parameter.

For me interfaces also help me design my classes. For example I write the interface first to determine what’s important for the class to be able to do and have, and then I create the concrete class that implements that interface. And when I’m writing code that uses the class, I ask for the interface rather than the concrete type.

And there’s all sorts of other reasons to use interfaces, for example to create mocks for testing, for dependency injection, for fluent API design, and probably a hundred other reasons.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top