2009-02-09 21 views
20

Ich entwickle eine Anwendung, wo ich eine Methode einer generischen Klasse aufrufen muss und mir den tatsächlichen Typ der Instanzen nicht interessiert. So etwas wie der folgenden Java-Code:Was Java-Platzhalter in C# Generika entspricht

public class Item<T>{ 
    private T item; 

    public doSomething(){...} 
} 

... 
public void processItems(Item<?>[] items){ 
for(Item<?> item : items) 
    item.doSomething(); 
} 

Damals habe ich auf Eile war, so löste ich mein Problem, indem eine Schnittstelle mit den Methoden definieren, die ich brauchte zu berufen und machte die generische Klasse es umzusetzen.

public interface IItem 
{ 
    void doSomething(); 
} 

public class Item<T> : IItem { 
    private T item; 

    public void doSomething(){...} 
} 

... 
public void processItems(IItem[] items) 
{ 
foreach(IItem item in items) 
    item.doSomething(); 
} 

Diese Problemumgehung funktioniert gut, aber ich würde gerne wissen, was die richtige Art und Weise ist das gleiche Verhalten zu erreichen.

EDIT:

Ich habe vergessen zu verweisen, dass der Anrufer von processItems nicht die tatsächlichen Typen nicht kennt. Eigentlich war die Idee, dass das als Argument übergebene Array processItems gemischte Typen enthalten könnte. Da es nicht möglich ist, ein solches Array in .Net zu haben, scheint die Verwendung einer nicht-generischen Basisklasse oder -schnittstelle der einzige Weg zu sein.

Antwort

25

Der normale Weg, dies zu tun zu machen wäre die Methode generisch: es

public void ProcessItems<T>(Item<T>[] items) { 
    foreach(Item<T> item in items) 
    item.DoSomething(); 
} 

Angenommen, die Anrufer kennen sollte die Art, Typinferenz bedeutet, dass sie nicht explizit angeben. Zum Beispiel:

Item<int> items = new Item<int>(); // And then populate... 
processor.ProcessItems(items); 

auch sagen, dass eine nicht-generische Schnittstelle zu schaffen, der die Typ Agnostiker Operationen angibt, kann auch nützlich sein. Es hängt sehr stark von Ihrem genauen Anwendungsfall ab.

+0

Blargh, ein paar Sekunden zu früh. : P –

+0

Ja, schlag mich auch. Können Sie jedoch mit einem Beispiel für den Anrufer bearbeiten? –

+0

nur 'ProcessItems (data);' –

1

Es gibt keine Möglichkeit, Typ Parameter in .NET generische Implementierung weglassen; das ist Absicht. Tatsächlich kann dies nur in Java aufgrund seiner typenlöschungsbasierten Implementierung erreicht werden.

Sie können nur eine nicht generische Basisschnittstelle verwenden (denken Sie an IEnumerable<T> und IEnumerable).

0

Weiter zu Jons Post. Wenn Sie die Methode generisch machen (eine Vorlage), wird die Anforderung für diese Art von Funktionalität (unter Verwendung von <? >) aufgehoben. Sie können einen Typ immer in eine generische Klasse/Funktion einspeisen und für Fälle, in denen Sie nicht wissen, welchen Typ Sie benötigen, können Sie auch die anstößige Methode/Klasse generisch machen ... schließlich muss der Benutzer beim Aufruf einen Typ angeben eine solche Funktion oder die Verwendung einer generischen Klasse, damit der Code kompiliert werden kann ... andernfalls werden Sie einige Compilerfehler bekommen.

3

Ich sehe, dass Sie nur eine Methode ohne Parameter aufrufen möchten ... es gibt bereits einen Vertrag dafür: Action.

public void processItems(IEnumerable<Action> actions) 
{ 
    foreach(Action t in actions) 
    t(); 
} 

Auftraggeber:

List<Animal> zoo = GetZoo(); 
List<Action> thingsToDo = new List<Action>(); 
// 
thingsToDo.AddRange(zoo 
    .OfType<Elephant>() 
    .Select<Elephant, Action>(e => e.Trumpet)); 
thingsToDo.AddRange(zoo 
    .OfType<Lion>() 
    .Select<Lion, Action>(l => l.Roar)); 
thingsToDo.AddRange(zoo 
    .OfType<Monkey>() 
    .Select<Monkey, Action>(m => m.ThrowPoo)); 
// 
processItems(thingsToDo); 
0

ich mit dem gleichen Problem zu kämpfen haben, wenn es um die Portierung von Java Sachen kam, wo ich Konstrukte wie

if (o instanceof Collection<?>) doSoemthing((Collection<?>)o); 

es zum Glück hatte dreht aus, dass eine generische ICollection auch eine nicht-generische ICollection ist und wenn jemand die Elemente darin als reine Objekte behandeln muss, ist es immer noch möglich:

if (o is ICollection) DoSomething((ICollection)o); 

Auf diese Weise, da wir uns nicht um die tatsächliche Art von Elementen in der Sammlung kümmern, bekommen wir hier nur Objekte. Ein Hinweis hier: Wenn die Sammlung primitive Typen (int oder Byte zum Beispiel) hielt, dann tritt ein Autoboxing ein, das zu einer Leistungseinbuße führen kann.