Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. What's the point of IEnumerable and IEnumerable<T>?

What's the point of IEnumerable and IEnumerable<T>?

Scheduled Pinned Locked Moved C#
questioncsharpjavavisual-studiocom
11 Posts 2 Posters 4 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • T Taka Muraoka

    So, I found out by accident that a class doesn't actually need to implement IEnumerable to be able to foreach over it. This works just fine (VS2005):

    class X
    {
    private List<int> mList = new List<int>() ;
    public X() { mList.Add(1) ; mList.Add(2) ; mList.Add(3) ; }
    public IEnumerator GetEnumerator() { return mList.GetEnumerator() ; }
    }

    X x = new X() ;
    foreach ( int n in x )
    System.Console.WriteLine( n ) ;

    MSDN confirms[^] this behaviour:

    Evaluates to a type that implements IEnumerable or a type that declares a GetEnumerator method.

    Although this[^] suggests that it's a C# thing only.

    The reason for implementing IEnumurable therefore, is (a) to identify the class as enumerable/foreach-able, and (b) to provide a language independant implementation that will work in other languages that don't provide the C# foreach performance "hack".

    But even given that, it still begs the question: why did the C# team feel the need for foreach to accept objects that don't implement IEnumerable? But it gets worse when using generics. Deriving X from IEnumerable<T> forces you to implement both the generic (expected) and non-generic (huh?!) version of GetEnumerator():

    class X : IEnumerable<int>
    {
    private List<int> mList = new List<int>() ;
    public X() { mList.Add(1) ; mList.Add(2) ; mList.Add(3) ; }
    IEnumerator<int> IEnumerable<int>.GetEnumerator() { return mList.GetEnumerator() ; }
    public IEnumerator GetEnumerator() { return mList.GetEnumerator() ; }
    }

    X x = new X() ;
    foreach ( int n in x )
    System.Console.WriteLine( n ) ;

    Except that when stepping through the debugger, foreach uses the *non-generic* GetEnumerator(). So why even bother having the generic one? I'm pretty new to C# but this seems kinda dumb. Am I missing something?


    I enjoy occasionally wandering around randomly, and often find that when I do so, I get to where I wanted to be [

    S Offline
    S Offline
    Scott Dorman
    wrote on last edited by
    #2

    Taka Muraoka wrote:

    I'm pretty new to C# but this seems kinda dumb. Am I missing something?

    Not really. The biggest thing you're missing is the fact that when .NET 1.0 was first released there were no real internal guidelines being followed by the team. As the outside world started using the Framework more and the team got smarter about what they were trying to do, guidelines started to form and best practices started to take shape. The fact that C# implements a foreach iterator was done mostly as a convenience for the developer more than anything else. There is always an argument over which one provides more performance and there are cases where one wins over the other. The reality is that any type which should support "foreachable" behavior should implement the interface. The rules for implementing the generic IEnumerable<T> exist that way because IEnumerable<T> implements the IEnumerable interface.

    Scott. —In just two days, tomorrow will be yesterday. —Hey, hey, hey. Don't be mean. We don't have to be mean because, remember, no matter where you go, there you are. - Buckaroo Banzai


    [Forum Guidelines] [Articles] [Blog]

    T 1 Reply Last reply
    0
    • S Scott Dorman

      Taka Muraoka wrote:

      I'm pretty new to C# but this seems kinda dumb. Am I missing something?

      Not really. The biggest thing you're missing is the fact that when .NET 1.0 was first released there were no real internal guidelines being followed by the team. As the outside world started using the Framework more and the team got smarter about what they were trying to do, guidelines started to form and best practices started to take shape. The fact that C# implements a foreach iterator was done mostly as a convenience for the developer more than anything else. There is always an argument over which one provides more performance and there are cases where one wins over the other. The reality is that any type which should support "foreachable" behavior should implement the interface. The rules for implementing the generic IEnumerable<T> exist that way because IEnumerable<T> implements the IEnumerable interface.

      Scott. —In just two days, tomorrow will be yesterday. —Hey, hey, hey. Don't be mean. We don't have to be mean because, remember, no matter where you go, there you are. - Buckaroo Banzai


      [Forum Guidelines] [Articles] [Blog]

      T Offline
      T Offline
      Taka Muraoka
      wrote on last edited by
      #3

      Thanks for the info.

      Scott Dorman wrote:

      The fact that C# implements a foreach iterator was done mostly as a convenience for the developer more than anything else.

      Given that this kind of feature is pretty standard in modern languages, I'd say it's perhaps a little more than a developers' convenience. But it wouldn't even occur to me to do it any other way than via the IEnumerable interface. For someone to say "we'll have it accept anything that has a GetEnumerator() method" suggests to me someone who doesn't really know what they're doing :|

      Scott Dorman wrote:

      The rules for implementing the generic IEnumerable exist that way because IEnumerable implements the IEnumerable interface.

      This is the one that really baffles me. The generic GetEnumerator() doesn't appear to get called so I don't quite see the point of having IEnumerable<T> at all. You might as well just derive from IEnumerable :shrug:


      I enjoy occasionally wandering around randomly, and often find that when I do so, I get to where I wanted to be [^]. Awasu 2.3.2 [^]: A free RSS/Atom feed reader with support for Code Project.

      S 1 Reply Last reply
      0
      • T Taka Muraoka

        Thanks for the info.

        Scott Dorman wrote:

        The fact that C# implements a foreach iterator was done mostly as a convenience for the developer more than anything else.

        Given that this kind of feature is pretty standard in modern languages, I'd say it's perhaps a little more than a developers' convenience. But it wouldn't even occur to me to do it any other way than via the IEnumerable interface. For someone to say "we'll have it accept anything that has a GetEnumerator() method" suggests to me someone who doesn't really know what they're doing :|

        Scott Dorman wrote:

        The rules for implementing the generic IEnumerable exist that way because IEnumerable implements the IEnumerable interface.

        This is the one that really baffles me. The generic GetEnumerator() doesn't appear to get called so I don't quite see the point of having IEnumerable<T> at all. You might as well just derive from IEnumerable :shrug:


        I enjoy occasionally wandering around randomly, and often find that when I do so, I get to where I wanted to be [^]. Awasu 2.3.2 [^]: A free RSS/Atom feed reader with support for Code Project.

        S Offline
        S Offline
        Scott Dorman
        wrote on last edited by
        #4

        Taka Muraoka wrote:

        Given that this kind of feature is pretty standard in modern languages, I'd say it's perhaps a little more than a developers' convenience.

        Just because it's in most modern languages only means it's a popular feature. I think what you will find behind the scenes in the IL is pretty much the same calls if you used a class that implemented IEnumerable or one that didn't. The code generated is still a call to GetEnumerator and then calls to GetCurrent. The fact that foreach works without IEnumerable was probably done in order to provide the most flexibility. I wouldn't necessary say that it suggests "someone who doesn't really know what they are doing".

        Taka Muraoka wrote:

        This is the one that really baffles me. The generic GetEnumerator() doesn't appear to get called so I don't quite see the point of having IEnumerable at all.

        What are you using to determine that the generic GetEnumerator isn't being called?

        Scott. —In just two days, tomorrow will be yesterday. —Hey, hey, hey. Don't be mean. We don't have to be mean because, remember, no matter where you go, there you are. - Buckaroo Banzai


        [Forum Guidelines] [Articles] [Blog]

        T 1 Reply Last reply
        0
        • S Scott Dorman

          Taka Muraoka wrote:

          Given that this kind of feature is pretty standard in modern languages, I'd say it's perhaps a little more than a developers' convenience.

          Just because it's in most modern languages only means it's a popular feature. I think what you will find behind the scenes in the IL is pretty much the same calls if you used a class that implemented IEnumerable or one that didn't. The code generated is still a call to GetEnumerator and then calls to GetCurrent. The fact that foreach works without IEnumerable was probably done in order to provide the most flexibility. I wouldn't necessary say that it suggests "someone who doesn't really know what they are doing".

          Taka Muraoka wrote:

          This is the one that really baffles me. The generic GetEnumerator() doesn't appear to get called so I don't quite see the point of having IEnumerable at all.

          What are you using to determine that the generic GetEnumerator isn't being called?

          Scott. —In just two days, tomorrow will be yesterday. —Hey, hey, hey. Don't be mean. We don't have to be mean because, remember, no matter where you go, there you are. - Buckaroo Banzai


          [Forum Guidelines] [Articles] [Blog]

          T Offline
          T Offline
          Taka Muraoka
          wrote on last edited by
          #5

          Scott Dorman wrote:

          I think what you will find behind the scenes in the IL is pretty much the same calls if you used a class that implemented IEnumerable or one that didn't. The code generated is still a call to GetEnumerator and then calls to GetCurrent.

          I haven't really bothered looking too deeply into IL but as a hard-core C++ guy, it certainly doesn't make any sense.

          Scott Dorman wrote:

          The fact that foreach works without IEnumerable was probably done in order to provide the most flexibility. I wouldn't necessary say that it suggests "someone who doesn't really know what they are doing".

          Well, it doesn't really make any sense from a general OO point of view, either :-)

          Scott Dorman wrote:

          What are you using to determine that the generic GetEnumerator isn't being called?

          Just stepping through in the debugger. Modifying it in the code sample in my OP to return null seems to have no ill effects.


          I enjoy occasionally wandering around randomly, and often find that when I do so, I get to where I wanted to be [^]. Awasu 2.3.2 [^]: A free RSS/Atom feed reader with support for Code Project.

          S 1 Reply Last reply
          0
          • T Taka Muraoka

            Scott Dorman wrote:

            I think what you will find behind the scenes in the IL is pretty much the same calls if you used a class that implemented IEnumerable or one that didn't. The code generated is still a call to GetEnumerator and then calls to GetCurrent.

            I haven't really bothered looking too deeply into IL but as a hard-core C++ guy, it certainly doesn't make any sense.

            Scott Dorman wrote:

            The fact that foreach works without IEnumerable was probably done in order to provide the most flexibility. I wouldn't necessary say that it suggests "someone who doesn't really know what they are doing".

            Well, it doesn't really make any sense from a general OO point of view, either :-)

            Scott Dorman wrote:

            What are you using to determine that the generic GetEnumerator isn't being called?

            Just stepping through in the debugger. Modifying it in the code sample in my OP to return null seems to have no ill effects.


            I enjoy occasionally wandering around randomly, and often find that when I do so, I get to where I wanted to be [^]. Awasu 2.3.2 [^]: A free RSS/Atom feed reader with support for Code Project.

            S Offline
            S Offline
            Scott Dorman
            wrote on last edited by
            #6

            Taka Muraoka wrote:

            I haven't really bothered looking too deeply into IL but as a hard-core C++ guy, it certainly doesn't make any sense.

            The IL is much closer to reading assembly than anything else, but it can sometimes reveal some interesting results.

            Taka Muraoka wrote:

            Well, it doesn't really make any sense from a general OO point of view, either

            I can't disagree with this. I was only saying why I think it might have been done that way.

            Taka Muraoka wrote:

            Just stepping through in the debugger. Modifying it in the code sample in my OP to return null seems to have no ill effects.

            What you are seeing is actually correct behavior for your code. You have explicitly defined the IEnumerable<T>.GetEnumerator() and implicitly defining the IEnumerable.GetEnumerator(). Since IEnumerable.GetEnumerator() is public (and implicit) this code gets called. There are a few ways to change this so it calls the generic enumerator: 1: Leave your class X defined as is, and change the call in the foreach loop to read

            foreach (int n in (IEnumerable<int>)x)
            {
            System.Console.WriteLine(n);
            }

            2: Leave the foreach call as is, and change X to read:

            class X : IEnumerable<int>
            {
            private List<int> mList = new List<int>();
            public X()
            {
            mList.Add(1); mList.Add(2); mList.Add(3);
            }

            IEnumerator<int> IEnumerable<int>.GetEnumerator()
            {
                return mList.GetEnumerator();
            }
            

            IEnumerator IEnumerable.GetEnumerator()
            {
            return mList.GetEnumerator();
            }
            }

            or

            class X : IEnumerable<int>
            {
            private List<int> mList = new List<int>();
            public X()
            {
            mList.Add(1); mList.Add(2); mList.Add(3);
            }

            public IEnumerator<int> GetEnumerator()
            {
                return mList.GetEnumerator();
            }
            

            IEnumerator IEnumerable.GetEnumerator()
            {
            return mList.GetEnumerator();
            }
            }

            Scott. —In just two days, tomorrow will be yesterday. —Hey, hey, hey. Don't be mean. We don't have to be mean because, remember, no matter where you go, there you are. - Buckaroo Banzai


            [

            T 1 Reply Last reply
            0
            • S Scott Dorman

              Taka Muraoka wrote:

              I haven't really bothered looking too deeply into IL but as a hard-core C++ guy, it certainly doesn't make any sense.

              The IL is much closer to reading assembly than anything else, but it can sometimes reveal some interesting results.

              Taka Muraoka wrote:

              Well, it doesn't really make any sense from a general OO point of view, either

              I can't disagree with this. I was only saying why I think it might have been done that way.

              Taka Muraoka wrote:

              Just stepping through in the debugger. Modifying it in the code sample in my OP to return null seems to have no ill effects.

              What you are seeing is actually correct behavior for your code. You have explicitly defined the IEnumerable<T>.GetEnumerator() and implicitly defining the IEnumerable.GetEnumerator(). Since IEnumerable.GetEnumerator() is public (and implicit) this code gets called. There are a few ways to change this so it calls the generic enumerator: 1: Leave your class X defined as is, and change the call in the foreach loop to read

              foreach (int n in (IEnumerable<int>)x)
              {
              System.Console.WriteLine(n);
              }

              2: Leave the foreach call as is, and change X to read:

              class X : IEnumerable<int>
              {
              private List<int> mList = new List<int>();
              public X()
              {
              mList.Add(1); mList.Add(2); mList.Add(3);
              }

              IEnumerator<int> IEnumerable<int>.GetEnumerator()
              {
                  return mList.GetEnumerator();
              }
              

              IEnumerator IEnumerable.GetEnumerator()
              {
              return mList.GetEnumerator();
              }
              }

              or

              class X : IEnumerable<int>
              {
              private List<int> mList = new List<int>();
              public X()
              {
              mList.Add(1); mList.Add(2); mList.Add(3);
              }

              public IEnumerator<int> GetEnumerator()
              {
                  return mList.GetEnumerator();
              }
              

              IEnumerator IEnumerable.GetEnumerator()
              {
              return mList.GetEnumerator();
              }
              }

              Scott. —In just two days, tomorrow will be yesterday. —Hey, hey, hey. Don't be mean. We don't have to be mean because, remember, no matter where you go, there you are. - Buckaroo Banzai


              [

              T Offline
              T Offline
              Taka Muraoka
              wrote on last edited by
              #7

              Scott Dorman wrote:

              1: Leave your class X defined as is, and change the call in the foreach loop to read

              foreach (int n in (IEnumerable<int>x)
              {
              System.Console.WriteLine(n);
              }

              This is obviously kinda ugly. The intent of deriving from IEnumerable<T> is to enforce type-safety and I was hoping that the compiler would be smart enough to realize that X can only be iterated over using int's and nothing else.

              Scott Dorman wrote:

              2: Leave the foreach call as is, and change X to read:

              Both these work but there are still some oddities. The following code compiles but throws a cast exception at runtime, which is what one (or at least I) would expect:

              class X
              {
              private List<int> mList = new List<int>() ;
              public X() { mList.Add(1) ; mList.Add(2) ; mList.Add(3) ; }
              public IEnumerator GetEnumerator() { return mList.GetEnumerator() ; }
              }

              foreach ( X n in x ) // nb: iterate using an X, not an int
              System.Console.WriteLine( n ) ;

              But using the modified foreach with either of your #2 code samples doesn't even compile ("Cannot convert int to X"). So it seems that there's now no way to get into the non-generic GetEnumerator() :rolleyes: I don't particularly want to but it annoys me that I have to define this method but it never gets used :-) So, it looks like foreach is ignoring whether the object it's iterating over derives from IEnumerable or IEnumerable<T> and just looks for a public GetEnumerator() method (which is kinda lame). But then how does it know which GetEnumerator() to use in your 2a code? Both of them are public in their respective interfaces, and yes, I tried reversing the order they are defined in :-) I was kinda hoping the compiler could infer which one to use by the type being used in the foreach statement but for some reason, it's only allowing the generic one to be used. Oh well, I guess we can just chalk this one up to C# evolving on the fly. Thanks for your help.


              I enjoy occasionally wandering around randomly, and often find that when I do so, I get to where I wanted to be [^]. Awasu 2.3.2 [^]: A free RSS/Ato

              S 1 Reply Last reply
              0
              • T Taka Muraoka

                Scott Dorman wrote:

                1: Leave your class X defined as is, and change the call in the foreach loop to read

                foreach (int n in (IEnumerable<int>x)
                {
                System.Console.WriteLine(n);
                }

                This is obviously kinda ugly. The intent of deriving from IEnumerable<T> is to enforce type-safety and I was hoping that the compiler would be smart enough to realize that X can only be iterated over using int's and nothing else.

                Scott Dorman wrote:

                2: Leave the foreach call as is, and change X to read:

                Both these work but there are still some oddities. The following code compiles but throws a cast exception at runtime, which is what one (or at least I) would expect:

                class X
                {
                private List<int> mList = new List<int>() ;
                public X() { mList.Add(1) ; mList.Add(2) ; mList.Add(3) ; }
                public IEnumerator GetEnumerator() { return mList.GetEnumerator() ; }
                }

                foreach ( X n in x ) // nb: iterate using an X, not an int
                System.Console.WriteLine( n ) ;

                But using the modified foreach with either of your #2 code samples doesn't even compile ("Cannot convert int to X"). So it seems that there's now no way to get into the non-generic GetEnumerator() :rolleyes: I don't particularly want to but it annoys me that I have to define this method but it never gets used :-) So, it looks like foreach is ignoring whether the object it's iterating over derives from IEnumerable or IEnumerable<T> and just looks for a public GetEnumerator() method (which is kinda lame). But then how does it know which GetEnumerator() to use in your 2a code? Both of them are public in their respective interfaces, and yes, I tried reversing the order they are defined in :-) I was kinda hoping the compiler could infer which one to use by the type being used in the foreach statement but for some reason, it's only allowing the generic one to be used. Oh well, I guess we can just chalk this one up to C# evolving on the fly. Thanks for your help.


                I enjoy occasionally wandering around randomly, and often find that when I do so, I get to where I wanted to be [^]. Awasu 2.3.2 [^]: A free RSS/Ato

                S Offline
                S Offline
                Scott Dorman
                wrote on last edited by
                #8

                Taka Muraoka wrote:

                This is obviously kinda ugly. The intent of deriving from IEnumerable is to enforce type-safety and I was hoping that the compiler would be smart enough to realize that X can only be iterated over using int's and nothing else.

                I can't disagree with you here. I pointed this out as an option...I never said it was a good one.

                Taka Muraoka wrote:

                Both these work but there are still some oddities.

                What are the oddities?

                Taka Muraoka wrote:

                But using the modified foreach with either of your #2 code samples doesn't even compile ("Cannot convert int to X"). So it seems that there's now no way to get into the non-generic GetEnumerator()

                That is correct. By deriving X from IEnumerable<int> you are effectively telling the type system that when you enumerate over X (by calling GetEnumerator) that you are returning a type of int not X. Isn't that the point of providing the strongly typed version? You (and the compiler) know at compile time what the data type will be, so there is no need for it to call the non-typed version.

                Taka Muraoka wrote:

                I don't particularly want to but it annoys me that I have to define this method but it never gets used

                True, but that's how interfaces work. If you derive from an interface you have to implement all of the methods defined in the contract.

                Taka Muraoka wrote:

                So, it looks like foreach is ignoring whether the object it's iterating over derives from IEnumerable or IEnumerable<T> and just looks for a public GetEnumerator() method (which is kinda lame). But then how does it know which GetEnumerator() to use in your 2a code? Both of them are public in their respective interfaces, and yes, I tried reversing the order they are defined in I was kinda hoping the compiler could infer which one to use by the type being used in the foreach statement but for some reason, it's only allowing the generic one to be used.

                I'm not sure I would say that it's ignoring the interfaces. It knows to use the generic GetEnumerator in the 2a example based on the type defined in the foreach loop. The key is which method is defined explicitly or implicitly. In my 2a example, they are both explicitly defined (notice there is not a public keyword). These m

                T 1 Reply Last reply
                0
                • S Scott Dorman

                  Taka Muraoka wrote:

                  This is obviously kinda ugly. The intent of deriving from IEnumerable is to enforce type-safety and I was hoping that the compiler would be smart enough to realize that X can only be iterated over using int's and nothing else.

                  I can't disagree with you here. I pointed this out as an option...I never said it was a good one.

                  Taka Muraoka wrote:

                  Both these work but there are still some oddities.

                  What are the oddities?

                  Taka Muraoka wrote:

                  But using the modified foreach with either of your #2 code samples doesn't even compile ("Cannot convert int to X"). So it seems that there's now no way to get into the non-generic GetEnumerator()

                  That is correct. By deriving X from IEnumerable<int> you are effectively telling the type system that when you enumerate over X (by calling GetEnumerator) that you are returning a type of int not X. Isn't that the point of providing the strongly typed version? You (and the compiler) know at compile time what the data type will be, so there is no need for it to call the non-typed version.

                  Taka Muraoka wrote:

                  I don't particularly want to but it annoys me that I have to define this method but it never gets used

                  True, but that's how interfaces work. If you derive from an interface you have to implement all of the methods defined in the contract.

                  Taka Muraoka wrote:

                  So, it looks like foreach is ignoring whether the object it's iterating over derives from IEnumerable or IEnumerable<T> and just looks for a public GetEnumerator() method (which is kinda lame). But then how does it know which GetEnumerator() to use in your 2a code? Both of them are public in their respective interfaces, and yes, I tried reversing the order they are defined in I was kinda hoping the compiler could infer which one to use by the type being used in the foreach statement but for some reason, it's only allowing the generic one to be used.

                  I'm not sure I would say that it's ignoring the interfaces. It knows to use the generic GetEnumerator in the 2a example based on the type defined in the foreach loop. The key is which method is defined explicitly or implicitly. In my 2a example, they are both explicitly defined (notice there is not a public keyword). These m

                  T Offline
                  T Offline
                  Taka Muraoka
                  wrote on last edited by
                  #9

                  Scott Dorman wrote:

                  By deriving X from IEnumerable you are effectively telling the type system that when you enumerate over X (by calling GetEnumerator) that you are returning a type of int not X. Isn't that the point of providing the strongly typed version? You (and the compiler) know at compile time what the data type will be, so there is no need for it to call the non-typed version.

                  That's right, this is the behaviour I want but I was playing with the code you gave because I wanted to get a better handle on what was really going on. I was wondering if the non-generic GetEnumerator() would get called if I tried to iterate using something other than int's. But see my next comment...

                  Scott Dorman wrote:

                  If you derive from an interface you have to implement all of the methods defined in the contract.

                  Maybe the problem is because I'm still thinking in C++. If I write a C++ class that derives from an interface that in turn derives from another interface, my class has to effectively implement two interfaces. This is reinforced by the fact that I have to implement both versions of GetEnumerator(). So it seems to me that I should be able to foreach over an X using int's (via the generic interface) or using object's (via the non-generic interface). But it seems the compiler is unable to infer which one to call and onlys allow access to the generic one, giving a compile error if you try to "use" the non-generic one. Are there rules governing this i.e. why not the other way around?

                  Scott Dorman wrote:

                  The key is which method is defined explicitly or implicitly.

                  Maybe this is the problem, I don't quite understand what the difference is. No such thing exists in C++ - if a method is pure virtual, you have to implement it, end of story. The fact that your 2b code works really makes me think foreach is ignoring the interface hierarchy and just looks for a public GetEnumerator() method. But on the other hand, the generic GetEnumerator() definition in 2b must be equivalent to the one in 2a (i.e. 2 different ways of defining the same thing, otherwise the compiler would be complaining about an un-implemented interface method) but so then, why does declaring it public make a difference?!

                  Scott Dorman wrote:

                  When you declare one of them as public you are telling the compiler that this i

                  S 1 Reply Last reply
                  0
                  • T Taka Muraoka

                    Scott Dorman wrote:

                    By deriving X from IEnumerable you are effectively telling the type system that when you enumerate over X (by calling GetEnumerator) that you are returning a type of int not X. Isn't that the point of providing the strongly typed version? You (and the compiler) know at compile time what the data type will be, so there is no need for it to call the non-typed version.

                    That's right, this is the behaviour I want but I was playing with the code you gave because I wanted to get a better handle on what was really going on. I was wondering if the non-generic GetEnumerator() would get called if I tried to iterate using something other than int's. But see my next comment...

                    Scott Dorman wrote:

                    If you derive from an interface you have to implement all of the methods defined in the contract.

                    Maybe the problem is because I'm still thinking in C++. If I write a C++ class that derives from an interface that in turn derives from another interface, my class has to effectively implement two interfaces. This is reinforced by the fact that I have to implement both versions of GetEnumerator(). So it seems to me that I should be able to foreach over an X using int's (via the generic interface) or using object's (via the non-generic interface). But it seems the compiler is unable to infer which one to call and onlys allow access to the generic one, giving a compile error if you try to "use" the non-generic one. Are there rules governing this i.e. why not the other way around?

                    Scott Dorman wrote:

                    The key is which method is defined explicitly or implicitly.

                    Maybe this is the problem, I don't quite understand what the difference is. No such thing exists in C++ - if a method is pure virtual, you have to implement it, end of story. The fact that your 2b code works really makes me think foreach is ignoring the interface hierarchy and just looks for a public GetEnumerator() method. But on the other hand, the generic GetEnumerator() definition in 2b must be equivalent to the one in 2a (i.e. 2 different ways of defining the same thing, otherwise the compiler would be complaining about an un-implemented interface method) but so then, why does declaring it public make a difference?!

                    Scott Dorman wrote:

                    When you declare one of them as public you are telling the compiler that this i

                    S Offline
                    S Offline
                    Scott Dorman
                    wrote on last edited by
                    #10

                    Taka Muraoka wrote:

                    That's right, this is the behaviour I want but I was playing with the code you gave because I wanted to get a better handle on what was really going on. I was wondering if the non-generic GetEnumerator() would get called if I tried to iterate using something other than int's.

                    That's probably the best way to understand it. Hopefully it is clearer for you know.

                    Taka Muraoka wrote:

                    If I write a C++ class that derives from an interface that in turn derives from another interface, my class has to effectively implement two interfaces. This is reinforced by the fact that I have to implement both versions of GetEnumerator().

                    This is the same in C#. Your class X derives from the IEnumerable<T> interface, which derives from IEnumerable, so you end up having to implement both interfaces. Since each interface defines a method named GetEnumerator you must specifically tell the compiler which method is being implemented. Interface declarations in C# do not allow you to specify an access modifier. If it's declared in an interface it's a public method or property. In order to declare the same-named method from two different interfaces you must "inform" the compiler how to tell them apart. Ordinarily when you implement an interface, you simply define the body for the matching method signature:

                    public IEnumerator GetEnumerator() { ... }

                    Since (for this part of the example) there is only one method named GetEnumerator the compiler is able to determine which method implements the interface. However, when you add the IEnumerator<T> interface you now have an ambiguous match. In order to resolve that ambiguity, one (or both) of the methods must be defined "in terms of their interface". This is what is meant by an explicit implementation. You are explicitly declaring that you are implementing the IEnumerable.GetEnumerator method. The key thing here is that when you explicitly implement an interface member you cannot specify an access modifier. It's an implied accessibility, which is something I call "psuedo-public". That is, it's a public member but is not visible unless the object is explicitly cast to that interface. So, in my example

                    class X : IEnumerable<int>
                    {
                    private List<int> mList = new List<int>();
                    public X()

                    T 1 Reply Last reply
                    0
                    • S Scott Dorman

                      Taka Muraoka wrote:

                      That's right, this is the behaviour I want but I was playing with the code you gave because I wanted to get a better handle on what was really going on. I was wondering if the non-generic GetEnumerator() would get called if I tried to iterate using something other than int's.

                      That's probably the best way to understand it. Hopefully it is clearer for you know.

                      Taka Muraoka wrote:

                      If I write a C++ class that derives from an interface that in turn derives from another interface, my class has to effectively implement two interfaces. This is reinforced by the fact that I have to implement both versions of GetEnumerator().

                      This is the same in C#. Your class X derives from the IEnumerable<T> interface, which derives from IEnumerable, so you end up having to implement both interfaces. Since each interface defines a method named GetEnumerator you must specifically tell the compiler which method is being implemented. Interface declarations in C# do not allow you to specify an access modifier. If it's declared in an interface it's a public method or property. In order to declare the same-named method from two different interfaces you must "inform" the compiler how to tell them apart. Ordinarily when you implement an interface, you simply define the body for the matching method signature:

                      public IEnumerator GetEnumerator() { ... }

                      Since (for this part of the example) there is only one method named GetEnumerator the compiler is able to determine which method implements the interface. However, when you add the IEnumerator<T> interface you now have an ambiguous match. In order to resolve that ambiguity, one (or both) of the methods must be defined "in terms of their interface". This is what is meant by an explicit implementation. You are explicitly declaring that you are implementing the IEnumerable.GetEnumerator method. The key thing here is that when you explicitly implement an interface member you cannot specify an access modifier. It's an implied accessibility, which is something I call "psuedo-public". That is, it's a public member but is not visible unless the object is explicitly cast to that interface. So, in my example

                      class X : IEnumerable<int>
                      {
                      private List<int> mList = new List<int>();
                      public X()

                      T Offline
                      T Offline
                      Taka Muraoka
                      wrote on last edited by
                      #11

                      Scott Dorman wrote:

                      Defining the member in an interface doesn't imply that it is virtual at all.

                      Aha! This is the root of my confusion. I was assuming that a C# interface was the same as a C++ interface (i.e. a collection of pure virtual/abstract methods). I remembered reading in the O'Reilly "C# Essentials" book that a C# interface was simply a syntactic shortcut for a bunch of abstract members in an abstract class but their exact wording was "similar to" :| The output from the following code gave me a surprise:

                      interface I
                      {
                      void foo( string msg ) ;
                      }

                      class A : I
                      {
                      public void foo( string msg ) { System.Console.WriteLine( msg+" -> A::foo" ) ; }
                      }

                      class B : A
                      {
                      public void foo( string msg ) { System.Console.WriteLine( msg+" -> B::foo" ) ; }
                      }

                      static void Main()
                      {
                      B b = new B() ;
                      ((I)b).foo( "Static type I" ) ;
                      ((A)b).foo( "Static type A" ) ;
                      ((B)b).foo( "Static type B" ) ;
                      }

                      --- OUTPUT ---
                      Static type I -> A::foo
                      Static type A -> A::foo
                      Static type B -> B::foo

                      In C++, the B::foo() method would've been called every time. No wonder I thought foreach appeared to be ignoring the IEnumerable's in the inheritance hierarchy. In every other OO language I've worked with, to get access to GetEnumerator() you would have to go through the IEnumerable but in C#, deriving from an interface seems to be little more than a directive to the compiler telling it that certain methods need to defined i.e. it doesn't really affect the class as such (e.g. by causing the layout of the v-table to change, or whatever C# uses). Playing around a little with the definition of B:

                      class B : A,I
                      {
                      void foo( string msg ) { System.Console.WriteLine( msg+" -> B::foo" ) ; }
                      }

                      --- OUTPUT ---
                      Static type I -> A::foo
                      Static type A -> A::foo
                      Static type B -> A::foo

                      That's weird, but I think because B::foo() is not public:

                      class B : A,I
                      {
                      public void foo( string msg ) { System.Console.WriteLine( msg+" -> B::foo" ) ; }
                      }

                      --- OUTPUT ---
                      Static type I -> B::foo
                      Static type A -> A::foo
                      Static type B -> B::foo

                      And how about these two:

                      class B : A,I
                      {
                      void foo( string msg ) { System.Console.WriteLine( msg+" -> B::foo" ) ; }
                      void I.foo( string msg ) { System.Console.WriteLine( msg+" -> B::foo 2" ) ; }
                      }

                      --- OUTPUT ---
                      Static type I -> B::foo 2
                      Static type A -> A::foo
                      Static type B -> A::foo

                      c

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • World
                      • Users
                      • Groups