16

I have the following generic class

public class Home<T> where T : Class
{
   public string GetClassType
   {
       get{ return T.ToString() }
   }
}

Now, I'm getting an Object X which I know for sure is Home:

public void DoSomething(object x)
{
    if(x is // Check if Home<>)
    {
        // I want to invoke GetClassType method of x  
        // but I don't know his generic type
        x as Home<?> // What should I use here?
    }
}

Can I even make a cast without specifying the generic type of the class?

Pavel Tarno
  • 1,324
  • 1
  • 12
  • 29

5 Answers5

10

If you're sure the argument to DoSomething will be a Home<T>, why not make it a generic method?

public void DoSomething<T>(Home<T> home)
{
   ...
}

Of course, it would be even easier if DoSomething should logically be an instance method on Home<T>.

If you really want to stick with what you have, you could use reflection (untested):

public void DoSomething(object x)
{
    // null checks here.

    Type t = x.GetType();

    if (t.IsGenericType &&
          && t.GetGenericTypeDefinition() == typeof(Home<>))
    {
        string result = (string) t.GetProperty("GetClassType")
                                  .GetValue(x, null);

        Console.WriteLine(result);
    }

    else 
    {
        ... // do nothing / throw etc.
    }
}
Ani
  • 111,048
  • 26
  • 262
  • 307
7

What if Home derived from a base class?

public class Home
{
    public virtual string GetClassType { get; }
}
public class Home<T> : Home
    where T : class
{
    public override string GetClassType
    {
        get{ return T.ToString() } 
    }
    ...
}

and then

public void DoSomething(object x)
{
    if(x is Home)
    {
        string ct = ((Home)x).GetClassType;
        ...
    }
}
n8wrl
  • 19,439
  • 4
  • 63
  • 103
  • 2
    This is perfect! I had a generic class with 2 peroperties: `SomeClass Query` and `List Items`. I didn't care about the `Items`, only needed the `Query`, so moving that to a base class fixed it! – Cerbrus Oct 07 '20 at 09:53
2

I know this is an old thread but all answers posted so far have not directly addressed this question and instead only suggested workarounds (i.e. "use reflection", "make your DoSomething() method generic" or "create a non-generic base class and call this base class' method").

Can I even make a cast without specifying the generic type of the class?

So to clearly answer your question: No it is not possible. Due to the covariance constraints in C# you cannot cast into a generic class.

In more detail: I am assuming you would want to use x as Home<object> as the lowest common denomitator in order to be be able to call toString() provided by the Object class. Casting your object x to Home<object> would require covariance which is not possible with classes (only generic interfaces and delegates can be covariant). while this is great to prevent mistakes at compile time, it is certainly an annoyance when wanting to access methods on generic classes, as in your case. @n8wrl answer is probably your best shot in terms of "casting".

That being said, you could also go for an interface-based solution, using the out flag on your T parameter:

interface IHome<out T> {
  string GetClassType { get; }
}

public class Home<T> : IHome<T> where T : class
{
  public string GetClassType
  {
      get { return typeof(T).Name; }
  }
}

Then this should work:

public void DoSomething(object x)
{
    if(x is // Check if Home<>)
    {
        var y = x as IHome<object>;
        var z = y.GetClassType;
    }
}
Felix K.
  • 14,171
  • 9
  • 58
  • 72
1

How about making the function generic?

public void DoSomething<T>(object x)
{
    if(x is Home<T>)
    {
        x as Home<T> ...
    }
}

Edit: Another possiblity would be to create an interface which holds the property GetClassName so you would only need to check if it is of that interface.

public interface IDescribeClass
{
    public string GetClassName { get; set; }
}

BTW: I would use the full qualified name

public string ClassType
{
    get{ return typeof(T).FullName; }
}
David Rettenbacher
  • 5,088
  • 2
  • 36
  • 45
0

Have you tried changing your method definition to something like this?

public void DoSomething<T>(Home<T> x)
{

}
Keith
  • 5,311
  • 3
  • 34
  • 50