How can I overload a C # method with specific instances of a generic type

Coming from a C ++ background, I ran into overloading with overloading based on a specific instance of a generic type. The following doesn't work as only one instance of the code for the class is Foo<T>

always generated, so internally the Method

type this

is simple Foo<T>

and not Foo<A>

or Foo<B>

as I hoped. In C ++ I use to create templates as unique types.

using System.Collections.Generic;
class A
{
    // Concrete class
}

class B
{
    // Concrete class
}

class Bar
{
    public void OverloadedMethod(Foo<A> a) {} // do some A related stuff
    public void OverloadedMethod(Foo<B> b) {} // do some B related stuff
    public void OverloadedMethod(OtherFoo of) {} // do some other stuff

     public void VisitFoo(FooBase fb) { fb.Method(this); }
}

abstract class FooBase
{
    public abstract void Method(Bar b);
}

class Foo<T> : FooBase
{
    // Class that deals with As and Bs in an identical fashion.
    public override void Method(Bar b)
    {
        // Doesn't compile here
        b.OverloadedMethod(this);
    }
}

class OtherFoo : FooBase
{
    public override void Method(Bar b)
    {
        b.OverloadedMethod(this);
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<FooBase> ListOfFoos = new List<FooBase>();
        ListOfFoos.Add(new OtherFoo());
        ListOfFoos.Add(new Foo<A>());
        ListOfFoos.Add(new Foo<B>());

        Bar b = new Bar();
        foreach (FooBase fb in ListOfFoos)
            b.VisitFoo(fb);
        // Hopefully call each of the Bar::Overloaded methods
    }
}

      

Is there a way to make something like this work in C #? I would rather not duplicate the code in Foo as separate classes for each type in which I want to use it.

Edit: Hopefully this is a little clearer.

+1


source to share


5 answers


This works for the static case. Working with instance functions will be a little more complicated. This post from Jon Skeet may provide a sane way to deal with instance methods.



class Program
{
    static void Main(string[] args)
    {
        var testA = new Foo<A>();
        testA.Method();
        var testB = new Foo<B>();
        testB.Method();
        Console.ReadLine();
        var testString = new Foo<string>(); //Fails
        testString.Method(); 
        Console.ReadLine();
    }
}

class A { }
class B { }
class Bar
{
    public static void OverloadedMethod(Foo<A> a)
    {
        Console.WriteLine("A");
    }
    public static void OverloadedMethod(Foo<B> b)
    {
        Console.WriteLine("B");
    }
}
class Foo<T>
{
    static Foo()
    {
        overloaded = (Action<Foo<T>>)Delegate.CreateDelegate(typeof(Action<Foo<T>>), typeof(Bar).GetMethod("OverloadedMethod", new Type[] { typeof(Foo<T>) }));
    }

    public void Method()
    {
        overloaded(this);
    }

    private static readonly Action<Foo<T>> overloaded;
}

      

+1


source


I now have a really complete piece of code that demonstrates the problem. Note to OP: Try compiling your code before submitting it. There were tons of things I had to do to get this far. It's good to make it as easy as possible for other people to help you. I've also removed a bunch of extraneous bits. OtherFoo is not very relevant here, and is not FooBase.

class A {}
class B {}

class Bar
{
    public static void OverloadedMethod(Foo<A> a) { }
    public static void OverloadedMethod(Foo<B> b) { }
}

class Foo<T>
{
    // Class that deals with As and Bs in an identical fashion.
    public void Method()
    {
        // Doesn't compile here
        Bar.OverloadedMethod(this);
    }
}

      



Yes it doesn't compile. What exactly did you expect from this? Be aware that overload resolution is done at compile time, not runtime. As patch 888 says, you can use and intercept the appropriate overloaded method, but which of the two overloads do you expect the compiler to choose differently? What do you want to do with Foo<string>

instead of Foo<A>

or Foo<B>

?

All of this suggests that .NET generics are indeed significantly different from C ++ templates, of course ...

+3


source


I haven't tried it, but it looks like you should achieve what you want by making A and B available (with an acyclic visitor pattern for example).

+2


source


Edit: I'm not sure if you can complete this when you try. I've tried all sorts of tricks to try and get this to work and can't get it to compile. The best I can do is move the method call outside of my Generic class. If the method is called from outside, you can specifically define that T is in a generic value. However, inside a method at compile time, the compiler doesn't know what T is, so it doesn't know which overloaded method to call. The only way I can see is to use a switch to define the type of T and manually specify the overload to call.

The best I can do is that it isn't exactly what you want, but it can be used for a similar effect:

class Stuff<T>
{
    public T value { get; set; }
}

class Program
{
    static void DummyFunc(Stuff<int> inst)
    {
        Console.WriteLine("Stuff<int>: {0}", inst.value.ToString());
    }

    static void DummyFunc(Stuff<string> inst)
    {
        Console.WriteLine("Stuff<string>: {0}", inst.value);
    }
    static void DummyFunc(int value)
    {
        Console.WriteLine("int: {0}", value.ToString());
    }
    static void DummyFunc(string value)
    {
        Console.WriteLine("string: {0}", value);
    }
    static void Main(string[] args)
    {
        var a = new Stuff<string>();
        a.value = "HelloWorld";

        var b = new Stuff<int>();
        b.value = 1;

        var c = "HelloWorld";
        var d = 1;

        DummyFunc(a);
        DummyFunc(b);
        DummyFunc(c);
        DummyFunc(d);
    }
}

      

and got the output:

Stuff<string>: HelloWorld
Stuff<int>: 1
string: HelloWorld
int: 1

      

I have four overloaded functions referring to two base reference classes (one for int and one for string) and two reference regular types (one for int and one for string) and everything works fine ... this is what you are after ?

Edit: The problem is not related to calling overloaded methods, it is related to your foreach trying to convert all the elements in the list to the same type as the first one in order to reference the overloaded method. The first item that does not meet this exact definition will cause compilation to fail.

0


source


I was hoping to find an easier way to do this, but for now I'm going with this:

Replace the class with Foo<T>

these classes:

abstract class Foo<T> : FooBase
{
    // Class that deals with As and Bs in an identical fashion.
}

class Foo_A : Foo<A>
{
    public override void Method(Bar b)
    {
        b.OverloadedMethod(this);
    }
}

class Foo_B : Foo<B>
{
    public override void Method(Bar b)
    {
        // Doesn't compile here
        b.OverloadedMethod(this);
    }
}

      

And change the instance to

List<FooBase> ListOfFoos = new List<FooBase>();
ListOfFoos.Add(new OtherFoo());
ListOfFoos.Add(new Foo_A());
ListOfFoos.Add(new Foo_B());

      

This at least doesn't require code duplication in Foo<T>

and just requires me to forward the constructors.

0


source







All Articles