Data questa interfaccia "IHandle" e due classi per essere gestite:Odd C# comportamento in sede di attuazione generica interfaccia
interface IHandle<T>
{
void Handle(T m);
}
class M1
{
public int Id;
}
class MReset
{
}
voglio creare una base generica che si occupa di "reset", così come la gestione delle istanze M1 :
class HandlerBase<T> :
IHandle<MReset>,
IHandle<T> where T : M1
{
protected int Count;
void IHandle<T>.Handle(T m)
{
++Count;
Console.WriteLine("{0}: Count = {0}", m.Id, Count);
}
void IHandle<MReset>.Handle(MReset m)
{
Count = 0;
}
}
Questo non viene compilata in quanto il compilatore ritiene T possono essere degli "MReset" quindi uscite:
errore CS0 695: 'HandlerBase' non può implementare sia 'IHandle' e 'IHandle' perché possono unire per un certo parametro di tipo sostituzioni
Che di per sé è un po 'strano dato che non riesco a capire come T potrebbe forse essere di tipo MReset poiché deve essere di tipo M1. Ma va bene, posso accettare che il compilatore è più intelligente di me :-)
Edit: Il compilatore non è più intelligente di me :-) Secondo un commento su Why does this result in CS0695? abbiamo "le dichiarazioni di vincolo non sono considerati in sede di determinazione tutti i tipi possibili costruiti ".
Ora scambiare la dichiarazioni di interfaccia:
class HandlerBase<T> :
IHandle<T> where T : M1,
IHandle<MReset>
{
... same as before ..
}
E improvvisamente ricevo un messaggio di errore diverso che indica che non posso implementare IHandle.Handle (MReset m) dal momento che la dichiarazione di classe non dice che sta attuando che interfaccia:
errore CS0540: 'HandlerBase.IHandle < ...> Maniglia (MReset)': contiene tipo non implementa l'interfaccia 'IHandle'
Domanda: perché l'ordine delle dichiarazioni fa la differenza? Cosa non va nel secondo esempio?
Alla fine si scopre che c'è una soluzione:
class HandlerBase :
IHandle<MReset>
{
protected int Count;
void IHandle<MReset>.Handle(MReset m)
{
Count = 0;
}
}
class Handler<T> : HandlerBase,
IHandle<T> where T : M1
{
void IHandle<T>.Handle(T m)
{
++Count;
Console.WriteLine("{0}: Count = {0}", m.Id, Count);
}
}
Ma la soluzione funziona solo se HandlerBase
implementa IHandle<MReset>
- non se l'interfaccia generica IHandle<T>
è implementato in HandlerBase
prima. Perché?
Edit: Implementazione IHandle<T>
in HandlerBase
fa lavoro (e se avessi mostrato il codice che qualcuno potrebbe aver visto). Questo funziona:
class HandlerBase<T> :
IHandle<T> where T : M1
{
protected int Count;
void IHandle<T>.Handle(T m)
{
++Count;
Console.WriteLine("Type = {0}, Id = {1}, Count = {2}", GetType(), m.Id, Count);
}
}
class Handler<T> : HandlerBase<T>,
IHandle<MReset>
where T : M1
{
void IHandle<MReset>.Handle(MReset m)
{
Count = 0;
Console.WriteLine("RESET");
}
}
Purtroppo la mia seconda dichiarazione di classe è stato questo:
class Handler<T> : HandlerBase<T> where T : M1,
IHandle<MReset>
{
void IHandle<MReset>.Handle(MReset m)
{
Count = 0;
Console.WriteLine("RESET");
}
}
Avviso la sottile differenza nella posizione di where T : M1
:-) L'ultimo esempio dichiara che T devono attuare IHandle<MReset>
(in aggiunta a M1
). Duh!
[correlati/duplicati] (http://stackoverflow.com/questions/15316898/why-does-this-result-in-cs0695). Non sto chiudendo come duplicato solo a causa di questa domanda "perché l'ordine delle dichiarazioni fa la differenza?" a cui non viene data risposta. –
Questo è davvero il link pertinente. Ho appena modificato la risposta accettata per rendere più visibile l'importante informazione dalla specifica C#: ** "Le dichiarazioni di vincoli non vengono prese in considerazione quando si determinano tutti i possibili tipi costruttivi." ** –