Un'implementazione non interrotta della varianza di implementazione dell'interfaccia dovrebbe essere covariante nel tipo di ritorno e controvariante nei tipi di argomento.
Ad esempio:
public interface IFoo
{
object Flurp(Array array);
}
public class GoodFoo : IFoo
{
public int Flurp(Array array) { ... }
}
public class NiceFoo : IFoo
{
public object Flurp(IEnumerable enumerable) { ... }
}
Entrambi sono legali sotto le "nuove" regole, giusto? Ma che dire di questo:
public class QuestionableFoo : IFoo
{
public double Flurp(Array array) { ... }
public object Flurp(IEnumerable enumerable) { ... }
}
Tipo di difficile dire quale implementazione implicita è migliore qui. Il primo è una corrispondenza esatta per il tipo di argomento, ma non il tipo di ritorno. Il secondo è una corrispondenza esatta per il tipo di ritorno, ma non il tipo di argomento. Mi sto orientando verso il primo, perché chiunque utilizzi l'interfaccia IFoo
può dargli sempre un Array
, ma non è ancora del tutto chiaro.
E questo non è il peggiore, di gran lunga. Cosa succede se invece lo facciamo:
public class EvilFoo : IFoo
{
public object Flurp(ICollection collection) { ... }
public object Flurp(ICloneable cloneable) { ... }
}
Quale si vince il premio? È un sovraccarico perfettamente valido, ma ICollection
e ICloneable
non hanno nulla a che fare l'uno con l'altro e Array
implementa entrambi. Non riesco a vedere una soluzione ovvia qui.
Si ottiene solo peggio se iniziamo aggiungendo sovraccarichi per l'interfaccia stessa:
public interface ISuck
{
Stream Munge(ArrayList list);
Stream Munge(Hashtable ht);
string Munge(NameValueCollection nvc);
object Munge(IEnumerable enumerable);
}
public class HateHateHate : ISuck
{
public FileStream Munge(ICollection collection);
public NetworkStream Munge(IEnumerable enumerable);
public MemoryStream Munge(Hashtable ht);
public Stream Munge(ICloneable cloneable);
public object Munge(object o);
public Stream Munge(IDictionary dic);
}
Buona fortuna cercando di svelare questo mistero senza impazzire.
Ovviamente, tutto ciò è discutibile se si asserisce che le implementazioni dell'interfaccia dovrebbero supportare solo varianza di tipo restituito e non varianza di tipo argomento. Ma quasi tutti considererebbero una tale mezza implementazione completamente infranta e iniziare a inviare spam a segnalazioni di bug, quindi non credo che il team C# lo farà.
Non so se questo è il motivo ufficiale per cui non è supportato in C# oggi, ma dovrebbe servire come buon esempio del tipo di codice "solo scrittura" che potrebbe portare a, e parte di la filosofia progettuale del team C# è quella di cercare di impedire agli sviluppatori di scrivere codice terribile.
Se l'interfaccia 'IClonable' è stata scritta prima del codice, in che modo il metodo Clone può restituire tipi specifici? L'unica garanzia che abbiamo è che qualsiasi cosa venga restituita dalla tua implementazione di Clone, sarà derivata da "Oggetto". Dato che devi aderire alla definizione esatta dell'interfaccia, dovrai restituire ciò che l'interfaccia dice, essendo "Oggetto". –
Qui voglio che la mia implementazione del metodo Clone dovrebbe essere in grado di restituire un tipo specifico non il metodo Clone() di IClonable quando sto installando l'interfaccia IClonable. Ma questo non è permesso, otteniamo un errore di compilazione. Quindi voglio sapere il motivo, perché il concetto di Covariance o Contravariance non è consentito per l'implementazione di un'interfaccia. – Amit