Extending objects that get returned as generics

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

    #16
    Re: Extending objects that get returned as generics

    Jon Skeet [C# MVP] <skeet@pobox.co m> wrote in
    news:MPG.1e3611 621088ceff98cce c@msnews.micros oft.com:
    [color=blue]
    > Absolutely - if you try to add a Person to a List<Farmer>, that
    > shouldn't work, should it?[/color]

    Yeah I think this is another problem of the "wording" but in this case
    its the actual (correct) code that is confusing. It just seems strange
    that if I receive a List<T> where T: Person that I can't add a 'Person'
    to it. But of course you make perfect sense in your example - if I
    receive a List<Farmer> (where Farmer: Person) then of course I can't add
    a Person to it because not every person is a farmer! (E I E I O).

    I guess when one sees "where T: Person" they can ONLY say "T is a
    Person" from OUTSIDE the function. When viewed from INSIDE the
    function, T is T, and even though whatever T is may be derived from
    Person, you can't treat it as a Person directly.

    For example, the following compiles:

    static Person mike = new Person("mike");
    static Farmer john = new Farmer("john", 10);
    static Farmer bob = new Farmer("bob", 20);

    private static List<T> GetNames<T>() where T: Person
    {
    List<T> l = new List<T>();

    l.Add(mike as T);
    l.Add(john as T);
    l.Add(bob as T);

    return l;
    }

    private static void Test()
    {
    List<Person> people = GetNames<Person >();
    List<Farmer> farmers = GetNames<Farmer >();
    }

    Both 'people' and 'farmers' will have 3 entries, but the first entry in
    'farmers' is null (as expected). Furthermore, the entry that is null is
    'mike', which IS a person, which is what we are specifying T should be.

    Inside the function, you really do have to look at T as a macro
    replacement more than simply being a Type specifier. Outside the
    function, it is definitely a type specifier.

    [color=blue]
    > 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...[/color]

    I guess in this case making the parameter a List<T> doesn't make
    sense... it should simply return List<Person>, or for Keith's example,
    it should return a List<ADUser>. But in that case the function isn't
    generic, which was the whole point, so it doesn't matter anyway.

    My brain hurts now. :)

    -mdb

    Comment

    • Keith Elder

      #17
      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?
      >
      > -Keith[/color]

      I took another stab at this a different way thinking I could solve the
      problem but I seemed to have run into a snafu with null exception and
      casting. Here is what I did to the base library class trying to come up
      with a more flexible solution. The problem lies within the
      GetUsersByName( ) where user is always null and cannot be converted.
      Anyone see something that I'm missing and a way to fix this?


      class ADUser
      {
      public string FirstName;
      public string LastName;

      public static List<T> GetUsersByName< T>(string firstName, string
      lastName) where T : ADUser
      {
      List<T> users = new List<T>();
      using (DirectorySearc her ds = GetDirectorySea rcher())
      {
      ds.SearchRoot = AD.DirectoryEnt ry;
      ds.PageSize = 100;
      ds.Filter = field;
      try
      {
      using (SearchResultCo llection result = ds.FindAll())
      {
      foreach (SearchResult singleResult in result)
      {
      // LINE BELOW IS THE ONE IN QUESTION
      // IT ALWAYS RETURNS user as null
      T user = new ADUser(singleRe sult) as T;
      users.Add(user) ;
      }
      }
      return users;
      }
      catch (Exception ex)
      {
      throw new Exception("Some thing blew up");
      }
      }
      }
      }

      class DataGridUser : ADUser
      {
      private Bitmap photo = null;
      public Bitmap Photo
      {
      get
      {
      if (photo == null) { this.photo
      Some.Lib.GetIma geFromURL(this. PhotoURL); }
      return this.photo;
      }
      }
      }


      class WinForm
      {
      WinForm()
      {
      List<DataGridUs er> users =
      ADUser.GetUsers ByName<DataGrid User>("frank", String.Empty);
      }
      }

      Comment

      • Jon Skeet [C# MVP]

        #18
        Re: Extending objects that get returned as generics

        Keith Elder <keith@removeth is.dotnetpimps. net> wrote:[color=blue]
        > I took another stab at this a different way thinking I could solve the
        > problem but I seemed to have run into a snafu with null exception and
        > casting. Here is what I did to the base library class trying to come up
        > with a more flexible solution. The problem lies within the
        > GetUsersByName( ) where user is always null and cannot be converted.
        > Anyone see something that I'm missing and a way to fix this?[/color]

        Yes, it will always be null if T isn't ADUser. Imagine if you'd asked
        it to treat "new object()" as a string - it couldn't.

        Now, the code you've given doesn't create a new DataGridUser, and
        doesn't indicate that you'd *need* to. Where would you be setting the
        Photo property anyway?

        --
        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

          #19
          Re: Extending objects that get returned as generics

          Jon Skeet [C# MVP] wrote:[color=blue]
          > Keith Elder <keith@removeth is.dotnetpimps. net> wrote:[color=green]
          >> I took another stab at this a different way thinking I could solve the
          >> problem but I seemed to have run into a snafu with null exception and
          >> casting. Here is what I did to the base library class trying to come up
          >> with a more flexible solution. The problem lies within the
          >> GetUsersByName( ) where user is always null and cannot be converted.
          >> Anyone see something that I'm missing and a way to fix this?[/color]
          >
          > Yes, it will always be null if T isn't ADUser. Imagine if you'd asked
          > it to treat "new object()" as a string - it couldn't.
          >
          > Now, the code you've given doesn't create a new DataGridUser, and
          > doesn't indicate that you'd *need* to. Where would you be setting the
          > Photo property anyway?
          >[/color]


          My bad, the WinForm class should have been:

          class WinForm
          {
          WinForm()
          {
          List<DataGridUs er> users =
          DataGridUser.Ge tUsersByName<Da taGridUser>("fr ank", String.Empty);
          }
          }

          After reading Michaels last post a ways up in this thread, I see there
          really isn't a way to not have user returned as the type specified. We
          were pretty much typing the same examples it seems.

          Unless someone can talk me out of it, I'm going back to:

          public static ArrayList GetUsersByName( string firstName, string lastName)
          {
          ArrayList ar = new ArrayList();
          ar.Add(new ADUser());
          ar.Add(new ADuser());
          return ar;
          }

          ArrayList users = ADUser.GetUsers ByName("frank", String.Empty);

          I think this is really more flexible when trying to do inheritance
          unless someone can talk me out of it.

          Comment

          • Jon Skeet [C# MVP]

            #20
            Re: Extending objects that get returned as generics

            Keith Elder <keith@removeth is.dotnetpimps. net> wrote:[color=blue][color=green]
            > > Now, the code you've given doesn't create a new DataGridUser, and
            > > doesn't indicate that you'd *need* to. Where would you be setting the
            > > Photo property anyway?[/color][/color]

            <snip>
            [color=blue]
            > After reading Michaels last post a ways up in this thread, I see there
            > really isn't a way to not have user returned as the type specified. We
            > were pretty much typing the same examples it seems.
            >
            > Unless someone can talk me out of it, I'm going back to:
            >
            > public static ArrayList GetUsersByName( string firstName, string lastName)
            > {
            > ArrayList ar = new ArrayList();
            > ar.Add(new ADUser());
            > ar.Add(new ADuser());
            > return ar;
            > }
            >
            > ArrayList users = ADUser.GetUsers ByName("frank", String.Empty);
            >
            > I think this is really more flexible when trying to do inheritance
            > unless someone can talk me out of it.[/color]

            What does that buy you? You're still not creating any DataGridUsers in
            the list. I don't see any advantage.

            If you need to create a list of DataGridUsers, use List<DataGridUs er>
            at that point, and only at that point. If you're going to create a list
            which may have some DataGridUsers and some plain ADUsers in, you might
            as well use a List<ADUser> and check each element.

            --
            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]

              #21
              Re: Extending objects that get returned as generics

              Michael Bray <mbray@makeDInt oDot_ctiusaDcom > wrote:[color=blue][color=green]
              > > Absolutely - if you try to add a Person to a List<Farmer>, that
              > > shouldn't work, should it?[/color]
              >
              > Yeah I think this is another problem of the "wording" but in this case
              > its the actual (correct) code that is confusing. It just seems strange
              > that if I receive a List<T> where T: Person that I can't add a 'Person'
              > to it. But of course you make perfect sense in your example - if I
              > receive a List<Farmer> (where Farmer: Person) then of course I can't add
              > a Person to it because not every person is a farmer! (E I E I O).[/color]

              Exactly.
              [color=blue]
              > I guess when one sees "where T: Person" they can ONLY say "T is a
              > Person" from OUTSIDE the function. When viewed from INSIDE the
              > function, T is T, and even though whatever T is may be derived from
              > Person, you can't treat it as a Person directly.[/color]

              No - in the same way that if you were passed a Farmer[] you couldn't
              set an element in that to a new Person. (What I'm saying is that this
              isn't a new issue with generics :)

              <snip>
              [color=blue]
              > Inside the function, you really do have to look at T as a macro
              > replacement more than simply being a Type specifier. Outside the
              > function, it is definitely a type specifier.[/color]

              I'm not entirely sure what you mean - but I suspect everyone thinks of
              generics in a very slightly different way anyway.
              [color=blue][color=green]
              > > 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...[/color]
              >
              > I guess in this case making the parameter a List<T> doesn't make
              > sense...[/color]

              Well, it does - it's just that you can't express the constraint you
              want to be able to.
              [color=blue]
              > it should simply return List<Person>, or for Keith's example,
              > it should return a List<ADUser>. But in that case the function isn't
              > generic, which was the whole point, so it doesn't matter anyway.[/color]

              Indeed :)

              --
              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...