locked
Discovering type of generic object and casting accordingly. RRS feed

  • Question

  • Here's the setup. I have a gereric class ("GenericClass<T>") and a test class ("TestClass"). Finally, I have a class ("MyClass") that is stub for some client code. The following code snippet works just fine:

    public class GenericClass<T>
    {
      private T inner;
      public GenericClass(T obj) { inner = obj; }
      public void Go() { Console.WriteLine(inner.GetType().Name); }
    } 

    public
    class TestClass
    {
      private object inner;
      public TestClass(object obj) { inner = obj; }
      public void Go()
      {
        GenericClass<string> gc = inner as GenericClass<string>;
        if (gc != null) { gc.Go(); }
        else { Console.WriteLine(inner.GetType().Name); }
      }
    }

    public 

    static class MyClass
    {
      public static void Go()
      {
        GenericClass<string> gc = new GenericClass<string>("test");
        TestClass tc = new TestClass(gc);
        tc.Go();
      }
    }

    If I use the statement MyClass.Go(); I see String in the console. Cool beans. However, my problem is this line: GenericClass<string> gc = inner as GenericClass<string>;. I do not know (and I'm unable to know) that the generic type for the GenericClass instance I'm processing is a string. I could in fact be anything. What I need is something like the following psuedo code:

        GenericClass<ANYTHING> gc = inner as GenericClass<ANYTHING>;
        if (gc != null
    ) { gc.Go(); }

    can I do this?

    thanx,

    Ralf
    Sunday, August 23, 2009 8:12 PM

Answers

  • I don't really understand what you're trying to accomplish, the snippets are too opaque.  Generically, you'll want to keep intermediate classes generic for as long as possible, until  the client nails it down with a specific type.  Your TestClass could be generic like this:

      public class TestClass<T> {
        private GenericClass<T> inner;
        public TestClass(T obj) { inner = new GenericClass<T>(obj); }
        public void Go() {
          inner.Go();
        }
      }

    Then the client nails it down with the type argument being a string:

      public static class MyClass {
        public static void Go() {
          var tc = new TestClass<string>("test");
          tc.Go();
        }
      }


    Hans Passant.
    Sunday, August 23, 2009 9:10 PM
    Moderator
  • Ralf -

    The reason you're seeing "what are you trying to accomplish" questions in response to your problem is because more experienced coders don't base control flow on types of objects.

    Code comparing types or typenames to constant values - and using that for branching - is almost always solving the underlying problem in an incorrect way. Hence the questions of "what are you trying to accomplish" - they're asking this so that they can provide a better design for the actual problem.

    To put more clearly: never branch based on a type of object that you created yourself, and do your best not to branch based on a type of object created by someone else. In my years of C# programming, I've branched based on type in exactly one method, and that was just to work around some shortcomings in the BCL. If you find yourself branching on types in your own software, then you need to adjust your design.

            -Steve
    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
    MSBuild user? Try out the DynamicExecute task in the MSBuild Extension Pack source; it's currently in Beta so get your comments in!
    Monday, August 24, 2009 12:38 PM

All replies

  • If I understand your question properly, then I think the only way you can make this work is to make the Go method of MyClass a generic method too, i.e

    pubic static void Go<T>(T value)
    {
      GenericClass<T> gc = new GenericClass<T>(value);
      TestClass tc = new TestClass(gc);
      tc.Go();
    }

    Then when you call go you would say;

    MyClass.Go<string>("test");

    or

    MyClass.Go<CustomerClass>(new Customer());

    etc.

    Does that help, or did I misunderstand ?

    Sunday, August 23, 2009 8:27 PM
  • The way I see it, the functionality of the Go method does not really depend on the generic parameter. Therefore, you could make the Go method a member of a non-generic base class, say BaseClass, that GenericClass<T> would derive from. Then, the inner member variable would be of type BaseClass and you can invoke the Go method without performing any casts whatsoever.

    Sasha Goldshtein | http://blogs.microsoft.co.il/blogs/sasha
    Sunday, August 23, 2009 8:45 PM
  • I don't really understand what you're trying to accomplish, the snippets are too opaque.  Generically, you'll want to keep intermediate classes generic for as long as possible, until  the client nails it down with a specific type.  Your TestClass could be generic like this:

      public class TestClass<T> {
        private GenericClass<T> inner;
        public TestClass(T obj) { inner = new GenericClass<T>(obj); }
        public void Go() {
          inner.Go();
        }
      }

    Then the client nails it down with the type argument being a string:

      public static class MyClass {
        public static void Go() {
          var tc = new TestClass<string>("test");
          tc.Go();
        }
      }


    Hans Passant.
    Sunday, August 23, 2009 9:10 PM
    Moderator
  • Yes, the sample provide is simplistic. The details of the project I was working on would have been cumbersome and distracting from the question I was posing. I tried to capture the essense of the issue in the snippet above. (As an aside, I'm taking a different approach altogether as I belive I can achieve what I'm looking for without the cast I illustrated in my sample code. I was already moving in that direction when I submitted the earlier post but I thought the question as posed was interesting in its own right.)

    Let me try a different approach. Every now and again I find it useful to take a parameter, say an interface of some sort, and branch my logic based on the parameter's actual type. For example, I may have defined interface ISomeInterface and a number of classes that implement it. In a method that accepts a parameter of type ISomeInterface I may need to take one action if the parameter is of type ImplementingType and a different action otherwise like so:

    private void SomeMethod(ISomeInterface si)
    {
      if(si is ImplementingType)
      {
        DoThis(si as ImplementingType);
      }
      else
      {
        DoThat(si);
      }
    }

    This isn't a common scenario, but when it does occur this approach works fine. But I got to wondering what if the implementing type is a generic, and I don't know the type of <T>? And, for the sake of discussion, let's stipulate that I cannot use a generic method and I can't limit <T> to a number of known types. Is there anyway to use the is/as keywords to cast, say, an object type to a generic type as shown here?

    public void SomeMethod(object obj)
    {
      if(obj is GenericDerivedType<T>)
      {
        DoThis(obj as GenericDerivedType<T>);
      }
      else
      {
        DoThat(obj);
      }
    }


    Thanx, btw, for all the responses. I'd considered those approaches, but they weren't applicable to the situation I was coding for and I've changed my approach in any event. And as I indicated, the specific problem I was considering got me to thinking in a more general fashion.

    Thanx,

    Ralf Thompson
    Monday, August 24, 2009 1:55 AM
  • Like I asked before, why can't the DoThis and DoThat actions be specific overrides of a virtual method defined in the interface?
    I still don't see why this is not a classic case of polymorphism.
    Sasha Goldshtein | http://blogs.microsoft.co.il/blogs/sasha
    Monday, August 24, 2009 7:07 AM
  • public abstract class GenericClassBase
    {
      public abstract void Go();
    }
     

    public
    class GenericClass<T> : GenericClassBase
    {
      private T inner;
      public GenericClass(T obj) { inner = obj; }
      public override void Go() { Console.WriteLine(inner.GetType().Name); }
    } 

    public
    class TestClass
    {
      private object inner;
      public TestClass(object obj) { inner = obj; }
      public void Go()
      {
        GenericClassBase gc = inner as GenericClassBase;
        if
    (gc != null) { gc.Go(); }
        else { Console.WriteLine(inner.GetType().Name); }
      }
    }

    public 

    static class MyClass
    {
      public static void Go()
      {
        GenericClass<string> gc = new GenericClass<string>("test");
        TestClass tc = new TestClass(gc);
        tc.Go();
      }
    }
    Monday, August 24, 2009 8:35 AM
  • Hello,

    To me it seems really weird that you instantiate this GenericClass outside to your TestClass, passing some arbitrary value, and then passing the instance to your TestClass, can't you just do everything inside TestClass ?

    Can you explain why and what are you trying to accomplish ? even more can you tell us what each class supposed to represent and responsible for, at least briefly ?


    Eyal, Regards.

    blog.eyalsh.net
    Monday, August 24, 2009 11:07 AM
  • Ralf -

    The reason you're seeing "what are you trying to accomplish" questions in response to your problem is because more experienced coders don't base control flow on types of objects.

    Code comparing types or typenames to constant values - and using that for branching - is almost always solving the underlying problem in an incorrect way. Hence the questions of "what are you trying to accomplish" - they're asking this so that they can provide a better design for the actual problem.

    To put more clearly: never branch based on a type of object that you created yourself, and do your best not to branch based on a type of object created by someone else. In my years of C# programming, I've branched based on type in exactly one method, and that was just to work around some shortcomings in the BCL. If you find yourself branching on types in your own software, then you need to adjust your design.

            -Steve
    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
    MSBuild user? Try out the DynamicExecute task in the MSBuild Extension Pack source; it's currently in Beta so get your comments in!
    Monday, August 24, 2009 12:38 PM
  • Hi Ralf,

     

    I think many community members have provided you with very helpful suggestions on this issue.   Would you mind letting us know the result of the suggestions? 

     

    If you need further assistance, please feel free to let me know.   I will be more than happy to be of assistance.

     

    Have a nice weekend, all!

     

     

    Best Regards,
    Lingzhi Sun

    MSDN Subscriber Support in Forum

    If you have any feedback on our support, please contact msdnmg@microsoft.com


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Friday, August 28, 2009 10:38 AM
    Moderator