2011-08-14 20 views
8

he decidido recientemente para refrescar mi memoria en relación con conceptos básicos de C#, por lo que este podría ser trivial, pero he tropezaron con el problema siguiente:interfaces de C# Covarianza y contravarianza al implementar

StringCollection se utilizó en v1 .NET 0.0 con el fin de crear una colección con establecimiento inflexible para cuerdas en lugar de un object basado ArrayList (esto más tarde se mejoró mediante la inclusión de colecciones genéricas):

Tomando una mirada rápida a StringCollection definición, se puede ver lo siguiente:

// Summary: 
//  Represents a collection of strings. 
[Serializable] 
public class StringCollection : IList, ICollection, IEnumerable 
{ 
... 
    public int Add(string value); 
... 
} 

Se puede ver que implementa IList, que contiene la siguiente declaración (entre algunas otras declaraciones):

int Add(object value); 

Pero no:

int Add(string value); 

Mi primera suposición fue que es posible debido a las reglas de covarianza de .NET framework.

lo tanto, sólo para asegurarse, he intentado escribir mi propia clase que implementa IList y cambió

int Add(object value); 

para recuperar un tipo de cadena en lugar de un tipo de objeto, pero para mi sorpresa, cuando se trata de compilar el proyecto , Tengo un error en tiempo de compilación:

does not implement interface member 'System.Collections.IList.Add(object)' 

¿Alguna idea de por qué?

Gracias!

Respuesta

7

El comportamiento se debe a la implementación explícita de IList.Add(object) en lugar de co/contravariancia. Según la documentación de MSDN, StringCollection implementa explícitamente IList.Add(object); el método Add(string) no está relacionado. La aplicación puede parecerse a algo como esto:

class StringCollection : IList 
{ 
    ... 
    public int Add(string value) 
    {} // implementation 

    public int IList.Add (object value) 
    { 
     if (!value is string)) return -1; 
     return Add(value as string) 
    } 
} 

se puede observar esta distinción:

StringCollection collection = new StringCollection(); 
    collection.Add(1); // compile error 
    (collection as IList).Add(1); // compiles, runtime error 
    (collection as IList).Add((object)"") // calls interface method, which adds string to collection 

Adición

Lo anterior no se refiere a por qué este patrón se implementa. La especificación del lenguaje C# indica que [§13.4.1, énfasis añadido]:

In some cases, the name of an interface member may not be appropriate for the implementing class, in which case the interface member may be implemented using explicit interface member implementation. [...]

It is not possible to access an explicit interface member implementation through its fully qualified name in a method invocation, property access, or indexer access. An explicit interface member implementation can only be accessed through an interface instance, and is in that case referenced simply by its member name.

StringCollection adhiere al comportamiento requerido IList - IList no hace ninguna garantía de que cualquier objeto arbitrario puede ser añadido a la misma. StringCollection ofrece garantías más sólidas, principalmente, que contendrá solo cadenas. La clase incluye sus propios métodos fuertemente tipados para Add, Contains, Item y otros para el caso de uso estándar donde se accede como StringCollection en lugar de IList. Pero todavía funciona perfectamente como IList, aceptando y devolviendo objetos, pero devolviendo un código de error (como lo permite IList) si se intenta agregar un elemento que no sea una cadena.

En última instancia, si una interfaz aparece en la clase (es decir, se implementa explícitamente) queda a criterio del autor de la clase. En el caso de las clases de infraestructura, las implementaciones explícitas se incluyen en la documentación de MSDN, pero no son accesibles como miembros de clase (por ejemplo, se muestran en contextos de autocompletado).

+0

Entonces, ¿debo recordar que, como regla general, la implementación explícita de una interfaz NO aparece en las definiciones de clases incorporadas en las librerías .NET? (porque obviamente no puedo ver esta implementación entre todos los métodos en la clase StringCollection) ¿Por qué? –

+0

Por lo general, en efecto, similar al sombreado de miembros: enmendé la respuesta para abordar esto. – drf

0

Si está utilizando .NET 2.0+, sólo usaría genéricos:

IList<string> list = new List<string>(); 

que debe darle todo lo que quiere.

0

IList.Add(object) puede aceptar parámetros que no sean cadenas - puede aceptar cualquier tipo. Entonces, si declara que su implementación de la interfaz solo acepta cadenas, ya no coincide con la especificación de la interfaz, porque ahora no podría pasar un Stream por ejemplo.

Varianza puede trabajar la otra manera: si el método de interfaz se declaró a aceptar cadenas, a continuación, los objetos que aceptan estaría bien, también desde cadenas son objetos, y por lo tanto cualquier entrada con el método de la interfaz también sería de entrada aceptable para tu implementación (Sin embargo, aún tendría que proporcionar una implementación de interfaz explícita con un método que acepte cadena, porque en C# una implementación de método de interfaz coincide exactamente con la declaración del método de interfaz.)

0

Lo que IList básicamente especifica es que puede llamar al Add y pasar cualquier objeto como parámetro, desde un Int en caja a otro IList a un System.DivideByZeroException. Si solo proporciona un método Add(string), no ha cumplido este requisito, ya que solo puede agregar cadenas.

En otras palabras, no podrá llamar al StringCollection.Add(new Object());, que debería ser perfectamente factible si la interfaz se implementó correctamente.: D

Cuestiones relacionadas