Wie kann ich überlastete eine C # Methode durch spezifische Instanzen eines generischen Typs

StackOverflow https://stackoverflow.com/questions/405429

  •  03-07-2019
  •  | 
  •  

Frage

mit Überlastung basierend auf einer bestimmten Instanz eines generischen Typs

Ich stamme aus einer C ++ Hintergrund, ich habe in einem Baumstumpf laufen. Die folgende funktioniert nicht, da nur einmal Instanz der Code für die Foo<T> Klasse jemals erzeugt wird, so im Inneren des Method, die Art der this einfach Foo<T> ist, nicht Foo<A> oder Foo<B> wie ich gehofft hatte. In C ++ Ich bin zu Vorlagen verwendet als eindeutige Typen instanziiert wird.

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
    }
}

Gibt es eine Möglichkeit, so etwas zu bekommen in C # zu arbeiten? Ich würde eher nicht für jede Art den Code in Foo als separate Klassen duplizieren möchte ich für ihn verwenden.

Edit: Hoffentlich ist das ein wenig klarer.

War es hilfreich?

Lösung

Dies funktioniert für den statischen Fall. Der Umgang mit Instanz-Funktionen wäre ein bisschen komplizierter. Dieser Beitrag von Jon Skeet könnte eine vernünftige Art und Weise bietet mit Instanz-Methoden zu behandeln.

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;
}

Andere Tipps

Ich habe jetzt ein wirklich komplettes Stück Code, der das Problem demonstriert. Hinweis an die OP: Sie versuchen, Ihren Code kompilieren, bevor es zu veröffentlichen. Es gab ein paar Dinge, die ich zu tun hatte, so weit zu kommen. Es ist gut, es so einfach wie möglich zu machen für andere Menschen zu helfen. Ich habe auch eine Reihe von Fremd Bits entfernt. OtherFoo ist hier nicht wirklich relevant, noch ist 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);
    }
}

Ja, das nicht kompiliert. Was hast du erwartet es, genau das zu tun? Beachten Sie, dass die Überladungsauflösung wird durchgeführt in kompilieren Zeit , nicht die Ausführungszeit. Wie fallen888 sagt, könnte man die entsprechende überladene Methode werfen und rufen - aber welche der beiden Überlastungen würden erwarten, dass Sie der Compiler sonst holen? Was möchten Sie tun es mit Foo<string> statt Foo<A> oder Foo<B>?

zu tun

Das geht alles, was das .NET-Generika in der Tat signifikant unterschiedlich ++ von C-Vorlagen sind zu demonstrieren, natürlich ...

Ich habe es nicht ausprobiert, aber es scheint, sollten Sie in der Lage zu erreichen, was Sie, indem Sie eine wollen & B besuchbar (zum Beispiel mit dem azyklische Besuchermuster).

Edit: Ich bin nicht sicher, dass Sie diese abschließen können, wie Sie versuchen. Ich habe alle möglichen Tricks versucht, um zu versuchen, diese nicht zur Arbeit zu kommen und bekommen es zu kompilieren. Das Beste, was ich tun kann, ist den Methodenaufruf außerhalb meiner Allgemein Klasse zu ziehen. Wenn Ihr Methodenaufruf draußen ist, dann können Sie gezielt festlegen, welche T in der generisch ist. Doch innerhalb der Methode, bei der Kompilierung, der Compiler nicht weiß, was T wird, damit es nicht wissen, welche Methode überlastet zu nennen. Der einzige Weg, ich um diesen zu sehen ist, einen Schalter zu verwenden, um die Art von T zu bestimmen und manuell die Überlastung angeben zu rufen.

Das Beste, was ich tun kann, ist dies, was nicht ganz ist, was Sie nach, aber es könnte zu einem ähnlichen Effekt verwendet werden:

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);
    }
}

und bekam Ausgabe:

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

ich vier überladene Funktionen haben Referenzierung zwei Referenzierung generische Klassen (eine für int und eine für String) und zwei Referenzierung reguläre Typen (eine für int und eine für string) und es funktioniert alles in Ordnung ... das ist, was Sie ‚wieder nach?

Edit: Das Problem scheint nicht mit der Berufung der überladenen Methoden zu sein, muss es mit Ihrem foreach zu tun, die alle Elemente in der Liste auf den gleichen Typ wie der erste, um zu konvertieren versucht, das referenzieren überladene Methode. Der erste Punkt, der nicht auf diese genaue Definition entspricht, wird dazu führen, dass die Kompilierung zum Scheitern verurteilt.

Ich hatte gehofft, einen einfacheren Weg zu finden, dies zu tun, aber jetzt werde ich mit diesem:

Ersetzen Foo<T> Klasse mit diesen Klassen:

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);
    }
}

und ändern Sie die instantiations zu

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

Das zumindest ist es nicht erforderlich, den Code in Foo<T> Dublieren, und verlangt, dass ich nur die Konstrukteure zu übermitteln.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top