Domanda

Sto cercando di capire il concetto di .NET Generics e di usarli nel mio codice, ma continuo a riscontrare un problema.

Qualcuno può provare a spiegarmi perché la seguente configurazione non viene compilata?

public class ClassA
{
    ClassB b = new ClassB();

    public void MethodA<T>(IRepo<T> repo) where T : ITypeEntity
    {
        b.MethodB(repo);
    }
}

public class ClassB
{
    IRepo<ITypeEntity> repo;

    public void MethodB(IRepo<ITypeEntity> repo)
    {
        this.repo = repo;
    }
}

Ricevo il seguente errore:
impossibile convertire da IRepo < 'T > a IRepo < 'ITypeEntity >

Il metodo A viene chiamato con un IRepo < 'DetailType > parametro oggetto in cui DetailType eredita da ITypeEntity.

Continuo a pensare che questo dovrebbe essere compilato poiché sto vincolando T all'interno di MethodA per essere di tipo ITypeEntity.

Qualsiasi pensiero o feedback sarebbe estremamente utile.

Grazie.

Modifica: Nick R ha un grande suggerimento, ma sfortunatamente nel mio contesto, non ho la possibilità di rendere ClassA generico. ClassB potrebbe essere però.

È stato utile?

Soluzione

L'ereditarietà non funziona allo stesso modo quando si usano i generici. Come sottolinea Smashery, anche se TypeA eredita da TypeB, myType & Lt; TypeA & Gt; non eredita da myType < TypeB > ;.

Pertanto, non è possibile effettuare una chiamata a un metodo definito come MethodA (myType < TypeB > b) in attesa di un myType < TypeB > e dagli un myType < TypeA > anziché. I tipi in questione devono corrispondere esattamente. Pertanto, non verrà compilato quanto segue:

myType<TypeA> a; // This should be a myType<TypeB>, even if it contains only TypeA's

public void MethodB(myType<TypeB> b){ /* do stuff */ }

public void Main()
{
  MethodB(a);
}

Quindi, nel tuo caso, dovresti passare un IRepo < ITypeEntity > a MethodB, anche se contiene solo DetailTypes. Dovresti fare qualche conversione tra i due. Se si utilizzava un IList generico, è possibile effettuare le seguenti operazioni:

public void MethodA<T>(IList<T> list) where T : ITypeEntity
{
  IList<T> myIList = new List<T>();

  foreach(T item in list)
  {
    myIList.Add(item);
  }

  b.MethodB(myIList);
}

Spero che sia utile.

Altri suggerimenti

Bene, questo va bene. Ho sostanzialmente ridefinito le classi per prendere parametri generici. Questo potrebbe essere ok nel tuo contesto.

public interface IRepo<TRepo>
{
}

public interface ITypeEntity
{
}


public class ClassA<T> where T : ITypeEntity
{
    ClassB<T> b = new ClassB<T>();
    public void MethodA(IRepo<T> repo)
    {
        b.MethodB(repo);
    }
}
public class ClassB<T> where T : ITypeEntity
{
    IRepo<T> repo;
    public void MethodB(IRepo<T> repo)
    {
        this.repo = repo;
    }
}

Il problema è difficile da risolvere. DetailType può ereditare da ITypeEntity, ma in realtà non è ITypeEntity. L'implementazione di DetailType potrebbe introdurre diverse funzionalità, quindi DetailType implementa ITypeEntity ma non è uguale a ITypeEntity. Spero abbia senso ...

Vedi la domanda di @monoxide

E come ho detto lì , controllando la serie di post di Eric Lippert sulla contraddizione e la covarianza per i generici renderà molto più chiaro questo.

  

Ottengo il seguente errore: impossibile   converti da IRepo < 'T > a   & IRepo lt; & 'ITypeEntity gt;

Questo errore di compilazione viene visualizzato perché IRepo<T> e IRepo<ITypeEntity> non sono non la stessa cosa. La seconda è una specializzazione della prima. ITypeEntity è una definizione di tipo generico , in cui il parametro di tipo T è un segnaposto e where è un tipo generico costruito della definizione di tipo generico, in cui il tipo il parametro T da è specificato come MethodA.

  

Continuo a pensare che questo dovrebbe   compilare come sto vincolando T all'interno   Il metodo A deve essere di tipo ITypeEntity.

Il vincolo Dictionary<<K, V> non aiuta qui perché contrappone solo il tipo che puoi fornire T nei siti di chiamata per Dictionary<K, V>.

Ecco la terminologia della documentazione MSDN (vedere Generici in .NET Framework ) che può aiutare:

  • Una definizione di tipo generico è a classe, struttura o interfaccia dichiarazione che funziona come a modello, con segnaposto per il tipi che può contenere o utilizzare. Ad esempio, la classe IComparer<T> può contenere due tipi: chiavi e valori. Perché è solo un modello, non puoi creare istanze di una classe, struttura o interfaccia che è a definizione di tipo generico.

  • Parametri o tipo di tipo generico parametri, sono i segnaposto in un tipo generico o una definizione di metodo. Il <=> tipo generico ha due tipi parametri, K e V, quello rappresentano i tipi delle sue chiavi e valori.

  • Un tipo generico costruito o tipo costruito, è il risultato di specificando i tipi per il generico parametri di tipo di tipo generico definizione.

  • Un argomento di tipo generico è di qualsiasi tipo che viene sostituito da un generico parametro di tipo.

  • Il termine generico tipo generico include entrambi i tipi costruiti e definizioni di tipo generico.

  • I vincoli sono limiti posti parametri di tipo generico. Per ad esempio, potresti limitare un tipo parametro per i tipi che implementano il <=> generico interfaccia, per garantire che istanze del tipo può essere ordinato. Puoi vincola anche i parametri di tipo a tipi che hanno una base particolare classe, che ha un valore predefinito costruttore o che sono di riferimento tipi o tipi di valore. Utenti del il tipo generico non può sostituire il tipo argomenti che non soddisfano il vincoli.

T è una variabile di tipo che verrà associata a un tipo partcular in uso. La restrizione garantisce che quel tipo rappresenterà un sottoinsieme dei tipi che implementano ITypeEntity, esclusi altri tipi che implementano l'interfaccia.

al momento della compilazione anche se lo stai vincolando, il compilatore sa solo che T in MethodA è un tipo di riferimento. non sa a quale tipo è vincolato.

Questo è un uso ridondante dei generici, se T può essere solo un'istanza di ITypeEntity non dovresti usare i generici.

I generici sono per quando hai più tipi che possono essere dentro qualcosa.

Se B è una sottoclasse di A, ciò non significa che Class<B> sia una sottoclasse di Class<A>. Quindi, per lo stesso motivo, se dici & Quot; T è un ITypeEntity & Quot ;, ciò non significa che & Quot; IRepo<T> è un IRepo<ITypeEntity> " ;. Potrebbe essere necessario scrivere il tuo metodo di conversione se vuoi che funzioni.

Nel contesto di avvolgere la testa attorno a metodi generici, permettimi di darti una semplice funzione generica. È un equivalente generico di IIf () (immediato se) di VB, che è di per sé una cattiva imitazione dell'operatore ternario di tipo C (?). Non è utile per nulla poiché il vero operatore ternario è migliore, ma forse ti aiuterà a capire come sono costruite le funzioni generiche e in quali contesti dovrebbero essere applicate.

T IIF<T>(bool Expression, T TruePart, T FalsePart)
{
    return Expression ? TruePart : FalsePart;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top