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. C# Generic: Is this a bug or by design

C# Generic: Is this a bug or by design

Scheduled Pinned Locked Moved C#
csharpdesignhelpquestion
6 Posts 3 Posters 0 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.
  • M Offline
    M Offline
    mschuckmann
    wrote on last edited by
    #1

    Given the following class hierarchy and simple Program.Main using System; using System.Collections.Generic; using System.Text; namespace GenericTest { class Base { public void Name() { Console.WriteLine("I'm a Base class"); } }; class Middle : Base { public new void Name() { Console.Write("I'm a Middle class"); } }; class Child : Middle { public new void Name() { Console.Write("I'm a Child class"); } }; class Program { static void PrintName( T instance ) where T : GenericTest.Base { instance.Name(); } static void Main(string[] args) { Child c = new Child(); PrintName( c ); } } } I see the following output I'm a Base class And I would have expected to see I'm a Child class Is this a bug with Generics or is it a side effect of how the Generic is generated for reference types? Thanks Matt Schuckmann

    L 1 Reply Last reply
    0
    • M mschuckmann

      Given the following class hierarchy and simple Program.Main using System; using System.Collections.Generic; using System.Text; namespace GenericTest { class Base { public void Name() { Console.WriteLine("I'm a Base class"); } }; class Middle : Base { public new void Name() { Console.Write("I'm a Middle class"); } }; class Child : Middle { public new void Name() { Console.Write("I'm a Child class"); } }; class Program { static void PrintName( T instance ) where T : GenericTest.Base { instance.Name(); } static void Main(string[] args) { Child c = new Child(); PrintName( c ); } } } I see the following output I'm a Base class And I would have expected to see I'm a Child class Is this a bug with Generics or is it a side effect of how the Generic is generated for reference types? Thanks Matt Schuckmann

      L Offline
      L Offline
      Luc Pattyn
      wrote on last edited by
      #2

      Hi, it is correct by definition of the C# language. You tell PrintName it is accepting objects of type Base, hence instance.Name() actually is a call to Base.Name(). Exactly the same thing would happen when you do:

      static void SomeMethod(Base instance) {
      instance.Name();
      }

      If you want to get Child.Name() you should make Name() virtual and override it, instead of using the "new" keyword. :)

      Luc Pattyn [My Articles] [Forum Guidelines]

      M 1 Reply Last reply
      0
      • L Luc Pattyn

        Hi, it is correct by definition of the C# language. You tell PrintName it is accepting objects of type Base, hence instance.Name() actually is a call to Base.Name(). Exactly the same thing would happen when you do:

        static void SomeMethod(Base instance) {
        instance.Name();
        }

        If you want to get Child.Name() you should make Name() virtual and override it, instead of using the "new" keyword. :)

        Luc Pattyn [My Articles] [Forum Guidelines]

        M Offline
        M Offline
        mschuckmann
        wrote on last edited by
        #3

        The wayI understood the where statement is that I'm telling it to accept objects derived from type Base or that have the same interface as type Base. If I wanted to tell it to accept objects of type Base I wouldn't use a Generic I'd just use a simple method, like you suggested. So your basically telling me that within a Generic you lose all static type information and everything is staticly bound and treated as if it was the type specified in the where statement. This is really sad your basicly telling me that a generic is no better than a class or method that takes a common ancestor class for whatever types you're going to pass it. I understand making the method virtual to accomplish the intended results of this example but for reasons out side of this example I don't want the method to be virtual and I wanted to use the hiding semantics of the new keyword. Thank You Matt S.

        L 1 Reply Last reply
        0
        • M mschuckmann

          The wayI understood the where statement is that I'm telling it to accept objects derived from type Base or that have the same interface as type Base. If I wanted to tell it to accept objects of type Base I wouldn't use a Generic I'd just use a simple method, like you suggested. So your basically telling me that within a Generic you lose all static type information and everything is staticly bound and treated as if it was the type specified in the where statement. This is really sad your basicly telling me that a generic is no better than a class or method that takes a common ancestor class for whatever types you're going to pass it. I understand making the method virtual to accomplish the intended results of this example but for reasons out side of this example I don't want the method to be virtual and I wanted to use the hiding semantics of the new keyword. Thank You Matt S.

          L Offline
          L Offline
          Luc Pattyn
          wrote on last edited by
          #4

          mschuckmann wrote:

          The wayI understood the where statement is that I'm telling it to accept objects derived from type Base or that have the same interface as type Base.

          That is correct; and it also applies to SomeMethod() in my example.

          mschuckmann wrote:

          within a Generic you lose all static type information

          That is not correct: you dont loose all information, both the Generic method and the SomeMethod method accept Base objects or Base derivatives, and these objects keep their characteristics. It is only the way you define your classes (thru the virtual/override/new keywords) that decides whether you get the functionality of the base method or the more specialized method. That has nothing to do with generics. C# offers two ways to get the specialized method: by using the specialized type (Child.Name), or by using virtual methods.

          mschuckmann wrote:

          a generic is no better than a class or method that takes a common ancestor class

          That is correct if you use where T : type. But generics can be used without type constraint (acting somewhat like C++ templates). And they can be used with more complex constraints; read up on "where" in MSDN for that. :)

          Luc Pattyn [My Articles] [Forum Guidelines]

          M 1 Reply Last reply
          0
          • L Luc Pattyn

            mschuckmann wrote:

            The wayI understood the where statement is that I'm telling it to accept objects derived from type Base or that have the same interface as type Base.

            That is correct; and it also applies to SomeMethod() in my example.

            mschuckmann wrote:

            within a Generic you lose all static type information

            That is not correct: you dont loose all information, both the Generic method and the SomeMethod method accept Base objects or Base derivatives, and these objects keep their characteristics. It is only the way you define your classes (thru the virtual/override/new keywords) that decides whether you get the functionality of the base method or the more specialized method. That has nothing to do with generics. C# offers two ways to get the specialized method: by using the specialized type (Child.Name), or by using virtual methods.

            mschuckmann wrote:

            a generic is no better than a class or method that takes a common ancestor class

            That is correct if you use where T : type. But generics can be used without type constraint (acting somewhat like C++ templates). And they can be used with more complex constraints; read up on "where" in MSDN for that. :)

            Luc Pattyn [My Articles] [Forum Guidelines]

            M Offline
            M Offline
            mschuckmann
            wrote on last edited by
            #5

            Luc Pattyn wrote:

            That is not correct: you dont loose all information, both the Generic method and the SomeMethod method accept Base objects or Base derivatives, and these objects keep their characteristics.

            The objects themselves may keep their characteristics, because after all they are objects, but they no longer behave like themselves the with respect to non-virtual methods and this is counter intuitive to anyone who has done extensive template programing.

            Luc Pattyn wrote:

            It is only the way you define your classes (thru the virtual/override/new keywords) that decides whether you get the functionality of the base method or the more specialized method. That has nothing to do with generics.

            But the symantics of how they've implimented the generic feature makes the object behave differently with in the code of the generic (effectively making it lose it's type within the Generic), what is the point of providing the generic with specific type information (i.e. printName(c) ) on the type I'm passing it if it's just going to ignore it and staticely the object to the lowest common denominator implimentation for non-virtual methods. In my opinion this hugely limits the functionality of generics when compared to templates and I find it hard to believe that anyone actually thinks this is good behavior.

            Luc Pattyn wrote:

            That is correct if you use where T : type. But generics can be used without type constraint (acting somewhat like C++ templates). And they can be used with more complex constraints; read up on "where" in MSDN for that.

            If you leave out the where constraint you've essentially specified a generic with a constraint of object and you can't call any methods except those declared by object. Therefore my example code will not compile without the where constraint. So sure you can impliment generics without type constraints but don't expect to do anything interesting in your generic code. C++ templates effectively impliment constraints but much more flexibly and without any new confusing and limiting syntax. They do this by verifying any type they are instantiated with impliment (or claim to impliment) any methods that are is used by the template at compile time. Thanks again Matt S.

            T 1 Reply Last reply
            0
            • M mschuckmann

              Luc Pattyn wrote:

              That is not correct: you dont loose all information, both the Generic method and the SomeMethod method accept Base objects or Base derivatives, and these objects keep their characteristics.

              The objects themselves may keep their characteristics, because after all they are objects, but they no longer behave like themselves the with respect to non-virtual methods and this is counter intuitive to anyone who has done extensive template programing.

              Luc Pattyn wrote:

              It is only the way you define your classes (thru the virtual/override/new keywords) that decides whether you get the functionality of the base method or the more specialized method. That has nothing to do with generics.

              But the symantics of how they've implimented the generic feature makes the object behave differently with in the code of the generic (effectively making it lose it's type within the Generic), what is the point of providing the generic with specific type information (i.e. printName(c) ) on the type I'm passing it if it's just going to ignore it and staticely the object to the lowest common denominator implimentation for non-virtual methods. In my opinion this hugely limits the functionality of generics when compared to templates and I find it hard to believe that anyone actually thinks this is good behavior.

              Luc Pattyn wrote:

              That is correct if you use where T : type. But generics can be used without type constraint (acting somewhat like C++ templates). And they can be used with more complex constraints; read up on "where" in MSDN for that.

              If you leave out the where constraint you've essentially specified a generic with a constraint of object and you can't call any methods except those declared by object. Therefore my example code will not compile without the where constraint. So sure you can impliment generics without type constraints but don't expect to do anything interesting in your generic code. C++ templates effectively impliment constraints but much more flexibly and without any new confusing and limiting syntax. They do this by verifying any type they are instantiated with impliment (or claim to impliment) any methods that are is used by the template at compile time. Thanks again Matt S.

              T Offline
              T Offline
              Tim Paaschen
              wrote on last edited by
              #6

              mschuckmann wrote:

              If you leave out the where constraint you've essentially specified a generic with a constraint of object and you can't call any methods except those declared by object. Therefore my example code will not compile without the where constraint. So sure you can impliment generics without type constraints but don't expect to do anything interesting in your generic code.

              I do agree to your point of view, that the limitation of generics in regard to derived classes are unexpected. As a workaround you can use an interface, that defines the required methods, as the constraint. However, you must explicitly specify the interface for each class:

              interface Named
              {
              void Name();
              };

              class Base : Named
              {
              public void Name()
              {
              Console.WriteLine("I'm a Base class");
              }
              };

              class Middle : Base, Named
              {
              public new void Name()
              {
              Console.Write("I'm a Middle class");
              }
              };

              class Child : Middle, Named
              {
              public new void Name()
              {
              Console.Write("I'm a Child class");
              }
              };

              class Program
              {
              static void PrintName<T>( T instance ) where T : Named
              {
              instance.Name();
              }

              static void Main( string\[\] args )
              {
              	Child c = new Child();
              	PrintName( c );
              	Console.ReadLine();
              }
              

              }

              Regards, Tim

              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