2

the foreach in the following C# code works, even if I doesn't do Days:IEnumerable. So this looks like a kind of duck typing. Why is this possible? I thought Days:IEnumerable is obligatory.

using System;
using System.Collections;
using System.Collections.Generic;



namespace ConsoleApplication1
{
    // works even if I does not
    // Days:IEnumerable
    public class Days
    {
        string[] tage = { "mon", "tue", "wed", "thu", "fri" };
        public IEnumerator GetEnumerator()
        {
            for (int i = 0; i < tage.Length; i++)
                yield return tage[i];
        }
    } 


    class Program
    {
        static void Main(string[] args)
        {
            Days daylist = new Days();
            foreach (string s in daylist) { Console.WriteLine(s); } 



        }

    }
}
knowledge
  • 941
  • 1
  • 11
  • 26
  • Implementing `IEnumerable` is not required, `foreach` just requires the iteratee has a `GetEnumerator` method which returns a value of some type with the required members. – Lee Jun 02 '17 at 13:41
  • https://stackoverflow.com/q/6368967/3140 explains why it's duck typed -- TLDR: because it allows the compiler to avoid boxing sometimes – Jacob Krall Jun 02 '17 at 13:42
  • See [this](https://stackoverflow.com/a/44782/2846483). – dymanoid Jun 02 '17 at 13:43
  • I learned that duck typing in general is not so good. Why it is supported in C#? Is avoid boxing sometime such a big advantage? – knowledge Jun 02 '17 at 14:05

3 Answers3

1

According to the C# Language Specification, section 8.8.4 "The foreach statement" (https://msdn.microsoft.com/en-us/library/aa664754(v=vs.71).aspx)

The type of the expression of a foreach statement must be a collection type (as defined below) [...]

[...] A type C is said to be a collection type if it implements the System.Collections.IEnumerable interface or implements the collection pattern by meeting all of the following criteria:

C contains a public instance method with the signature GetEnumerator() that returns a struct-type, class-type, or interface-type, which is called E in the following text.

E contains a public instance method with the signature MoveNext() and the return type bool.

E contains a public instance property named Current that permits reading the current value. The type of this property is said to be the element type of the collection type.

So, not only your class is not obliged to implement IEnumerable, but also, your GetEnumerator() method is not obliged to return an IEnumerator. All that the returned type has to do is implement methods with the right signatures.

Community
  • 1
  • 1
Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
0

No, you don't need that interface. If you try to do a foreach on a non-enumerable data type, like bool, you get this error:

CS1579: foreach statement cannot operate on variables of type 'bool' because 'bool' does not contain a public definition for 'GetEnumerator'

So the requirement is simple: the class must have a public GetEnumerator method.

Then the next error tells us more:

CS0202: foreach requires that the return type 'void' of 'Program.C.GetEnumerator()' must have a suitable public MoveNext method and public Current property

So this is a valid implementation to use foreach, while it is unlike the call will ever end ;)

public class E
{
    public bool MoveNext() { return true; }

    public object Current { get { return null; } }
}

public class C
{
    public E GetEnumerator()
    {
        return new E();
    }
}
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
0

Like you said, this benefits from Duck Typing the interface is not mandatory. See MSDN thorough article on the topic. https://msdn.microsoft.com/en-us/magazine/mt797654.aspx

Roberto Hernandez
  • 2,397
  • 1
  • 16
  • 18