12

I've got the following code:

public static T GetCar<T>() where T : ICar
{
    T objCar = default(T);

    if (typeof(T) == typeof(SmallCar)) {
        objCar = new SmallCar("");
    } else if (typeof(T) == typeof(MediumCar)) {
        objCar = new MediumCar("");
    } else if (typeof(T) == typeof(BigCar)) {
        objCar = new BigCar("");
    }

    return objCar;
}

And this is the error I am getting: Cannot implicitly convert type 'Test.Cars' to 'T'

What Am I missing here? All car types implement the ICar interface.

Thanks

Avi Turner
  • 10,234
  • 7
  • 48
  • 75
MeTitus
  • 3,390
  • 2
  • 25
  • 49
  • 2
    It will be easier to help if you post Test.Cars code along usage in the question. – Sascha Apr 07 '12 at 19:29
  • possible duplicate of [implict type cast in generic method](http://stackoverflow.com/questions/2765924/implict-type-cast-in-generic-method) – nawfal Jan 15 '14 at 18:13

4 Answers4

16

You cannot convert to T because of the fact that T isn't known at compile time. If you want to get your code to work you can change the return type to ICar and remove the generic T return type.

You also can cast to T. This would work too. If you only using the default constructor you can also constain on new() and use new T() to get your code to work.

Samples

public ICar GetCar<T>()
    where T : ICar
{
    ICar objCar = null;

    if (typeof(T) == typeof(SmallCar)) {
        objCar = new SmallCar();
    } else if (typeof(T) == typeof(MediumCar)) {
        objCar = new MediumCar();
    } else if (typeof(T) == typeof(BigCar)) {
        objCar = new BigCar();
    }

    return objCar;
}

Cast:

public T GetCar<T>()
    where T : ICar
{
    Object objCar = null;

    if (typeof(T) == typeof(SmallCar)) {
        objCar = new SmallCar();
    } else if (typeof(T) == typeof(MediumCar)) {
        objCar = new MediumCar();
    } else if (typeof(T) == typeof(BigCar)) {
        objCar = new BigCar();
    }

    return (T)objCar;
}

New-constraint:

public T GetCar<T>()
    where T : ICar, new()
{
    return new T();
}
Community
  • 1
  • 1
Felix K.
  • 6,201
  • 2
  • 38
  • 71
  • Used your 2nd solution. Thanks. – MeTitus Apr 07 '12 at 19:59
  • @Marco Glad that i could help. – Felix K. Apr 07 '12 at 20:59
  • I'm curious. In your 3rd example with the new constraint, how would it work with his example of returning different classes. That's clear in your 2nd example, but how would you do it with the 3rd? – Doug Jun 22 '16 at 20:18
  • @Doug `T` is in this case the class you've choosen, for example `SmallCar`. The constraint tells the compiler that every type which is given in the method as generic parameter must have a parameterless constructor ( new constraint ) and must implement the type `ICar`. The method body now creates a new instance of `T` and doesn't care which type it actually is. However this method is somehow limited as it's not possible yet to define possible parameters for the method. If you want to pass parameters you can use the `Activator` class. – Felix K. Jun 22 '16 at 20:57
  • 1
    I just want to mention that you are missing the brackets for the `new()` contraint: https://msdn.microsoft.com/en-us/library/sd2w2ew5.aspx – Jens Apr 06 '17 at 07:50
7

Your code is illegal because while you might be testing and know that your given T is BigCar or some other such type, the compiler cannot know that in advance and therefore the code is illegal. Based upon your given usage, you could have

public static T GetCar<T>() where T : ICar, new()
{
    return new T();
}

The new() constraint allows you to invoke the default (parameterless) constructor on a type.

Anthony Pegram
  • 123,721
  • 27
  • 225
  • 246
  • But I don't want to invoke the default constructor on the given type. In the example I used a parameterless constructor for the types, but that is not the case.Thanks for you help. – MeTitus Apr 07 '12 at 19:48
  • @Marco in that case, call a factory method, but whatever it is, exposing a parameterless constructor is *less harmful* than the if-else type checking you're doing (especially inside a generic method). – nawfal Jan 15 '14 at 18:31
2

You can simplify your code

public static T GetCar<T>()
    where T : ICar, new()
{
    return new T();
}
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
0

Generics is a run-time concept. Information of the types used in a generic data type regardless if its a value or reference type can be obtained at run-time using reflection.

When a code with T is compiled into MSIL it only identifies it as having a type parameter. Therefore generic type parameter T is not known at compile-time.

class Program
{
    static void Main(string[] args)
    {
        ICar smallCar = Helper.GetCar<SmallCar>("car 1");
        ICar mediumCar = Helper.GetCar<MediumCar>("car 2");

        Console.ReadLine();
    }
}

static class Helper
{
    public static T GetCar<T>(string carName) where T : ICar
    {
        ICar objCar = default(T);

        if (typeof(T) == typeof(SmallCar))
        {
            objCar = new SmallCar { CarName = carName };
        }
        else if (typeof(T) == typeof(MediumCar))
        {
            objCar = new MediumCar { CarName = carName };
        }

        return (T)objCar;
    }

}

interface ICar
{
    string CarName { get; set; }
}

class SmallCar : ICar
{
    public string CarName { get; set ; }
}

class MediumCar : ICar
{
    public string CarName { get; set; }
}
Nipuna
  • 6,846
  • 9
  • 64
  • 87