Domanda

Sto sviluppando un'applicazione in cui ho la necessità di invocare un metodo di una classe generica e non mi preoccupano il tipo effettivo istanze. Qualcosa come il seguente codice Java:

public class Item<T>{
  private T item;

  public doSomething(){...}
}

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

Al momento ero in fretta, quindi ho risolto il mio problema definendo un'interfaccia con i metodi di cui avevo bisogno per invocare e fatto la classe generica attuarlo.

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

Questa soluzione funziona bene, ma mi piacerebbe sapere qual è il modo corretto per ottenere lo stesso comportamento.

Modifica

Ho dimenticato di riferimento che il chiamante di processItems non conosce i tipi effettivi. In realtà l'idea era che la matrice passata come argomento processItems potrebbero contenere tipi mescolati. Dalla sua non è possibile avere una tale varietà di Net, utilizzando una classe base generico non o interfaccia sembra essere l'unico modo.

È stato utile?

Soluzione

Il modo normale per farlo sarebbe quello di rendere il metodo generico:

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

Supponendo che il chiamante conosce il tipo, inferenza di tipo dovrebbe significare che non c'è bisogno di specificare in modo esplicito. Ad esempio:

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

Detto questo, creando un'interfaccia non generica che specifica le operazioni di tipo agnostico può essere utile pure. Sarà molto dipenderà dal vostro caso esatto utilizzo.

Altri suggerimenti

I see that you only want to invoke some method with no parameters... there's already a contract for that: Action.

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

Client:

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

There's no way you can omit Type Parameters in .NET generic implementation; this is by design. In fact, this can only be achieved in Java because of its type-erasure-based implementation.

You can only use a base non-generic interface (think IEnumerable<T> and IEnumerable).

Further to Jon's post. making the method generic (a template) negates the requirement for this sort of functionality (using <?>). You can always feed a type into a generic class/function and for cases where you don't know what type you will need you can make the offending method/class generic as well... ultimately the user has to provide a type when calling such a function or using a generic class, for the code to be able to compile... otherwise you will get some compiler errors.

I've been struggling with the same issue when it came to porting stuff from Java where I've had constructs like

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

Fortunately it turns out that a generic ICollection is also a non-generic ICollection and if someone needs to treat the elements in it as pure objects it is still possible:

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

That way, since we don't care about the actual type of elements in collection all we get here are objects. A note here: if the collection was holding primitive types (int or byte for example) then autoboxing kicks in which might introduce performance penalty.

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