Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# specialize generic class

Is it possible to do the following specialization in C#? I can do this in C++ but do not understand how to achieve the same result in C#.

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

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

Added:

The problem with suggested GenericPrinterInt solution is that I need to explicitly create it. new GenericPrinter<int> will still print Unspecialized method.

What I want is to use this GenericPrinter from another generic class without the knoledge is T equal to int or something else.

like image 444
Nosturion Avatar asked Nov 07 '16 17:11

Nosturion


2 Answers

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#.

like image 89
romain-aga Avatar answered Oct 03 '22 21:10

romain-aga


It isn't possible in C#.
You can use inheritance instead:

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

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

According to the updated question, I can only suggest you the following approach. You could create a static factory method in which you can check the type of T and instantiate an appropriate specialized class if the type matches the criteria:

class GenericPrinter<T>
{
    public static GenericPrinter<T> Create()
    {
        if (typeof(int).IsAssignableFrom(typeof(T)))
            return (GenericPrinter<T>)(object)new GenericPrinterInt();

        if (typeof(double).IsAssignableFrom(typeof(T)))
            return (GenericPrinter<T>)(object)new GenericPrinterDouble();

        // Other types to check ...

        return new GenericPrinter<T>();
    }

    public virtual void Print()
    {
        Console.WriteLine("Unspecialized method");
    }
}

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

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

Some other generic class:

class SomeGenericClass<T>
{
    public readonly GenericPrinter<T> Printer = GenericPrinter<T>.Create();
}

Usage sample:

var intClass = new SomeGenericClass<int>();
intClass.Printer.Print();
// Output: Specialized with int

var doubleClass = new SomeGenericClass<double>();
doubleClass.Printer.Print();
// Output: Specialized with double

var stringClass = new SomeGenericClass<string>();
stringClass.Printer.Print();
// Output: Unspecialized method
like image 27
Dmitry Avatar answered Oct 03 '22 21:10

Dmitry