The following code says I cannot return an object of type Certification? Why not as that is the definition of T?
public T BuildList<T>(T list, AttributeModel model) where T : Certification
{
return new Certification("", "", "");
}
Update:
I was asked to explain why I think this should work. Fair question.
I read "where T : Certification" to mean that the method can also be read as:
public Certification BuildList(Certification list, AttributeModel model)
and so, it should require returning an object of type Certification. I'm obviously not understanding something basic about generics here - what do I have wrong?
Update 2:
I tried the following per @DStanley - this also won't compile:
public class Animal
{
public T BuildList<T>(T list, AttributeModel model) where T : Animal
{
return new Animal();
}
}
Because the constraint says that T derives from Certification. It does not say that T is Certification.
Let's replace Certification with a different type and see if it makes more sense:
public T BuildList<T>(T list, AttributeModel model) where T : Animal
{
return new Animal("", "", "");
}
Now what if you called Dog d = BuildList<Dog>()? The method will return an Animal, not a Dog.
The compiler knows that the method is supposed to return a T but instead it is returning a Certification, which works if T is Certification, but not if T is a type that derives from Certification. You can return a more specific type but not a less specific one:
public Dog getThing<Dog>()
{
return new Animal(); // obviously fails - an Animal is not necessarily a Dog
return (Dog)(new Animal()); // This will _compile_ but will fail at runtime since the Animal is not a Dog
}
public Animal getThing<Animal>()
{
return new Dog(); // Dog is a type of Animal so this is fine.
}
Whether in your case the return type should just be Certification or if you should somehow return an object of type T is unclear from the context.
I read "where T : Certification" to mean that the method can also be read as:
public Certification BuildList(Certification list, AttributeModel model)
this is true only if T is Certification when the method is called. Since it is generic, the method can be called for any type that inherits from Certification, so if you had
public class MyCertification : Certification;
and use that for T (meaning you call var o = BuildList<MyCertification>(...)) the method now effectively becomes:
public public MyCertification BuildList(MyCertification list, AttributeModel model)
{
return new Certification("", "", "");
}
since Certification is not a MyCertification (in the same way that a generic Animal is not a Dog), this would fail.
The compiler requires that whatever is returned from the method derives from T (NOT Certification). since Certification would not derived from a type that itself derives from Certification, the compiler tells you that the return type is invalid.
The compiler doesn't make T a Certification. It's a wrapper. Consider the following code:
internal class Program
{
static void Main(string[] args)
{
var dog = new Dog();
var cat = new Cat();
var farm = new AnimalFarmGeneric<Animal>();
farm.FeedAnimal(dog);
farm.FeedAnimal(cat);
var catFarm = new AnimalFarmGeneric<Cat>();
var strayCat = catFarm.GetAnimal();
//catFarm.FeedAnimal(strayCat); // compilation error; Animal is not Cat
farm.FeedAnimal(strayCat); // works, strayCat is Animal
}
public class AnimalFarmGeneric<T> where T : Animal
{
public T FeedAnimal(T animal)
{
animal.Feed();
return animal;
}
// examples for showing difference in IL
public T GetAnimalGeneric()
{
return (T)new Animal(); // Fails if T is Dog or Cat
}
public Animal GetAnimal()
{
return new Animal();
}
}
public class Animal
{
public bool IsHungry = true;
public virtual void Feed()
{
IsHungry = false;
}
}
public class Dog : Animal
{
public override void Feed()
{
Console.WriteLine("Fed animal dog food.");
base.Feed();
}
}
public class Cat : Animal
{
public override void Feed()
{
Console.WriteLine("Fed animal cat food");
base.Feed();
}
}
}
If we look at the IL generated for GetAnimal and GetAnimalGeneric, you will see they are different return types.
GetAnimal:
.method public hidebysig instance class ScratchConsole.Program/Animal
GetAnimal() cil managed
{
// Code size 11 (0xb)
.maxstack 1
.locals init (class ScratchConsole.Program/Animal V_0) <-- Concrete Type
IL_0000: nop
IL_0001: newobj instance void ScratchConsole.Program/Animal::.ctor()
IL_0006: stloc.0
IL_0007: br.s IL_0009
IL_0009: ldloc.0
IL_000a: ret
} // end of method AnimalFarmGeneric`1::GetAnimal
GetAnimalGeneric:
.method public hidebysig instance !T GetAnimalGeneric() cil managed
{
// Code size 16 (0x10)
.maxstack 1
.locals init (!T V_0) <---- Generic Type
IL_0000: nop
IL_0001: newobj instance void ScratchConsole.Program/Animal::.ctor()
IL_0006: unbox.any !T <---- unboxing
IL_000b: stloc.0
IL_000c: br.s IL_000e
IL_000e: ldloc.0
IL_000f: ret
} // end of method AnimalFarmGeneric`1::GetAnimalGeneric
The confusion for me seems to be that when you use where T : Certification it is easy to read that as T is a Certification. It's really means that T and Certification are assignment compatible. The specification goes into this further than I can.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With