views:

113

answers:

5

Edit: C# 3.0, net 3.5.

I am C++ programmer, so maybe I miss some simple solution in C#.

Simplified example: I have several classes inherited from class MyBase (with method Inc). The inherited classes may override Inc. I have several overloaded "functions" (static methods) for the inherited classes:

void Print(MyInherited1 i1) ....
void Print(MyInherited2 i2) ....

and so on. Edit: those methods are "external" to the MyBase and MyInherited, they are not part of any of those classes.

I have generics function that modify its argument and call the Print function:

void Transform<T>(T t)
{
    t.Inc();
    Print(t);
}

Edit: this is simplified example, in real code I cannot convert the Transform method to non-generic one using just polymorphism.

Now, in C++ it would work just like that. However in C# method Inc is unknown. So I specify that T is of type MyBase.

void Transform<T>(T t) where T : MyBase
{
    t.Inc();
    Print(t);
}

but I still have the problem with Print -- there is no such method for the base class.

As a workaround I used ugly (!) solution -- PrintProxy where I put several

if (t is Inherited1)
  Print(t as Inherited1);
else if ...

How to mix those two concepts -- overloading and generics? Nice, C# way?

Thank you in advance for help.

A: 

What is the access modifier on the .Print(..) method in your MyBase class? If none is specified it's private by default which would preclude derived classes from accessing it - mark it protected at least.

protected void Print(MyBase obj) {
    //do stuff
}

If for some reason Print(..) isn't in the inheritance hierarchy and you're trying to use it in a composited way try public (although your description indicates this isn't the case).

public void Print(MyBase obj) { //...

Public is a good way to debug it because your code is most apt to "see" it whether from the same assembly or a different one.

John K
not an answer to the question
Natrium
There is no visibility issue in this question.
Lucero
Possibly I jumped the gun or misunderstood. We'll see ...
John K
+2  A: 

That's by design. The compiler has to know at compile time what method (overload) to bind to for the unbound (!) generic class, and in your case, this would only be known after the generic type has been set.

In contrast to the C++ templates, generics are working in a different way even though they share a similar syntax.

Lucero
Thank you, but I understand that. I am asking "how" not "why".
macias
+3  A: 

One option in C# 4 is to use dynamic typing:

void Transform<T>(T t)
{
    t.Inc();
    dynamic d = t;
    Print(d);
}

That will perform the overload resolution for you at execution time - which is basically the best you can do unless you can provide a genuinely generic Print method, as there will only be one version of Transform generated.

Note that there's no real need to be generic at this point - you could replace it with:

void Transform(MyBase t)
{
    ...
}

I typically find that constraints based on interfaces are more useful than those based on classes, unless I'm also doing something else generic (such as creating a List<T> which should be of the right actual type).

Obviously this dynamic approach has downsides:

  • It requires .NET 4.0
  • It's slower than a compile-time binding (although it's unlikely to be significant unless you're calling it a heck of a lot)
  • There's no compile-time validation (you can add an overload which takes object as a sort of fallback to provide custom error handling, but you still have to wait until execution time)

Basically this is just a difference between .NET generics and C++ templating - they're very different creatures, even if they tackle many similar problems.

Rather than having static Print methods, is there any reason you can't write an abstract Print method in MyBase, and override it in each class? That feels like a more OO solution anyway, to be honest - although obviously it doesn't make sense if the printing is actually somewhat logically distant from the class itself. Even if you don't want the actual Print method in the original type hierarchy, might you be able to expose enough functionality to let you write a virtual Print method? I assume all these methods should come up with some similar kind of result, after all.

EDIT: A couple of alternative ideas...

Passing in the printer

You can pass in a delegate to do the printing. If you're calling this from a non-generic context which knows the actual type, you can take advantage of method group conversions to make this simple. Here's a short but complete example:

using System;

class Test
{
    static void SampleMethod<T>(T item, Action<T> printer)
    {
        // You'd do all your normal stuff here
        printer(item);
    }

    static void Print(string x)
    {
        Console.WriteLine("Here's a string: {0}", x);
    }

    static void Print(int x)
    {
        Console.WriteLine("Here's an integer: {0}", x);
    }

    static void Main()
    {
        SampleMethod(5, Print);
        SampleMethod("hello", Print);
    }
}

Use a type/delegate dictionary

Another option is to have a Dictionary<Type, Delegate> containing the printing methods. This could either be inlined (if the printing methods are simple) or something like this:

static readonly Dictionary<Type, Delegate> Printers = 
    new Dictionary<Type, Delegate>
{
    { typeof(MyClass1), (Action<MyClass1>) Print },
    { typeof(MyClass2), (Action<MyClass2>) Print },
    { typeof(MyClass3), (Action<MyClass3>) Print },
};

Then in your method:

Delegate printer;
if (Printers.TryGetValue(typeof(T), out printer))
{
    ((Action<T>) printer)(t);
}
else
{
    // Error handling
}

This is another execution time solution though, and you'd need a bit more work if you wanted it to handle further derivation (e.g. walking up through the base classes if it can't find the relevant printer).

Jon Skeet
Thank you. The problem is I am still in 3.5 world, and the second -- I oversimplified example, I **have to** use generics because of the content of this (generic) method -- I cannot use polymorphism instead.So, is the nice way in 3.5 to mix generics and overloading? Nicer than my "solution"?
macias
@macias: I've added a couple of extra options. There's nothing really nice here though, due to the differences between .NET generics and C++ templates.
Jon Skeet
Great! I will use the first solution (with Action class) because this way I won't repeat the listing of all types -- when you add another class you would have to write new overloaded function Print and add one new entry in Dictionary. I agree though C++ is nicer here :-)
macias
A: 

why not using an interface?

void Transform<T>(T t) where T : MyBase, IMyPrintableClass
{
    t.Inc();
    t.Print();
}
Natrium
I think the problem is that the class doesn't know how to print itself - that code lies elsewhere. Whether it *should* or not is a different matter, but we can't really judge that without more context.
Jon Skeet
You assume that I can modify MyBase class (btw. next time I start with naming classes NotMyClass ;-) ).
macias
nope, I assume that your derived class implements an interface with a Print()-method
Natrium
+1  A: 

My first suggestion would be to use an interface instead of generics. I see no point in using generics in this example.

 void Transform(IMyBase t)
 {
    t.Inc();
 }

where IMyBase is:

 interface IMyBase 
 {
     void Inc();
 }

Option A

[Edit] Since Print does not belong to IMyBase, you could separate the printing logic completely and do something like this:

 void Transform(IMyBase t, IPrintLogic printLogic)
 {
    t.Inc();
    printLogic.Print(t);
 }

where IPrintLogic is defined as:

 interface IPrintLogic 
 {
     void Print(IMyBase t);
 }

now you have the freedom to instantiate any print logic you want:

 MyInherited1 obj = new MyInherited1();
 MyPrintLogic printer = new MyPrintLogic();
 Transform(obj, printer);

or, use a factory of some sort:

 Transform(obj, PrintLogicFactory.Create(obj));

Code inside your factory could then be similar to a bunch of if/then/else blocks, like you have right now.

Option B - Creating an intermediate object (like the Adapter pattern)

Depending on what your Transform method actually does, this may be an option also:

 IPrepared Transform(IMyBase t)
 {
    t.Inc();
    return this.PrepareForPrinting(t);
 }

and then print the IPrepared object, which is "prepared" for printing in a way:

 interface IPrintLogic 
 {
    void Print(IPrepared t);
 }

In that case, you would use it like:

 MyInherited1 obj = new MyInherited1();
 IPrepared prepared = Transform(obj);

 MyPrintLogic printer = new MyPrintLogic();
 printer.Print(prepared);
Groo
Thank you! Your A solution is similar to Jon Action one, but the latter one requires less writing -- because I don't have to put each function in separate class. I don't understand the B solution -- what is "this" inside Transform? The generics is also gone. But anyway, this is possible only when you call only once overloaded function.
macias
`this` keyword only means that `PrepareForPrinting` is a method inside the class which contains `Transform` method. No real reason, just to indicate that some processing happens inside that class and returns an intermediate result which can be easily printed regardless of the source. This means that you could eventually do something else with that intermediate stuff (e.g. copy it to clipboard instead of printing it), because `Transform` would no longer do the actual printing.
Groo