views:

2293

answers:

5

How would you do specialization in C#? I'll pose a problem. You have a template type, you have no idea what it is. But you do know if its derived from XYZ you want to call .alternativeFunc(). A great way is to call a specialized function or class and have normalCall return .normalFunc() while have the other specialization on any derived type of XYZ to call .alternativeFunc(). How would this be done in C#?

+7  A: 

Hi, assuming you're talking about template specialization as it can be done with C++ templates - a feature like this isn't really available in C#. This is because C# generics aren't processed during the compilation and are more a feature of the runtime.

However, you can achieve similar effect using C# 3.0 extension methods. Here is an example that shows how to add extension method only for "MyClass" type, which is just like template specialization. Note however, that you can't use this to hide default implementation of the method, because C# compiler always prefers standard methods to extension methods:

class MyClass<T> {
  public int Foo { get { return 10; } }
}
static class MyClassSpecialization {
  public static void Bar(this MyClass<int> cls) {
    return cls.Foo + 20;
  }
}

Now you can write this:

var cls = new MyClass<int>();
cls.Bar();

[EDIT] BTW: If you want to have a default case for the method that would be used when no specialization is provided, than I believe writing one generic "Bar" extension method should do the trick:

  public static void Bar<T>(this MyClass<T> cls) {
    return cls.Foo + 42;
  }

[/EDIT]

Tomas Petricek
Foo property vs Bar method... doesn't really seem like a typical specialization...
Marc Gravell
Thats a great solution, +1
acidzombie24
No, it's not typical specilaziation, but it's the only easy thing that you can do... (AFAIK)
Tomas Petricek
A: 

If you just want to test if a type is derrived from XYZ, then you can use:

theunknownobject.GetType().IsAssignableFrom(typeof(XYZ));

If so, you can cast "theunknownobject" to XYZ and invoke alternativeFunc() like this:

XYZ xyzObject = (XYZ)theunknownobject; 
xyzObject.alternativeFunc();

Hope this helps.

Jeroen Landheer
I don't know much C#, but whoever voted you down should of said why. I have no idea whats wrong when your answer or if anything is wrong with it.
acidzombie24
Not sure either. It seems valid enough to me. Although a bit more verbose than necessary.
jalf
A: 

C# doesn't have templates or specialization. Generics are not templates. What you're asking is not possible.

However, you can use the 'is' operator to test the type in your code:

if (foo is XYZ) { (foo as XYZ).alternativeFunc(); }
else { foo.normalFunc(); }

You can use IsAssignableFrom as Jeroen suggested too, but I find it less intuitive (because the name sounds as if it would take user-defined conversions into account, giving you false positives - which it doesn't), and because it's more verbose.

jalf
Now you're using two expensive casts instead of just one, plus a null test.
Konrad Rudolph
+2  A: 

In C#, the closest to specialization is to use a more-specific overload; however, this is brittle, and doesn't cover every possible usage. For example:

void Foo<T>(T value) {Console.WriteLine("General method");}
void Foo(Bar value) {Console.WriteLine("Specialized method");}

Here, if the compiler knows the types at compile, it will pick the most specific:

Bar bar = new Bar();
Foo(bar); // uses the sepcialized method

However....

void Test<TSomething>(TSomething value) {
    Foo(value);
}

will use Foo<T> even for TSomething=Bar, as this is burned in at compile-time.

One other approach is to use type-testing within a generic method - however, this is usually a poor idea, and isn't recommended.

Basically, C# just doesn't want you to work with specializations, except for polymorhism:

class SomeBase { public virtual void Foo() {...}}
class Bar : SomeBase { public override void Foo() {...}}

Here Bar.Foo will always resolve to the correct override.

Marc Gravell
A: 

A late answer on this question.

By adding an intermediate class and a dictionary, specialization is possible.

To specialize on T, we create an generic interface, having a method called (e.g.) Apply. For the specific classes that interface is implemented, defining the method Apply specific for that class. This intermediate class is called the traits class.

That traits class can be specified as a parameter in the call of the generic method, which then (of course) always takes the right implementation.

Instead of specifying it manually, the traits class can also be stored in a global IDictionary of . It can then be looked up and voila, you have real specialization there.

If convenient you can expose it in an extension method.

class MyClass<T>
{
    public string Foo() { return "MyClass"; }
}

interface BaseTraits<T>
{
    string Apply(T cls);
}

class IntTraits : BaseTraits<MyClass<int>>
{
    public string Apply(MyClass<int> cls)
    {
        return cls.Foo() + " i";
    }
}

class DoubleTraits : BaseTraits<MyClass<double>>
{
    public string Apply(MyClass<double> cls)
    {
        return cls.Foo() + " d";
    }
}

// Somewhere in a (static) class:
public static IDictionary<Type, object> register;
register = new Dictionary<Type, object>();
register[typeof(MyClass<int>)] = new IntTraits();
register[typeof(MyClass<double>)] = new DoubleTraits();

public static string Bar<T>(this T obj)
{
    BaseTraits<T> traits = register[typeof(T)] as BaseTraits<T>;
    return traits.Apply(obj);
}

var cls1 = new MyClass<int>();
var cls2 = new MyClass<double>();

string id = cls1.Bar();
string dd = cls2.Bar();

See this link to my recent blog and the follow ups for an extensive description and samples.

Barend Gehrels