Le altre risposte sono corrette ma c'è un punto sottile qui che ho pensato dovrebbe essere chiamato in modo specifico.
Normalmente in C#, il tipo di un intero letterale è int
, ma può essere convertito implicitamente in qualsiasi tipo numerico in cui la costante è nell'intervallo. Pertanto, anche senon è implicitamente convertibile in uint
, l'assegnazione myuint = 123;
è legale perché lo standard int
si adatta.
Da questo fatto è facile cadere nella convinzione errata che i valori letterali int
possano essere utilizzati ovunque sia previsto un valore uint
, ma hai scoperto perché questa convinzione è falsa.
L'algoritmo di inferenza del tipo è simile a questo. (Questa è una enorme semplificazione naturalmente; lambda rendono notevolmente più complessa.)
- Calcolare i tipi degli argomenti
- analizzare le relazioni tra argomenti e corrispondenti parametri formali
- Da questa analisi, tipo dedurre limiti su parametri di tipo generico
- Controllare i limiti per entrambi la completezza - ogni parametro di tipo generico deve avere limiti di limite e coerenza non devono essere contraddittori. Se l'inferenza è incompleta o incoerente, il metodo è inapplicabile.
- Se i tipi dedotti violano i loro vincoli, il metodo è inapplicabile.
- In caso contrario, il metodo con i tipi dedotti viene aggiunto all'insieme di metodi utilizzati per la risoluzione di sovraccarico.
La risoluzione di sovraccarico procede quindi a confrontare i metodi del candidato insieme l'uno contro l'altro per trovare il migliore.
(Si noti che il tipo di ritorno è, naturalmente, in nessun luogo considerato; C# verifica se il tipo di ritorno può essere assegnato a qualsiasi cosa viene assegnato a dopo risoluzione di sovraccarico ha scelto il metodo, non durante la risoluzione di sovraccarico.)
Nel tuo caso l'inferenza del tipo non riesce nel passaggio "verifica che ci sia un insieme coerente di limiti". T
è limitato a entrambi int
e uint
. Questa è una contraddizione, quindi il metodo non viene nemmeno aggiunto al set di metodi da considerare per la risoluzione del sovraccarico. Il fatto che gli argomenti siano convertibili in uint
non viene mai considerato; il motore di inferenza del tipo funziona esclusivamente sui tipi.
L'algoritmo di inferenza del tipo non "arretra" in alcun modo nel proprio scenario; non dice "OK, non posso inferire un tipo consistente per T
, ma forse uno dei singoli tipi funziona. Che cosa succede se ho provato entrambi i limiti int
e uint
? Possiamo vedere se uno di loro in realtà produce un metodo che funziona ". (Lo fa lo stesso quando sono coinvolti lambda, il che può indurlo a provare arbitrariamente molte combinazioni possibili di tipi in alcuni scenari.) Se l'algoritmo di inferenza funzionasse in questo modo otterresti il risultato che desideri, ma non.
In sostanza la filosofia è che l'algoritmo di inferenza non sta cercando di trovare un modo per far funzionare il programma, ma piuttosto di trovare una catena di ragionamento sui tipi che deriva una conclusione logica unico da informazioni provenienti da gli argomenti. C# cerca di fare ciò che l'utente intende fare, ma cerca anche di evitare di indovinare; in questo caso invece di indovinare potenzialmente, è necessario essere chiari sui tipi che si intende dedurre.
Come nota, proverei a limitare un po 'di più questo generico. Il modo in cui è scritto ogni tipo che implementa 'IComparable' otterrà la funzione 'Clamp', che potrebbe non essere semanticamente corretta. Questa è una di quelle situazioni in cui sfortunatamente non possiamo limitare 'T' a tipi specifici, come' dove T: isa (byte, sbyte, int, uint) 'ecc. –
@RonBeyer Secondo Eric Lippert, l'IComparable L'interfaccia deve fornire un ordine totale. Detto questo, perché "Clamp" non è semanticamente corretto, anche su, diciamo, archi? –
Rawling
@Rawling Ad esempio, 'stringa' implementa' IComparable ', che per me non sarebbe semanticamente corretto per un' Clamp' (char forse, ma non stringa). –