How to Do Template Specialization in C#

template specialization via extension method

Unfortunately, when you call Method() it is running as Thing<T>, not knowing it is Thing<int>. You can use reflection to find this out, or (better idea):

if(this instanceof Thing<int> x)
x.Specialisation();
else
this.Specialisation();

Alternatively you can declare Method() as virtual and create IntThing : Thing<int> with override void Method(){...}

C# specialize generic class

I guess the closer you could get in C# would be:

class GenericPrinter<T>
{
public virtual void Print()
{
Console.WriteLine("Unspecialized method");
}
}

class IntPrinter : GenericPrinter<int>
{
public override void Print()
{
Console.WriteLine("Specialized with int");
}
}

Otherwise, the answer is, you can't specialize in C#.

As Lyubomyr Shaydariv said in his comment:

C++ templates are not .NET generics. You can't.


From your edit I guess you will have some type checking to make.

You can do this with a dictionary for example.

class GenericPrinter<T>
{
private Dictionary<Type, Action> _actions
= new Dictionary<Type, Action>()
{
{ typeof(int), PrintInt }
};

public virtual void Print()
{
foreach (var pair in _actions)
if (pair.First == typeof(T))
{
pair.Second();
return ;
}
Console.WriteLine("Unspecialized method");
}

public virtual void PrintInt()
{
Console.WriteLine("Specialized with int");
}
}

Like you can see, you will have to make a method for each type, you want to handle. And you may also encounter some issues when you will try to manipulate T as int. Since, T is really generic (it hasn't any constraint), it will more likely act as an object in your code (not at runtime) you will have to cast it like that (int)(object)yourTVariable in your methods where you are sure that T is an int.

But for this part, I guess some of my peers, will have a better answer than me to give to you.


If it's just about displaying which type you are using:

public virtual void Print()
{
Console.WriteLine($"Specialized with {typeof(T).Name}");
}

But you won't have the unspecialized message anymore (and if you think about it, you can't have a GenericPrinter instantiated without specifying its type. Then it makes no sense to have a method that displays "unspecialized", you will always have a specified type)

Anyway, the answer is still the same, you can't specialize a generic in C#.

Template Specialization Workaround

No, that's not possible, you have to use dynamic dispatching

public class C
{
public int DoWork<T>()
{
if (typeof(T) == typeof(int))
return DoWorkInt();

return 13;
}
private int DoWorkInt() { return 42; }
}

C# does not support explicit specialization; that is, a custom implementation of a template for a specific type.

From Differences Between C++ Templates and C# Generics

Workaround to template specialization

If you're fine with making Base a generic class, this is how:

abstract class Base<T>
{
public abstract void Method(T args);
}

class Derived : Base<int>
{
public override void Method(int args) {}
}

class AnotherBaseClass<T, E>
where E : Base<T>
{
E e;
T t;

public void Func()
{
e.Method(t);
}
}

If you need Base to not be generic, you can add this:

abstract class Base
{
public void Method<T>(T args)
{
var genericSelf = this as Base<T>;
genericSelf.Method(args);
}
}

and make Base<T> inherit Base and ensure your concrete classes always derive Base<T> and never Base directly.

C# generic interface specialization

Overload resolution is performed at compile-time, not at run-time based on the actual type of the passed value.

IStorage i = new Storage();
i.Store("somestring"); // Prints Generic
i.Store(1); // Prints Generic

This will always call the "generic" method, because there is only one overload of Store in IStorage and the compiler doesn't know that i actually contains a Storage object. How can the compiler know about the other overload in Storage?

Storage s = (Storage)i;
s.Store("somestring"); // Prints Generic
s.Store(1); // Prints Specific

Here, the compiler knows that s contains a Storage object (or one deriving from Storage), because s is declared that way. So it sees two overloads. It chooses the specific overload for int values, because overload resolution rules say to prefer specific overloads over generic overloads.


It's technically possible to determine typeof(T) in the generic method at run-time and forward the method call to a specific method. But if you think about it, this doesn't make a lot of sense. A generic method means that the same implementation works for arguments of different, unrelated types. If you want different implementations for different types, you shouldn't use generics for this.


void Foo<T>(T t)
{
SubFoo(t);
}

void SubFoo<T>(T t);
void SubFoo(int t);

Generics work quite a bit different from C++ templates. The C# compiler compiles Foo only once -- to a generic method. Remember: generic means same implementation for different types. The C# compiler does not know at compile-time if T is going to be an int or a string or any other type. So the only possible implementation of Foo that works for any T is to call SubFoo<T>. If one of the SubFoo overloads would be called depending on T, the implementation of Foo wouldn't be the same for all T any more.

Why is specialization in C# generics limited?


What does it mean by "specialization"? Is it not the same as instantiation of a generic type with a specific type argument?

Author explains in the portion of his answer dedicated to Java generics that

specialization of a generic type [is] the ability to use specialized source code for any particular generic argument combination.

In other words, it is an ability to do something special if a generic type parameter is of a specific type. Supplying an implementation of List<T> that represents individual elements as bits when you instantiate the type as List<bool> would be an example of specialization.

What does it mean by "the degree of specialization is limited"?

Author means that although you can write things like

if (typeof(T) == typeof(bool)) {
...
}

your abilities to respond to a combination of type arguments are limited, because any decision on a type combination has to be made at run-time.

Why is it "a result of the fact that a generic type definition is compiled before any reification happens"?

Because reification is done in CLR, well after C# compiler is out of the picture. The compiler must produce a generic type definition for CLR to use as a "template" for making closed constructed types for instances of a generic class.



Related Topics



Leave a reply



Submit