Passing derived object of generic class to another class

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • MMAS

    Passing derived object of generic class to another class

    Hey everyone --

    Curious about some strange behaviour I'm seeing that seems to be
    related to my lack of understanding on how generics work in C#. Here's
    some simplified code (sorry for strange formatting) to show what my
    issue is.

    I have a generic abstract class here:

    public abstract class MyGenericAbstra ctClass<Twhere T : MyInterface
    {
    //...whatever...
    T _internal_inter face;
    public MyGenericAbstra ctClass(T internal_var)
    {
    _internal_inter face = internal_var;
    }
    }

    and I have classes that inherit from it, such as

    public class MyDerived :
    MyGenericAbstra ctClass<Somethi ngThatImplement sMyInterface>
    {
    ///...whatever...
    public MyDerived(Somet hingThatImpleme ntsMyInterface internal_var) :
    base(internal_v ar) { }
    }

    My problem comes when I try to pass my "MyDerived" objects to another
    class, defined as such:

    public class MyBrokenClass
    {
    // here I have a list of MyGenericAbstra ctClass objects
    private
    System.Collecti ons.Generic.Lin kedList<MyGener icAbstractClass <MyInterface> >
    _list_of_object s;

    public void
    AddMyGenericAbs tractClassToPri vateLinkedList( MyGenericAbstra ctClass<MyInter face>
    object_to_add)
    {
    _list_of_object s.AddLast(objec t_to_add);
    }
    }

    All of this compiles just fine.

    When I try to do something like this though:
    SomethingThatIm plementsMyInter face my_something = new
    SomethingThatIm plementsMyInter face();

    MyDerived my_derived = new MyDerived(my_so mething);
    MyBrokenClass my_broken = new MyBrokenClass() ;
    my_broken.AddMy GenericAbstract ClassToPrivateL inkedList(my_de rived);

    Then I get a compile time error as follows:


    Error 1:
    The best overloaded method match for
    'MyBrokenClass. AddMyGenericAbs tractClassToPri vateLinkedList( MyGenericAbstra ctClass<MyInter face>)'
    has some invalid arguments

    Error 2:
    Argument '1': cannot convert from 'MyDerived' to
    'MyGenericAbstr actClass<MyInte rface>'


    So, what's the story? What have I done wrong?

    Thanks kindly.
    Mustafa




  • Peter Duniho

    #2
    Re: Passing derived object of generic class to another class

    On Sat, 19 Jul 2008 19:04:00 -0700, MMAS <mustafashabib@ gmail.comwrote:
    [...]
    Error 2:
    Argument '1': cannot convert from 'MyDerived' to
    'MyGenericAbstr actClass<MyInte rface>'
    >
    >
    So, what's the story? What have I done wrong?
    The short answer: generics aren't covariant. That is, the type parameters
    have to match exactly...just having the parameters related by inheritance
    isn't sufficient.

    Off the top of my head, I think that if you inherited
    MyGenericAbstra ctClass<MyInter faceinstead of
    MyGenericAbstra ctClass<Somethi ngThatImplement sMyInterfacefor MyDerived,
    that would work. At least, that is, for the specific line you're having
    trouble with (I can't say whether that would fit into the design you're
    trying to do).

    Other solutions involving changing the way you relate the types and
    instances could work as well, but without knowing more about the bigger
    picture, it's hard to say.

    Pete

    Comment

    • MMAS

      #3
      Re: Passing derived object of generic class to another class

      On Jul 19, 10:14 pm, "Peter Duniho" <NpOeStPe...@nn owslpianmk.com>
      wrote:
      On Sat, 19 Jul 2008 19:04:00 -0700, MMAS <mustafasha...@ gmail.comwrote:
      [...]
      Error 2:
         Argument '1': cannot convert from 'MyDerived' to
      'MyGenericAbstr actClass<MyInte rface>'
      >
      So, what's the story? What have I done wrong?
      >
      The short answer: generics aren't covariant.  That is, the type parameters  
      have to match exactly...just having the parameters related by inheritance 
      isn't sufficient.
      >
      Off the top of my head, I think that if you inherited  
      MyGenericAbstra ctClass<MyInter faceinstead of  
      MyGenericAbstra ctClass<Somethi ngThatImplement sMyInterfacefor MyDerived, 
      that would work.  At least, that is, for the specific line you're having  
      trouble with (I can't say whether that would fit into the design you're  
      trying to do).
      >
      Other solutions involving changing the way you relate the types and  
      instances could work as well, but without knowing more about the bigger  
      picture, it's hard to say.
      >
      Pete

      Comment

      • MMAS

        #4
        Re: Passing derived object of generic class to another class

        Ah, I see. Kinda a drag -- I suppose MyGenericAbstra ct doesn't have to
        be a Generic and I can just cast it's private Interface variable to
        the real type when deriving from it. It just doesn't seem as nice that
        way...

        Thanks for the information.

        On Jul 19, 10:14 pm, "Peter Duniho" <NpOeStPe...@nn owslpianmk.com>
        wrote:
        On Sat, 19 Jul 2008 19:04:00 -0700, MMAS <mustafasha...@ gmail.comwrote:
        [...]
        Error 2:
           Argument '1': cannot convert from 'MyDerived' to
        'MyGenericAbstr actClass<MyInte rface>'
        >
        So, what's the story? What have I done wrong?
        >
        The short answer: generics aren't covariant.  That is, the type parameters  
        have to match exactly...just having the parameters related by inheritance 
        isn't sufficient.
        >
        Off the top of my head, I think that if you inherited  
        MyGenericAbstra ctClass<MyInter faceinstead of  
        MyGenericAbstra ctClass<Somethi ngThatImplement sMyInterfacefor MyDerived, 
        that would work.  At least, that is, for the specific line you're having  
        trouble with (I can't say whether that would fit into the design you're  
        trying to do).
        >
        Other solutions involving changing the way you relate the types and  
        instances could work as well, but without knowing more about the bigger  
        picture, it's hard to say.
        >
        Pete

        Comment

        • Peter Duniho

          #5
          Re: Passing derived object of generic class to another class

          On Sun, 20 Jul 2008 11:00:11 -0700, MMAS <mustafashabib@ gmail.comwrote:
          Ah, I see. Kinda a drag -- I suppose MyGenericAbstra ct doesn't have to
          be a Generic and I can just cast it's private Interface variable to
          the real type when deriving from it. It just doesn't seem as nice that
          way...
          Well, unfortunately you haven't posted enough code for anyone else to
          comment on what might be a better approach.

          As one example: is there any real reason that your List<Thas to be a
          List<MyGenericA bstractClass<My Interface>>? One of the reasons covariance
          isn't allowed in generics is that if it is, it opens the possibility for a
          generic class being used in an unsafe way. The issue the compiler doesn't
          like is that if you were allowed to convert from one form of the generic
          type to another, the compiler can't maintain type safety any more. But
          even if the compiler allowed that, you'd still have the type safety issue.

          If you don't want to do the casting, then perhaps the alternative solution
          is to fix your List<Tso that it uses
          MyGenericAbstra ctClass<Somethi ngThatImplement sMyInterfaceins tead of
          MyGenericAbstra ctClass<MyInter face>. After all, if that's not always what
          you're putting into the List<Tthen your code was broken anyway, and if
          it is what you're putting into the List<T>, then changing the type should
          be fine.

          Of course, there's always the possibility that you're misusing generics
          here anyway. The code you posted appears to have been abridged, so it's
          not possible to comment on whether the rest of the generic class really
          needs to be generic. But it doesn't seem to me that the part you showed
          us does.

          Pete

          Comment

          • MMAS

            #6
            Re: Passing derived object of generic class to another class

            Hi again Peter -

            I'm beginning to think that I do not need or even want generics in my
            situation. My List<MyGenericA bstractClass<My Interface>shoul d really
            become a List<MyGenericA bstractClassand MyGenericAbstra ctClass
            should just have the _internal_inter face typed as MyInterface.

            I'm new to Generics and thought this was a situation I could use them
            to get some code-time benefits. For example, I can get strongly typed
            _internal_inter face objects when I was coding my MyDerived objects.
            The MyBrokenClass example, in the real code, is really a wrapper
            around a collection of MyGenericAbstra ctClass objects that will be
            calling a common method on all objects.

            To get more detailed: my MyGenericAbstra ctClass in my real code is an
            abstract "Step". The MyInterface class in my real code is the specific
            arguments (StepArguments) for that step, and the MyBrokenClass is the
            "StepManage r" that calls the "RunStep" method for each Step object.
            Using Generics seemed a good idea to ensure that each specific
            implemented step got the correct type of Arguments and also so that I
            can use those arguments while coding the Step without having to do any
            casting. It seems that I was wrong.

            I can gladly post more code if you'd like to see how I've really
            implemented all of this, but figured a short explanation would be
            easier than many lines of code.

            Thanks again for the help.
            Mustafa

            On Jul 20, 1:20 pm, "Peter Duniho" <NpOeStPe...@nn owslpianmk.com>
            wrote:
            Well, unfortunately you haven't posted enough code for anyone else to  
            comment on what might be a better approach.
            >
            As one example: is there any real reason that your List<Thas to be a  
            List<MyGenericA bstractClass<My Interface>>?  One of the reasons covariance  
            isn't allowed in generics is that if it is, it opens the possibility for a  
            generic class being used in an unsafe way.  The issue the compiler doesn't  
            like is that if you were allowed to convert from one form of the generic  
            type to another, the compiler can't maintain type safety any more.  But 
            even if the compiler allowed that, you'd still have the type safety issue..
            >
            If you don't want to do the casting, then perhaps the alternative solution  
            is to fix your List<Tso that it uses  
            MyGenericAbstra ctClass<Somethi ngThatImplement sMyInterfaceins tead of  
            MyGenericAbstra ctClass<MyInter face>.  After all, if that's not always what  
            you're putting into the List<Tthen your code was broken anyway, and if  
            it is what you're putting into the List<T>, then changing the type should 
            be fine.
            >
            Of course, there's always the possibility that you're misusing generics  
            here anyway.  The code you posted appears to have been abridged, so it's  
            not possible to comment on whether the rest of the generic class really  
            needs to be generic.  But it doesn't seem to me that the part you showed  
            us does.
            >
            Pete

            Comment

            • Peter Duniho

              #7
              Re: Passing derived object of generic class to another class

              On Sun, 20 Jul 2008 12:55:29 -0700, MMAS <mustafashabib@ gmail.comwrote:
              [...]
              To get more detailed: my MyGenericAbstra ctClass in my real code is an
              abstract "Step". The MyInterface class in my real code is the specific
              arguments (StepArguments) for that step, and the MyBrokenClass is the
              "StepManage r" that calls the "RunStep" method for each Step object.
              Using Generics seemed a good idea to ensure that each specific
              implemented step got the correct type of Arguments and also so that I
              can use those arguments while coding the Step without having to do any
              casting. It seems that I was wrong.
              >
              I can gladly post more code if you'd like to see how I've really
              implemented all of this, but figured a short explanation would be
              easier than many lines of code.
              I have to admit, it hasn't helped me much. Your phrase "to ensure that
              each specific implemented step got the correct type of Arguments" doesn't
              make sense to me. If each Step sub-class uses the same interface to
              define their arguments, then isn't that the "type of Arguments"? And if
              so, how can a sub-class get the wrong type of Arguments?

              Conversely, if each Step sub-class uses some different combination of
              arguments, then in what way does having some interface used by all the
              sub-classes provide something useful? Wouldn't there be arguments that
              could not be represented by the interface, because of the fact that
              different sub-classes are using different combinations?

              Based on what you've written so far, it does seem like you're mis-using
              generics. But I'm afraid I'm unable to understand the intended design
              well enough to offer better advice.

              I agree that "many lines of code" probably wouldn't be helpful. But as
              near as I can tell, we're only talking about four different types here:
              your interface, your abstract class, one example of a sub-class, and your
              manager class. Surely it should be possible to put together a more
              complete code sample that what you posted earlier that illustrates why
              it's useful to encapsulate your arguments as an interface, as well as why
              simply doing that doesn't work for your manager class (i.e. what sort of
              casting winds up happening that you thought would be solved by generics?).

              I would think a useful sample of 50 lines or so, perhaps even less, would
              suffice. If you'd like more consideration given of your design, maybe you
              can try to come up with a simplified sample like that.

              Pete

              Comment

              • MMAS

                #8
                Re: Passing derived object of generic class to another class

                I've seen the folly of my ways, as they say. I've rid myself of the
                Arguments class and moved the various arguments directly into each
                derived Step class. I had a "validate" method on the Arguments
                interface that I was calling before each step object was run by my
                StepManager (to make sure all arguments were valid for this object),
                but have now made this an abstract function in my abstract base "Step"
                class that is implemented in each derived class.

                Like many first attempts at using something "new", I think I was using
                Generics just to use them rather than using them for the right
                reasons.

                I have to admit, it hasn't helped me much.  Your phrase "to ensure that 
                each specific implemented step got the correct type of Arguments" doesn't 
                make sense to me.  If each Step sub-class uses the same interface to  
                define their arguments, then isn't that the "type of Arguments"?  And if  
                so, how can a sub-class get the wrong type of Arguments?
                >
                Conversely, if each Step sub-class uses some different combination of  
                arguments, then in what way does having some interface used by all the  
                sub-classes provide something useful?  Wouldn't there be arguments that 
                could not be represented by the interface, because of the fact that  
                different sub-classes are using different combinations?
                >
                Based on what you've written so far, it does seem like you're mis-using  
                generics.  But I'm afraid I'm unable to understand the intended design  
                well enough to offer better advice.
                >
                I agree that "many lines of code" probably wouldn't be helpful.  But as 
                near as I can tell, we're only talking about four different types here:  
                your interface, your abstract class, one example of a sub-class, and your 
                manager class.  Surely it should be possible to put together a more  
                complete code sample that what you posted earlier that illustrates why  
                it's useful to encapsulate your arguments as an interface, as well as why 
                simply doing that doesn't work for your manager class (i.e. what sort of  
                casting winds up happening that you thought would be solved by generics?)..
                >
                I would think a useful sample of 50 lines or so, perhaps even less, would 
                suffice.  If you'd like more consideration given of your design, maybe you  
                can try to come up with a simplified sample like that.
                >
                Pete

                Comment

                • Peter Duniho

                  #9
                  Re: Passing derived object of generic class to another class

                  On Sun, 20 Jul 2008 17:08:51 -0700, MMAS <mustafashabib@ gmail.comwrote:
                  [...]
                  Like many first attempts at using something "new", I think I was using
                  Generics just to use them rather than using them for the right
                  reasons.
                  That does happen sometimes. :)

                  But now, if and when you are using generics for something that could
                  actually benefit from them, and you run into the lack of covariance again,
                  at least you'll have seen the issue before and understand it. So your
                  effort wasn't entirely wasted. :)

                  Anyway, glad you got your code straightened out.

                  Pete

                  Comment

                  Working...