I've been playing around with generics and I've been seeing some weird stuff. I hope you guys have an explanation! To make everything easier I've put the "problem" into an example:
namespace Lab
{
public class Animal
{
public Animal(string sound)
{
this.Sound = sound;
}
public string Sound { get; private set; }
public void Kick()
{
Printer.Print(this, Sound);
}
}
public class Dog : Animal
{
public Dog() : base("Bark, bark! I'll bite you!") { }
}
public class Printer
{
public static void Print<T>(T obj, string message)
{
System.Console.WriteLine("{0} says '{1}' \n", typeof(T).FullName.PadRight(10), message);
}
}
public static class Program
{
static void Main(string[] args)
{
Animal bird = new Animal("Tweet!");
Dog dog = new Dog();
System.Console.WriteLine("Kick bird:");
bird.Kick();
System.Console.WriteLine("Kick dog:");
dog.Kick();
System.Console.WriteLine("Print kick dog:");
Printer.Print(dog, dog.Sound);
System.Console.ReadLine();
}
}
}
So, I have two animals in my Lab: a dog and a bird. When I "kick" those animals they'll make a sound. The printer will print the sound and the type of animal. When I run the program it prints:
Kick bird: Lab.Animal says 'Tweet!'
Kick dog: Lab.Animal says 'Bark, bark! I'll bite you!'
Print kick dog: Lab.Dog says 'Bark, bark! I'll bite you!'
Why does the first kick of the dog tell me it is of the type Lab.Animal
?
And... how can I get it to return Lab.Dog
?
From the point of view of reflection, the difference between a generic type and an ordinary type is that a generic type has associated with it a set of type parameters (if it is a generic type definition) or type arguments (if it is a constructed type). A generic method differs from an ordinary method in the same way.
Use generic types to maximize code reuse, type safety, and performance. The most common use of generics is to create collection classes. The . NET class library contains several generic collection classes in the System.
The first kick of the dog tells you that the compile-time type of the type argument was Lab.Animal. In other words, your Animal.Kick
method is effectively:
Printer.Print<Animal>(this, Sound);
Type arguments aren't determined polymorphically - they're determined at compile-time. It becomes more complicated when the type argument of one call is actually the type parameter of the calling context, but it's fundamentally the same kind of thing.
To make it say Lab.Dog
, you'd have to get the actual execution-time type of the object, e.g. using
obj.GetType().FullName
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