I'm a C++ developer who tries to play with C# generics. My final goal is to mimic template specialization in C#.
In other term, I want a default implementation of a function available for all types and a specific implementation for a specific type.
My first attempt was with generics and extension methods but it seems C# generics are resolved at run-time, which gives this weird result (weird for a C++ programmer at least):
//any of my classes
public class MyClass
{
}
//class extension for my classes only
public static class MyClassExt
{
public static string GetCaption(this MyClass c)
{
return "MyClass caption";
}
}
//"default" extension for all
public static class objectExt
{
public static string GetCaption(this object o)
{
return $"object caption {o}";
}
}
internal class Program
{
public static string GetCaption<T>(T t)
{
//Isn't it supposed to call MyClassExt.GetCaption if t is MyClass ?
return t.GetCaption();
}
static void Main(string[] args)
{
int i = 0;
Console.WriteLine(i.GetCaption()); //> object caption 0
Console.WriteLine(new MyClass().GetCaption()); //> MyClass caption
//calls GetCaption<T> with T is MyClass
Console.WriteLine(GetCaption(new MyClass())); //> object caption csharpconsole.MyClass
}
}
What is the usual way to handle such case in C#?
Edit:
trying to implement @Enigmativity suggestion, I've tried this with no success:
//class extension for my classes only
public static class MyClassExt
{
//My attempt for @Enigmativity suggestion
public static string GetCaption<T>(this T t) where T : MyClass
{
return "MyClass caption";
}
}
//"default" extension for all
public static class objectExt
{
public static string GetCaption(this object o)
{
return $"object caption {o}";
}
}
internal class Program
{
public static string GetCaption<T>(T t)
{
//Isn't it supposed to call MyClassExt.GetCaption if t is MyClass ?
return t.GetCaption();
}
static void Main(string[] args)
{
int i = 0;
Console.WriteLine(i.GetCaption()); //> object caption 0
Console.WriteLine(new MyClass().GetCaption()); //> MyClass caption
//calls GetCaption<T> with T is MyClass
Console.WriteLine(GetCaption(new MyClass())); //> object caption csharpconsole.MyClass
}
}
My final goal is to mimic template specialization in c#.
I think that's unlikely to end well, to be honest. Generics aren't like C++ templates; they're used to accomplish similar goals, but with quite different approaches.
My first attempt was with generics and extension methods but it seems c# generics are resolved at run-time
No, that's not true - which explains the result you're getting.
When compiling the code for GetCaption<T>, the compiler resolves the call to t.GetCaption() without knowing anything about T, so the only extension method it can call is the one with the first parameter of type object - which is what you're seeing.
Basically, you need to remember that each generic method is only compiled once - not once against each type argument. Everything flows from that understanding: you can see polymorphism in terms of overriding, as normal, but not overloading where a different method is chosen at compile-time.
If you change the extension method to this:
public static string GetCaption<T>(T t) where T : MyClass
Then it will call the MyClass extension method as the compiler can tell that this is the most specific method to call.
Without the where T : MyClass then the object extension method is the most specific that it can determine at compile-time.
When I run your code with where T : MyClass I get:
object caption 0
MyClass caption
MyClass caption
Without it, exactly as you posted it, I get:
object caption 0
MyClass caption
object caption MyClass
Here is the full working program.
//any of my classes
public class MyClass
{
}
//class extension for my classes only
public static class MyClassExt
{
public static string GetCaption(this MyClass c)
{
return "MyClass caption";
}
}
//"default" extension for all
public static class objectExt
{
public static string GetCaption(this object o)
{
return $"object caption {o}";
}
}
internal class Program
{
public static string GetCaption<T>(T t) where T : MyClass
{
//Isn't it supposed to call MyClassExt.GetCaption if t is MyClass ?
return t.GetCaption();
}
static void Main(string[] args)
{
int i = 0;
Console.WriteLine(i.GetCaption()); //> object caption 0
Console.WriteLine(new MyClass().GetCaption()); //> MyClass caption
//calls GetCaption<T> with T is MyClass
Console.WriteLine(GetCaption(new MyClass())); //> object caption csharpconsole.MyClass
}
}
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