Tengo un problema con devolver la recopilación y la covarianza y me preguntaba si alguien tiene una mejor solución.¿Cómo tratar la covarianza al devolver la colección en C#?
El escenario es el siguiente:
Tengo la versión 2 de la aplicación y me gustaría mantener la aplicación versión completamente separado (a pesar de que podrían tener la misma lógica). En la implementación, me gustaría devolver una lista de elementos y, por lo tanto, en la interfaz, devolvería una lista de la interfaz del artículo. Sin embargo, en la implementación real de la interfaz, me gustaría devolver el objeto concreto del artículo. En el código, se ve algo como esto.
interface IItem
{
// some properties here
}
interface IResult
{
IList<IItem> Items { get; }
}
Entonces, habría 2 espacios de nombres que tienen una implementación concreta de estas interfaces. Por ejemplo,
Espacio de nombres Version1
class Item : IItem
class Result : IResult
{
public List<Item> Items
{
get { // get the list from somewhere }
}
IList<IItem> IResult.Items
{
get
{
// due to covariance, i have to convert it
return this.Items.ToList<IItem>();
}
}
}
Habrá otra aplicación del mismo espacio de nombres bajo versión 2.
Para crear estos objetos, habrá una fábrica que tomará la versión y creará el tipo de concreto apropiado según sea necesario.
Si la persona que llama conoce la versión exacta y hace lo siguiente, el código funciona bien
Version1.Result result = new Version1.Result();
result.Items.Add(//something);
Sin embargo, me gustaría que el usuario sea capaz de hacer algo como esto.
IResult result = // create from factory
result.Items.Add(//something);
Pero, debido a que se convierte en otra lista, el complemento no va a hacer nada porque el artículo no se volverá a añadir al objeto resultado original.
puedo pensar en algunas soluciones tales como:
- pude sincronizar las dos listas, pero que parece ser un trabajo extra para hacer
- retorno IEnumerable en lugar de IList y añadir un método para crear/Eliminación de colecciones
- Crear una colección personalizada que lleva el TConcrete y tInterfaz
entiendo por qué ocurre esto (debido al tipo de seguridad y todo), pero ninguno de workaroun d Creo que puede parece muy elegante. ¿Alguien tiene mejores soluciones o sugerencias?
¡Gracias de antemano!
actualización
Después de pensar en esto más, creo que puedo hacer lo siguiente:
public interface ICustomCollection<TInterface> : ICollection<TInterface>
{
}
public class CustomCollection<TConcrete, TInterface> : ICustomCollection<TInterface> where TConcrete : class, TInterface
{
public void Add(TConcrete item)
{
// do add
}
void ICustomCollection<TInterface>.Add(TInterface item)
{
// validate that item is TConcrete and add to the collection.
// otherwise throw exception indicating that the add is not allowed due to incompatible type
}
// rest of the implementation
}
entonces puedo tengo
interface IResult
{
ICustomCollection<IItem> Items { get; }
}
then for implementation, I will have
class Result : IResult
{
public CustomCollection<Item, IItem> Items { get; }
ICustomCollection<TItem> IResult.Items
{
get { return this.Items; }
}
}
de esa manera, si la persona que llama es accediendo a la clase Result, irá a través de CustomCollection.Add (elemento TConcrete) que ya es TConcrete.Si la persona que llama está accediendo a través de la interfaz IResult, irá a customCollection.Add (elemento TInterface) y la validación se realizará y se asegurará de que el tipo sea realmente TConcrete.
Lo intentaré y veré si esto funciona.
Creo que ir con la opción n. ° 2 requerirá la menor cantidad de código. También reducirá el área de superficie de su interfaz, ya que la implementación no será responsable del soporte completo de IList, que las personas que llaman probablemente no necesiten. –
Su solución se ve bien, pero ¿por qué usa 'ICustomCollection' y no 'ICollection ' directamente? –
svick
Totalmente podría.La única razón es que tengo algunos métodos específicamente para la recopilación que no está disponible para el ICollection normal al que se debe acceder mediante código interno, que olvidé mencionar. –
Khronos