Nipuna
Nipuna

Reputation: 7006

Cast generic type parameter to a specific type in C#

If you need to cast a generic type parameter to a specific type, we can cast it to a object and do the casting like below:

void SomeMethod(T t)
{
    SomeClass obj2 = (SomeClass)(object)t;
}

Is there a better way to achieve this, rather than casting it to an object and then to a specific type?

Problem:

I have a generic function which accepts a generic type parameter, inside the function based on a type checking I do some operations like below:

    void SomeMethod(T t)
    {
        if (typeof(T).Equals(typeof(TypeA)))
        {
            TypeA = (TypeA)(object)t;
            //Do some operation
        }
        else if (typeof(T).Equals(typeof(TypeB)))
        {
            TypeB = (TypeB)(object)t;
            //Do some operation
        }
    }

Upvotes: 48

Views: 131825

Answers (10)

Nikola.Lukovic
Nikola.Lukovic

Reputation: 1325

You can use Convert.ChangeType

SomeClass obj2 = (SomeClass)Convert.ChangeType(t, typeof(SomeClass));

Although, keep in mind that this will throw an exception if a cast is invalid.

Upvotes: 52

Martijn
Martijn

Reputation: 858

Other solutions here are good. Though I suggest this solution as I find it very clean and easy to use.

if (t is TypeB tb)
{
    // tb can be used as a variable of type TypeB
    tb.SomeTypeBMethod();
}
else
{
    throw new Exception("t is not of type TypeB");
}

Throwing the exception here is just an example. If t is not of type TypeB you should handle that however you need.

Upvotes: 1

John Foll
John Foll

Reputation: 156

I had a classB that inherited from another classA, and the Convert.ChangeType failed with "Object must implement IConvertible." System.InvalidCastException. But I was able to do it differently:

public classs ClassB : ClassA
{
    public string SomePropety { get; set; }
}

public class ClassA
{
    public string SomeField { get; set; }
}

public class SomeClass
{
   public T AGenericMethod<T>()
   {
       var retObj = SomeClassWithGeneric.SomeGenericMethod<T>();
       object obj = retObj;
       ClassA objA = (ClassA)obj;
       //Do stuff with objA....
       return  retObj;
   }
}

The trick here for me was to first set the var retObj to an object variable, then I could cast the object variable to the base class that I wanted. This was MUCH easier and cleaner looking than any other hacks I could have done I think!

This seems to easy Microsoft.

Upvotes: 0

Ben
Ben

Reputation: 1

Should that simple hack solve all problems?:

    public static TOut Cast<TOut, TIn>(TIn item) 
    {
        return Enumerable.Cast<TOut>(new[] { item }).First();
    }

Yeah, this way the compiler can't find casting problem and we create objects that makes the GC a little stressed, but its easy to write and understand.

Off course you can go in the implementation on the Cast operator and just copy the bits you need, but my guess is most won't understand the content of the code. So if you need more performance, you can get more, but that should work fine in most scenarios.

Upvotes: 0

Alphacat
Alphacat

Reputation: 343

Stumbled across this, and wanted to provide an updated answer for anyone else who found this later. In newer C# versions (8.0 as I write this), pattern matching will allow you to do this in a far more concise way:

void SomeMethod<T>(T t)
{
    switch(t)
    {
        case TypeA a:
            // Do some operation using a.
            Console.WriteLine($"{a} is a TypeA!");
            break;
        case TypeB b:
            // Do some operation using b.
            Console.WriteLine($"{b} is a TypeB!");
            break;
        default:
            // Handle this case.
            Console.WriteLine("I don't know what this type is.");
            break;
    }
}

This will check the type of the object, and if it finds a match will assign it to the indicated variable in one step, which then becomes availabe to use in the body of that case. (You can also do something similar in if statements: if (t is TypeA a) a.DoSomething(); ...)

All that being said, I do agree with the other responses that you should either constrain this as much as possible (void SomeMethod<T>(T t) where T : ISomeInterface {...}) or move the operation into the classes you're testing, if possible.

Upvotes: 19

Mohammad Ali
Mohammad Ali

Reputation: 561

I know this is a late question but here what you can do

A great option is to make your function accept parameter of class object, and do your switch case as you wish

And just do the casting

YourClass = (YourClass) parameterObject;

Upvotes: 0

Martin Mulder
Martin Mulder

Reputation: 12954

If there is no relation between the input type T and the target types TypeA or TypeB (using parameter contraints), and we are looking purely at the casting-problem, the answer is simple:

No, there is no better way than the method you are using!!!

I do agree with some other people, if you are doing more operations on that object, you might wanna choose a different design.

Upvotes: 2

Igor
Igor

Reputation: 62213

A better design is to put a constraint on it that is common between type T and the class you want to expect in your method, in this case SomeClass.

class SomeConsumer<T> where T : ISomeClass
{
    void SomeMethod(T t)
    {
        ISomeClass obj2 = (ISomeClass) t;
    }
}

interface ISomeClass{}

class SomeClass : ISomeClass {}

Edit based on edit of Question

That is bad design. Try to move that "operation" into the class itself so the caller does not have to know the type. If that is not possible share more of what is being done, what you want to accomplish though is that you do not have a stack of if/else statements where execution depends on the type of object being passed in to the method.

class SomeConsumer<T> where T : ISomeClass
{
    void SomeMethod(T t)
    {
        ISomeClass obj2 = (ISomeClass) t;
        // execute
        t.Operation();
    }
}

interface ISomeClass{
    void Operation();
}

class SomeClass : ISomeClass {
    public void Operation(){/*execute operation*/}
}

Upvotes: 14

Mostafiz
Mostafiz

Reputation: 7352

You can use as for that case

void SomeMethod(T t)
{
    SomeClass obj2 = t as SomeClass;
}

Upvotes: 7

Zein Makki
Zein Makki

Reputation: 30022

Using as:

SomeClass obj2 = t as SomeClass;

This would not throw an exception and t would be null if the cast fails.

I don't really know what you're trying to do, but I hope that you're not missing the point of Generics here.

If your intention is to restrict the method to type SomeClass and descendants:

void SomeMethod(T t)  where T : SomeClass

Upvotes: 43

Related Questions