2013-07-16 8 views
5

Non sono esattamente sicuro di come rendere questa domanda leggibile/comprensibile, ma ascoltami e spero che capirai il mio problema quando arriveremo alla fine (per lo meno è facilmente riproducibile).Perché l'inferenza del metodo non riesce a dedurre un parametro di tipo?

Provo a chiamare un metodo utilizzato per la convalida dei risultati in UnitTests. Essa ha la seguente firma:

void AssertPropertyValues<TEnumerable, TElement, TProperty>(
    TEnumerable enumerable, 
    Func<TElement, TProperty> propertyPointer, 
    params TProperty[] expectedValues) 
    where TEnumerable : System.Collections.Generic.IList<TElement> 

Ciò significa, che prende il seguente ingresso

  1. Qualsiasi oggetto enumerabile, e contiene oggetti dello stesso tipo come gli input per 2).
  2. Un Func (espressioni lambda di solito incapsulanti) che accetta un oggetto dello stesso tipo come "contenuto" di 1) e restituisce un oggetto dello stesso tipo del Tipo del contenuto dell'array fornito in 3).
  3. Un array di oggetti dello stesso tipo dell'output di Func in 2).

Quindi, una effettiva esecuzione di questo metodo potrebbe essere la seguente:

AssertPropertyValues(
    item.ItemGroups, 
    itemGroup => itemGroup.Name, 
    "Name1", "Name2", "Name3"); 

Almeno, è così che mi piacerebbe farlo sembrare come, ma mi imbatto in l'errore del compilatore ben nota: "Gli argomenti tipo per il metodo 'X' non possono essere dedotti dall'utilizzo.", Ed è quello che non capisco. Dovrebbe avere tutte le informazioni necessarie per quanto posso vedere, o forse è un'altra versione del problema "Covariance and Contravariance"?

Quindi per ora sono costretto a farlo in questo modo, invece:

AssertPropertyValues(
    item.ItemGroups, 
    (ItemGroup itemGroup) => itemGroup.Name, 
    "Name1", "Name2", "Name3"); 

Chiunque può segnalare il motivo per cui questo scenario non si può dedurre dal compilatore?

+1

hai provato a usare 'IEnumerable ' o sth allo stesso modo di 'TEnumerable'? in pratica il parametro 'propertyPointer' dovrebbe essere lo stesso del predicato, ad es. nel metodo di estensione' Enumerable.Select' (e quindi l'intero costrutto funziona allo stesso modo) ... quale tipo ha 'item.ItemGroups' ha (qualsiasi errore, che rende la firma esplicita è obbligatoria?)? altrimenti non ho il problema che stai affrontando ... –

+0

Ho corretto la formattazione dei tuoi esempi di codice in modo che siano ragionevoli e rinominati la tua domanda; questo non ha niente a che fare con il lambda. –

+0

@AndreasNiedermair Il mio problema deriva dal fatto che originariamente avevo questo vincolo in più posizioni, e in alcuni punti li usavo come tipi di ritorno, e quindi non potevo "accontentarli" con le sole interfacce. Non è più il caso quando provi la soluzione di EricLippert. –

risposta

24

Il problema è causato dal fatto che i vincoli non sono considerati parte della firma e non vengono mai utilizzati per dedurre durante l'inferenza di tipo. Ti aspetti che la deduzione vada:

  • TEnumerable viene determinato prendendo il tipo del primo argomento.
  • TElement è determinato prendendo le informazioni IList<T> attuazione da TElement
  • TProperty è determinato dal tipo di corpo del lambda

Ma C# non fa che secondo passo perché richiede considerando informazioni da un vincolo . Come si nota, se si forniscono tali informazioni nel lambda, il compilatore effettua la detrazione in base al tipo di parametro formale.

Fortunatamente il vincolo non è necessario. Riscrivere il tuo metodo per avere una firma più semplice che non ha un vincolo:

void AssertPropertyValues<TElement, TProperty>(
    IList<TElement> sequence, 
    Func<TElement, TProperty> projection, 
    params TProperty[] expectedValues) 

e ora si dovrebbe andare bene.

E mentre ci sei, probabilmente dovresti semplificarlo a IEnumerable<TElement> a meno che non sia necessario un IList<T> per qualche motivo.

+0

Ah, in realtà ho pensato che i vincoli fossero usati. È un peccato che non lo sia. Come ho sottolineato nel commento alla mia domanda, non ho potuto semplificare all'inizio, a causa delle dipendenze rispetto ad altri metodi che richiedevano la firma per i tipi di ritorno. Dopo un po 'di refactoring, la tua soluzione ha funzionato per me comunque. Grazie! :) (e sì, ho bisogno del IList ma grazie per averlo indicato comunque) –

Problemi correlati