Domanda

Provenendo da un background di C++, ho eseguito in un intoppo con l'overload in base a una specifica istanza di un tipo generico.Il seguente non funziona dal momento che solo una volta esempio di codice per la Foo<T> la classe è sempre generato, all'interno del Method, il tipo di this è semplicemente Foo<T>, non Foo<A> o Foo<B> come avevo sperato.In C++ sono abituato a modelli istanziato come tipi unici.

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

C'è un modo per ottenere qualcosa di simile a questo di lavorare in C#?Preferirei non dover duplicare il codice in Pippo come classi distinte per ogni tipo voglio utilizzare per.

Edit:Speriamo che questo sia un po ' più chiaro.

È stato utile?

Soluzione

Funziona con il caso statico. Trattare con le funzioni di istanza sarebbe un po 'più complicato. Questo di Jon Skeet potrebbe fornire un modo ragionevole per gestire i metodi di istanza.

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

Altri suggerimenti

Ora ho una veramente completa pezzo di codice in cui viene illustrato il problema.Nota per OP:si prega di provare a compilare il codice prima di pubblicarlo.C'erano un sacco di cose ho dovuto fare per arrivare a questo punto.È bene rendere il più semplice possibile per altre persone per aiutarvi.Ho anche rimosso un gruppo di estranei bit.OtherFoo non è davvero rilevante qui, né è 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);
    }
}

Sì, questo non compilare.Cosa ci si aspetta di fare, esattamente?Tenete a mente che la risoluzione dell'overload è eseguita a il tempo di compilazione, non tempo di esecuzione.Come fallen888 dice, si potrebbe cast e chiamare l'apposito metodo di overload - ma quale dei due overload si aspetta il compilatore di scegliere diversamente?Che cosa si vuole fare con Foo<string> invece di Foo<A> o Foo<B>?

Tutto questo va a dimostrare che .NET generici sono infatti significativamente diverso dal C++ modelli, ovviamente...

Non l'ho provato ma sembra che dovresti essere in grado di ottenere quello che vuoi realizzando un amplificatore &; B visitabile (ad es. Con il modello di visitatore aciclico).

Modifica: non sono sicuro che puoi completarlo mentre stai provando. Ho provato tutti i tipi di trucchi per tentare di farlo funzionare e non riesco a farlo compilare. Il meglio che posso fare è estrarre la chiamata del metodo fuori dalla mia classe Generic. Se la tua chiamata al metodo è esterna, puoi definire in modo specifico quale T è nel generico. Tuttavia, all'interno del metodo, al momento della compilazione, il compilatore non sa quale sarà T, quindi non sa quale metodo sovraccarico chiamare. L'unico modo per vedere ciò è utilizzare un interruttore per determinare il tipo di T e specificare manualmente il sovraccarico da chiamare.

Il meglio che posso fare è questo, che non è proprio quello che stai cercando, ma potrebbe essere usato per un effetto simile:

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

e ottenuto l'output:

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

Ho quattro funzioni sovraccaricate che fanno riferimento a due classi generiche di riferimento (una per int e una per stringa) e due tipi regolari di riferimento (una per int e una per stringa) e tutto funziona bene ... è questo quello che dopo?

Modifica: il problema non sembra essere con la chiamata dei metodi sovraccarichi, ha a che fare con il tuo foreach che sta cercando di convertire tutti gli elementi dell'elenco nello stesso tipo del primo per fare riferimento al metodo sovraccarico. Il primo elemento che non è conforme a quella definizione esatta farà fallire la tua compilazione.

Speravo di trovare un modo più semplice per farlo, ma per ora vado con questo:

Sostituisci Foo<T> classe con queste classi:

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

E cambia le istanze in

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

Questo almeno non richiede la pubblicazione del codice in <=> e richiede solo che io inoltri i costruttori.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top