Extending objects that get returned as generics

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

    Extending objects that get returned as generics

    I ran into a unique situation today whereby I have a core library that
    uses generics to return users from Active Directory. Example:

    List<ADUser> users = ADUser.GetByNam e("First", "Last");

    This works great. However, what I need to do is extend the ADUser
    object like so:

    class DataGridUser : ADUser
    {
    // add some new properties
    // add some new methods for databinding
    }

    I can't figure out how to extend it though since the GetByName() returns
    a List<ADUser>. Trying this doesn't work:

    List<DataGridUs er> users = DataGridUser.Ge tByName("First" , "Last");

    This throws an exception that GetByName cannot convert DataGridUser to
    ADUser. What am I missing or doing wrong? Anyone got any ideas?

    -Keith
  • Jon Skeet [C# MVP]

    #2
    Re: Extending objects that get returned as generics

    Keith Elder wrote:[color=blue]
    > I ran into a unique situation today whereby I have a core library that
    > uses generics to return users from Active Directory. Example:
    >
    > List<ADUser> users = ADUser.GetByNam e("First", "Last");
    >
    > This works great. However, what I need to do is extend the ADUser
    > object like so:
    >
    > class DataGridUser : ADUser
    > {
    > // add some new properties
    > // add some new methods for databinding
    > }
    >
    > I can't figure out how to extend it though since the GetByName() returns
    > a List<ADUser>. Trying this doesn't work:
    >
    > List<DataGridUs er> users = DataGridUser.Ge tByName("First" , "Last");
    >
    > This throws an exception that GetByName cannot convert DataGridUser to
    > ADUser. What am I missing or doing wrong? Anyone got any ideas?[/color]

    Are you sure the compiler doesn't complain that it can't convert
    List<ADUser> to List<DataGridUs er>? The problem is that List<ADUser>
    and List<DataGridUs er> are entirely different types, even though
    DataGridUser derives from ADUser.

    There's a recent thread on this newsgroup about it, called "Casting
    generic collections & inheritance". Look on groups.google.c om for it if
    you don't have it locally.

    If this isn't what you mean, please provide a short but complete
    example which demonstrates your problem. See
    http://www.pobox.com/~skeet/csharp/complete.html for more details on
    what I mean by that.

    Jon

    Comment

    • Joanna Carter [TeamB]

      #3
      Re: Extending objects that get returned as generics

      "Keith Elder" <keith@removeth is.dotnetpimps. net> a écrit dans le message de
      news: eYudnWNR9vILWFb eRVn-gA@comcast.com...

      | I can't figure out how to extend it though since the GetByName() returns
      | a List<ADUser>. Trying this doesn't work:
      |
      | List<DataGridUs er> users = DataGridUser.Ge tByName("First" , "Last");
      |
      | This throws an exception that GetByName cannot convert DataGridUser to
      | ADUser. What am I missing or doing wrong? Anyone got any ideas?

      Correct. See Jon's reply. But assuming that you are creating DataGridUser
      instances and adding them to a List<ADUser>, then you can always cast the
      individual items to DataGridUser.

      As Jon said, the two type of generic list are not related, they are sibling
      classes. Your other choice is to modify the GetByName method or add another
      method that returns the type of list that you are wanting.

      Joanna

      --
      Joanna Carter [TeamB]
      Consultant Software Engineer


      Comment

      • Keith Elder

        #4
        Re: Extending objects that get returned as generics

        Joanna Carter [TeamB] wrote:[color=blue]
        > "Keith Elder" <keith@removeth is.dotnetpimps. net> a écrit dans le message de
        > news: eYudnWNR9vILWFb eRVn-gA@comcast.com...
        >
        > | I can't figure out how to extend it though since the GetByName() returns
        > | a List<ADUser>. Trying this doesn't work:
        > |
        > | List<DataGridUs er> users = DataGridUser.Ge tByName("First" , "Last");
        > |
        > | This throws an exception that GetByName cannot convert DataGridUser to
        > | ADUser. What am I missing or doing wrong? Anyone got any ideas?
        >
        > Correct. See Jon's reply. But assuming that you are creating DataGridUser
        > instances and adding them to a List<ADUser>, then you can always cast the
        > individual items to DataGridUser.
        >
        > As Jon said, the two type of generic list are not related, they are sibling
        > classes. Your other choice is to modify the GetByName method or add another
        > method that returns the type of list that you are wanting.
        >
        > Joanna
        >[/color]


        Thanks Joanna,

        I think my problem lies in the fact that I really cannot touch this core
        library ADUser. Extending it is the only way to really modify it.
        Think of it no differently than System.Data.XXX X. Pretty much all apps
        rely on that object to work and it rarely gets modified.

        I did try casting to DataGridUser, however it fails because the method
        GetByName returns List<ADUser>. Care to give an example of what you
        were thinking?

        Comment

        • Joanna Carter [TeamB]

          #5
          Re: Extending objects that get returned as generics

          "Keith Elder" <keith@removeth is.dotnetpimps. net> a écrit dans le message de
          news: X8WdnZd8rYihSFb enZ2dnUVZ_sSdnZ 2d@comcast.com...

          | I did try casting to DataGridUser, however it fails because the method
          | GetByName returns List<ADUser>. Care to give an example of what you
          | were thinking?

          {
          List<ADUser> users = DataGridUser.Ge tByName("First" , "Last");

          if (users.Count > 0)
          DataGridUser user = (DataGridUser) users[0];
          ...
          }

          This assumes that the objects in the list really are DataGridUsers.

          Joanna

          --
          Joanna Carter [TeamB]
          Consultant Software Engineer


          Comment

          • Michael Bray

            #6
            Re: Extending objects that get returned as generics

            Keith Elder <keith@removeth is.dotnetpimps. net> wrote in
            news:eYudnWNR9v ILWFbeRVn-gA@comcast.com:
            [color=blue]
            > This throws an exception that GetByName cannot convert DataGridUser to
            > ADUser. What am I missing or doing wrong? Anyone got any ideas?
            >[/color]

            I have two things to say about this...

            First, being one of the people involved in the thread that Jon referred to,
            I would think that the problem here is that the person that wrote the
            ADUser library didn't fully consider what users might want to do with those
            objects and functions that the library provides. What I mean by this is
            that there are probably functions provided by the library that you want to
            use List<DataGridUs er> with but can't because they expect List<ADUser>, for
            example:

            public void DoSomething(Lis t<ADUser> list) { ... }

            Based on the discussion thread with Jon, what the author should have done
            is:

            public void DoSomething<T>( List<T> list) where T : ADUser { ... }

            It wouldn't surprise me at all if a lot of libraries that are written to
            support generics aren't written this way, simply because they don't have to
            be.

            Jon, would you agree that *in general*, any functions which accept generics
            should be written to support this least generic form of the function? It
            almost seems as if the first form (with List<ADUser>) of the function
            signature should be the "not recommended" way of writing this function,
            because it totally limits the flexibility of the function as it relates to
            classes derived from ADUser.

            Second, Keith, if you can't make alterations to the base library, then you
            might want to look at the "Decorator" pattern. I think it might help here.
            Here's one:

            DLearn how to use the C# Decorator design pattern to add new functionality to existing objects without modifying their classes, with quick and easy examples. 100% Source code.


            -mdb

            Comment

            • Keith Elder

              #7
              Re: Extending objects that get returned as generics

              Michael Bray wrote:[color=blue]
              > Keith Elder <keith@removeth is.dotnetpimps. net> wrote in
              > news:eYudnWNR9v ILWFbeRVn-gA@comcast.com:
              >[color=green]
              >> This throws an exception that GetByName cannot convert DataGridUser to
              >> ADUser. What am I missing or doing wrong? Anyone got any ideas?
              >>[/color]
              >
              > I have two things to say about this...
              >
              > First, being one of the people involved in the thread that Jon referred to,
              > I would think that the problem here is that the person that wrote the
              > ADUser library didn't fully consider what users might want to do with those
              > objects and functions that the library provides. What I mean by this is
              > that there are probably functions provided by the library that you want to
              > use List<DataGridUs er> with but can't because they expect List<ADUser>, for
              > example:
              >
              > public void DoSomething(Lis t<ADUser> list) { ... }
              >
              > Based on the discussion thread with Jon, what the author should have done
              > is:
              >
              > public void DoSomething<T>( List<T> list) where T : ADUser { ... }
              >
              > It wouldn't surprise me at all if a lot of libraries that are written to
              > support generics aren't written this way, simply because they don't have to
              > be.
              >
              > Jon, would you agree that *in general*, any functions which accept generics
              > should be written to support this least generic form of the function? It
              > almost seems as if the first form (with List<ADUser>) of the function
              > signature should be the "not recommended" way of writing this function,
              > because it totally limits the flexibility of the function as it relates to
              > classes derived from ADUser.
              >
              > Second, Keith, if you can't make alterations to the base library, then you
              > might want to look at the "Decorator" pattern. I think it might help here.
              > Here's one:
              >
              > http://www.dofactory.com/Patterns/PatternDecorator.aspx
              >
              > -mdb[/color]


              Mark, I think you are absolutely right, the way it is written locks it
              in. Here is how the library is written sort of sudo code:

              class ADUser
              {
              public string FirstName;
              public string LastName;
              public string PictureURL;

              public static List<ADUser> GetUsersByName( string firstName,strin g lastName)
              {
              // pretend this searched Active directory
              // and found two users with the name frank
              List<ADUser> users = new List<ADUser>();
              users.Add(new ADUser("frank", "wannabe")) ;
              users.Add(new ADUser("frank", "cats"));
              return users;
              }
              }

              Based on that, what do you think *should* be done on this?

              Comment

              • Michael Bray

                #8
                Re: Extending objects that get returned as generics

                Keith Elder <keith@removeth is.dotnetpimps. net> wrote in
                news:UamdnV5TJq klQlbenZ2dnUVZ_ tmdnZ2d@comcast .com:
                [color=blue]
                > Based on that, what do you think *should* be done on this?[/color]

                On that, nothing.

                In my first response, I was assuming that the problem was that the library
                also provides functions that take a List<ADUser>, and that you cannot use
                these functions because you want to derive DataGridUser from ADUser, and
                then pass List<DataGridUs er> to that function (which doesn't compile.)
                This may have been assuming too much.

                It seems that you simply want to convert the List<ADUser> to your derived
                List<DataGridUs er>. If that's the case, then you can use something like
                this:

                public new List<DataGridUs er> GetByName(...)
                {
                List<ADUser> adUsers = base.GetByName( ...)
                List<DataGridUs er> dgUsers = new List<DataGridUs er>(adUsers.ToA rray());
                return dgUsers;
                }

                -mdb

                Comment

                • Keith Elder

                  #9
                  Re: Extending objects that get returned as generics

                  Michael Bray wrote:[color=blue]
                  > Keith Elder <keith@removeth is.dotnetpimps. net> wrote in
                  > news:UamdnV5TJq klQlbenZ2dnUVZ_ tmdnZ2d@comcast .com:
                  >[color=green]
                  >> Based on that, what do you think *should* be done on this?[/color]
                  >
                  > On that, nothing.
                  >
                  > In my first response, I was assuming that the problem was that the library
                  > also provides functions that take a List<ADUser>, and that you cannot use
                  > these functions because you want to derive DataGridUser from ADUser, and
                  > then pass List<DataGridUs er> to that function (which doesn't compile.)
                  > This may have been assuming too much.
                  >
                  > It seems that you simply want to convert the List<ADUser> to your derived
                  > List<DataGridUs er>. If that's the case, then you can use something like
                  > this:
                  >
                  > public new List<DataGridUs er> GetByName(...)
                  > {
                  > List<ADUser> adUsers = base.GetByName( ...)
                  > List<DataGridUs er> dgUsers = new List<DataGridUs er>(adUsers.ToA rray());
                  > return dgUsers;
                  > }
                  >
                  > -mdb[/color]

                  Actually to make you feel better, there are some functions which take
                  the list List<ADUser> and then do things with that list in the ADUser
                  object. Those methods need to be re-structured as well it seems.

                  Thanks Mark, a great help.

                  -Keith

                  Comment

                  • James Curran

                    #10
                    Re: Extending objects that get returned as generics

                    class DataGridUser : ADUser
                    {

                    public DataGridUser(AD user aduser)
                    {
                    // presumably you have this ctor.
                    }

                    public static List<DataGridUs er> GetUsersByName( string firstName,strin g
                    lastName)
                    {
                    List<ADUser> users = ADUser.GetUsers ByName(firstNam e,lastName);
                    List<DataGridUs er> dgusers = new List<DataGridUs er> (users.Size);
                    foreach(ADUser user in users)
                    {
                    dgusers.Add(new DataGridUser(us er));
                    }

                    return dgusers;
                    }

                    Of course, this will have the same expansion problems as the original. TO
                    make use of Michael's suggestion:

                    static public void GetUsersByName< T>(List<T> list, string firstName,strin g
                    lastName) where T : DataGridUser
                    {

                    List<ADUser> users = ADUser.GetUsers ByName(firstNam e,lastName);
                    foreach(ADUser user in users)
                    {
                    list.Add(new T(user));
                    }
                    }

                    This would be call via:
                    List<DataGridUs er> dgusers = new List<DataGridUs er> ();
                    GetUsersByName( dgusers, "John", "Smith");


                    Now, I'm a little hazy on allowable C# generics syntax, but In C++
                    templates, we could write that as

                    static List<T> GetUsersByName< T>(string firstName,strin g lastName)
                    {
                    }

                    List<DataGridUs er> dgusers = GetUsersByName< DataGridUser>(" John",
                    "Smith");

                    --
                    Truth,
                    James Curran
                    [erstwhile VC++ MVP]

                    Home: www.noveltheory.com Work: www.njtheater.com
                    Blog: www.honestillusion.com Day Job: www.partsearch.com
                    "Keith Elder" <keith@removeth is.dotnetpimps. net> wrote in message
                    news:UamdnV5TJq klQlbenZ2dnUVZ_ tmdnZ2d@comcast .com...[color=blue]
                    > Mark, I think you are absolutely right, the way it is written locks it
                    > in. Here is how the library is written sort of sudo code:
                    >
                    > class ADUser
                    > {
                    > public string FirstName;
                    > public string LastName;
                    > public string PictureURL;
                    >
                    > public static List<ADUser> GetUsersByName( string firstName,strin g[/color]
                    lastName)[color=blue]
                    > {
                    > // pretend this searched Active directory
                    > // and found two users with the name frank
                    > List<ADUser> users = new List<ADUser>();
                    > users.Add(new ADUser("frank", "wannabe")) ;
                    > users.Add(new ADUser("frank", "cats"));
                    > return users;
                    > }
                    > }
                    >
                    > Based on that, what do you think *should* be done on this?[/color]


                    Comment

                    • Jon Skeet [C# MVP]

                      #11
                      Re: Extending objects that get returned as generics

                      Keith Elder <keith@removeth is.dotnetpimps. net> wrote:

                      <snip>
                      [color=blue]
                      > Mark, I think you are absolutely right, the way it is written locks it
                      > in. Here is how the library is written sort of sudo code:
                      >
                      > class ADUser
                      > {
                      > public string FirstName;
                      > public string LastName;
                      > public string PictureURL;
                      >
                      > public static List<ADUser> GetUsersByName( string firstName,strin g lastName)
                      > {
                      > // pretend this searched Active directory
                      > // and found two users with the name frank
                      > List<ADUser> users = new List<ADUser>();
                      > users.Add(new ADUser("frank", "wannabe")) ;
                      > users.Add(new ADUser("frank", "cats"));
                      > return users;
                      > }
                      > }
                      >
                      > Based on that, what do you think *should* be done on this?[/color]

                      Well, you haven't shown your DataGridUser class. Does it have a method
                      GetUsersByName as well?

                      --
                      Jon Skeet - <skeet@pobox.co m>
                      http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
                      If replying to the group, please do not mail me too

                      Comment

                      • Keith Elder

                        #12
                        Re: Extending objects that get returned as generics

                        Jon Skeet [C# MVP] wrote:[color=blue]
                        > Keith Elder <keith@removeth is.dotnetpimps. net> wrote:
                        >
                        > <snip>
                        >[color=green]
                        >> Mark, I think you are absolutely right, the way it is written locks it
                        >> in. Here is how the library is written sort of sudo code:
                        >>
                        >> class ADUser
                        >> {
                        >> public string FirstName;
                        >> public string LastName;
                        >> public string PictureURL;
                        >>
                        >> public static List<ADUser> GetUsersByName( string firstName,strin g lastName)
                        >> {
                        >> // pretend this searched Active directory
                        >> // and found two users with the name frank
                        >> List<ADUser> users = new List<ADUser>();
                        >> users.Add(new ADUser("frank", "wannabe")) ;
                        >> users.Add(new ADUser("frank", "cats"));
                        >> return users;
                        >> }
                        >> }
                        >>
                        >> Based on that, what do you think *should* be done on this?[/color]
                        >
                        > Well, you haven't shown your DataGridUser class. Does it have a method
                        > GetUsersByName as well?
                        >[/color]

                        The DataGridUser class is very simple. To bind to the datagrid, we need
                        to convert the photo url in ADUser to a Bitmap. So, no, there isn't a
                        GetUsersByName in DataGridUser.

                        class DataGridUser : ADUser
                        {
                        private Bitmap photo;
                        public Bitmap Photo
                        {
                        get
                        {
                        return Some.Common.Lib .GetImageFromUR L(this.PhotoURL );
                        }
                        }
                        }

                        Comment

                        • Michael Bray

                          #13
                          Re: Extending objects that get returned as generics

                          "James Curran" <jamescurran@mv ps.org> wrote in
                          news:Oo8acssGGH A.2948@TK2MSFTN GP10.phx.gbl:
                          [color=blue]
                          > Of course, this will have the same expansion problems as the original.
                          > TO make use of Michael's suggestion:
                          >
                          > static public void GetUsersByName< T>(List<T> list, string
                          > firstName,strin g lastName) where T : DataGridUser
                          > {
                          >
                          > List<ADUser> users =
                          > ADUser.GetUsers ByName(firstNam e,lastName); foreach(ADUser user
                          > in users) {
                          > list.Add(new T(user));
                          > }
                          > }
                          >[/color]

                          A fine example of why this is likely to be a common problem! I forgot to
                          even think of doing it myself! :)

                          However I was unable to get this to work in my own example (using
                          Person/Farmer, pardon the shift).

                          public class Person { ... }
                          public class Farmer : Person { ... }

                          private static void GetNames<T>(Lis t<T> list) where T : Person
                          {
                          Person mike = new Person("mike");

                          // This doesn't compile
                          list.Add(mike);
                          }

                          with the compiler saying that it can't convert 'Person' to 'T'. Am I
                          missing something??

                          -mdb

                          Comment

                          • Jon Skeet [C# MVP]

                            #14
                            Re: Extending objects that get returned as generics

                            Keith Elder <keith@removeth is.dotnetpimps. net> wrote:[color=blue]
                            > The DataGridUser class is very simple. To bind to the datagrid, we need
                            > to convert the photo url in ADUser to a Bitmap. So, no, there isn't a
                            > GetUsersByName in DataGridUser.
                            >
                            > class DataGridUser : ADUser
                            > {
                            > private Bitmap photo;
                            > public Bitmap Photo
                            > {
                            > get
                            > {
                            > return Some.Common.Lib .GetImageFromUR L(this.PhotoURL );
                            > }
                            > }
                            > }[/color]

                            In that case, a call to DataGridUser.Ge tUsersByName actually compiles
                            to a call to ADUser.GetUsers ByName anyway.

                            It sounds like you'll either need to convert the returned list, or just
                            treat every user as an ADUser until you really *need* to treat it as a
                            DataGridUser, at which point you can use "as" or cast.

                            --
                            Jon Skeet - <skeet@pobox.co m>
                            http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
                            If replying to the group, please do not mail me too

                            Comment

                            • Jon Skeet [C# MVP]

                              #15
                              Re: Extending objects that get returned as generics

                              Michael Bray <mbray@makeDInt oDot_ctiusaDcom > wrote:[color=blue]
                              > A fine example of why this is likely to be a common problem! I forgot to
                              > even think of doing it myself! :)
                              >
                              > However I was unable to get this to work in my own example (using
                              > Person/Farmer, pardon the shift).
                              >
                              > public class Person { ... }
                              > public class Farmer : Person { ... }
                              >
                              > private static void GetNames<T>(Lis t<T> list) where T : Person
                              > {
                              > Person mike = new Person("mike");
                              >
                              > // This doesn't compile
                              > list.Add(mike);
                              > }
                              >
                              > with the compiler saying that it can't convert 'Person' to 'T'. Am I
                              > missing something??[/color]

                              Absolutely - if you try to add a Person to a List<Farmer>, that
                              shouldn't work, should it?

                              I don't know any way of expressing in C# that T must be a *base* class
                              of Person, rather than *derived* from Person. I'm pretty sure it's
                              possible in Java, but I'm bad at remembering the syntax there too...

                              I'll keep an eye out for it though.

                              --
                              Jon Skeet - <skeet@pobox.co m>
                              http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
                              If replying to the group, please do not mail me too

                              Comment

                              Working...