2

In C#, I'd like to have a method of my generic class behave in different ways, using something akin to explicit template specialization

I see from a related post that such a thing might be possible in C#, using extensions, but my code is not behaving as desired: I want one behaviour for Thing<int>::Method() and another behaviour when <T> is anything other than <int>.

I have

using System;

public interface IThing
{
    void Method();
}

public class Thing<T> : IThing
{
    public void Method() 
    {
        this.Specialization<T>();
    }
}

public static class ThingExtensions
{
    public static void Specialization<T>(this Thing<T> cls)
    {
        Console.WriteLine("Thing<T> specialization");
        return;
    }

    public static void Specialization(this Thing<int> cls)
    {
        Console.WriteLine("Thing<int> specialization");
        return;
    }
}

public class Program
{
    public static void Main()
    {
        var t = new Thing<float>();
        t.Method();
        t.Specialization();

        var i = new Thing<int>();
        i.Method();
        i.Specialization();
    }
}

But this outputs

Thing<T> specialization
Thing<T> specialization
Thing<T> specialization
Thing<int> specialization

Rather than

Thing<T> specialization
Thing<T> specialization
Thing<int> specialization
Thing<int> specialization

The obvious question "why not just call Specialization<T> rather than Method?" is hopefully answered by my inclusion of the interface class - I am trying to fit into its framework.

I can see that Specialization<T> is a match for Specialization<int>, but I'm surprised that the latter is not regarded as a better match!

Servy
  • 202,030
  • 26
  • 332
  • 449
Neil Gatenby
  • 419
  • 6
  • 21
  • 1
    If your only concern is type int, why not just use one specialization method of type and check the type before performing your logic? – Ryan Wilson Nov 26 '18 at 14:58
  • 1
    Generics aren't templates. What you are asking sounds like traits anyway, not template specialization. The clean solution is coming in C# 8 with [default interface implementations](https://github.com/dotnet/csharplang/issues/288). I posted an example in [a related question last week](https://stackoverflow.com/questions/10729230/how-would-you-implement-a-trait-design-pattern-in-c/53450332#53450332) – Panagiotis Kanavos Nov 26 '18 at 14:59
  • 1
    Overload resolution happens at *compile time*. When `Method` is being *compiled*, what type information does it have to hand, and so which `Specialization` overload is it going to choose? – Damien_The_Unbeliever Nov 26 '18 at 15:01
  • 1
    The [accepted answer](https://stackoverflow.com/a/10729336/134204) in the traits question shows how to work with "traits" without official support - different traits have to implemented using extension methods that target different interfaces, one per "trait". They'll have to be called through that interface too – Panagiotis Kanavos Nov 26 '18 at 15:03

2 Answers2

2

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(){...}

Gerino
  • 1,943
  • 1
  • 16
  • 21
1

The call to this.Specialization<T>(); is resolved when the containing method is compiled. At that point T is anything.

Some languages with generic support (eg. C++) resolve the implementation of generic methods as late as possible, when all type parameters (or in C++'s case all template parameters: in C++ template parameters are not restricted to types).

But C# does not take this approach. This does have the advantage of making the compilation model simpler, but it is less powerful.

(Personally I would like the ability to explicitly specialise, including partial specialisation, but I also recognise this has challenges for the way types are resolved in .NET.)

Richard
  • 106,783
  • 21
  • 203
  • 265