21 Stimmen

Was ist das Äquivalent von Java-Jokersymbolen in C#-Generik?

Ich entwickle eine Anwendung, in der ich eine Methode einer generischen Klasse aufrufen muss, ohne mich um den tatsächlichen Typ der Instanz zu kümmern. Etwas wie der folgende Java-Code:

public class Item<T>{
  private T item;

  public doSomething(){...}
}

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

Da ich es damals sehr eilig hatte, löste ich das Problem, indem ich eine Schnittstelle mit den aufzurufenden Methoden definierte und die generische Klasse diese implementieren ließ.

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 Umgehung funktioniert gut, aber ich würde gerne wissen, was der richtige Weg ist, um das gleiche Verhalten zu erreichen.

EDIT:

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

25voto

Jon Skeet Punkte 1325502

Normalerweise würde man die Methode generisch gestalten:

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

Unter der Annahme, dass die Anrufer den Typ kennt, sollte die Typinferenz bedeuten, dass er ihn nicht explizit angeben muss. Zum Beispiel:

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

Allerdings kann es auch nützlich sein, eine nicht-generische Schnittstelle zu schaffen, die die typunabhängigen Operationen spezifiziert. Es hängt sehr stark von Ihrem genauen Anwendungsfall ab.

3voto

Amy B Punkte 104656

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

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

Kunde:

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

1voto

Anton Gogolev Punkte 109749

Es gibt keine Möglichkeit, Typ-Parameter in der generischen .NET-Implementierung wegzulassen; das ist so vorgesehen. In der Tat kann dies nur in Java aufgrund der typbasierten Implementierung erreicht werden.

Sie können nur eine nicht-generische Basisschnittstelle verwenden (z. B. IEnumerable<T> y IEnumerable ).

0voto

jheriko Punkte 2968

Zu Jons Beitrag: Wenn Sie die Methode generisch machen (eine Vorlage), entfällt die Notwendigkeit für diese Art von Funktionalität (mit <?>). Sie können immer einen Typ in eine generische Klasse/Funktion eingeben, und für Fälle, in denen Sie nicht wissen, welchen Typ Sie benötigen, können Sie die betreffende Methode/Klasse ebenfalls generisch machen... letztendlich muss der Benutzer einen Typ angeben, wenn er eine solche Funktion aufruft oder eine generische Klasse verwendet, damit der Code kompiliert werden kann... andernfalls werden Sie einige Compilerfehler erhalten.

0voto

Matthias Hryniszak Punkte 3063

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

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

Glücklicherweise stellt sich heraus, dass eine generische ICollection auch eine nicht-generische ICollection ist, und wenn jemand die Elemente in ihr als reine Objekte behandeln muss, ist das immer noch möglich:

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

Da wir uns nicht um den tatsächlichen Typ der Elemente in der Sammlung kümmern, erhalten wir auf diese Weise lediglich Objekte. Ein Hinweis an dieser Stelle: Wenn die Sammlung primitive Typen enthält (z. B. int oder byte), dann tritt das Autoboxing in Kraft, was zu Leistungseinbußen führen kann.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X