Pavel Tarno
Pavel Tarno

Reputation: 1324

Casting a generic class without specific type

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?

Upvotes: 16

Views: 16152

Answers (5)

Felix K.
Felix K.

Reputation: 15643

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

Upvotes: 2

n8wrl
n8wrl

Reputation: 19765

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

Upvotes: 7

Ani
Ani

Reputation: 113402

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

Upvotes: 10

David Rettenbacher
David Rettenbacher

Reputation: 5120

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

Upvotes: 1

Keith
Keith

Reputation: 5381

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

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

}

Upvotes: 0

Related Questions