creo que voy a explicar mis problemas con algunos ejemplos ..Realmente no entiendo esta co/contravariancia ... ¿No puedo tener los métodos genéricos de obtención y configuración?
interface IModel {}
class MyModel : IModel {}
interface IRepo<T> where T: IModel {
}
class Repo : IRepo<MyModel> {
}
// Cannot implicitly convert.. An explicit convertion exists. Missing cast?
IRepo<IModel> repo = new Repo();
por eso es necesario covarianza ..
interface IRepo<out T> where T: IModel {
}
Niza, funciona. Entonces quiero usarlo:
interface IRepo<out T> where T: IModel {
T ReturnSomething();
}
class Repo : IRepo<MyModel> {
public MyModel ReturnSomething() { return default(MyModel); }
}
Todo bien, pero el repositorio tiene que insertar objetos también. Con un parámetro de salida, no podemos hacer esto:
// Invalid variance: The type parameter 'T' must be contravariantly valid on 'IRepo<T>.InsertSomething(T)'. 'T' is covariant.
interface IRepo<out T> where T: IModel {
T ReturnSomething();
void InsertSomething(T thing);
}
class Repo : IRepo<MyModel> {
public MyModel ReturnSomething() { return default(MyModel); }
public void InsertSomething(MyModel thing) { }
}
así que trato de añadir dos parámetros:
interface IRepo<out TReturn, TInsert>
where TReturn : IModel
where TInsert : IModel
{
TReturn ReturnSomething();
void InsertSomething(TInsert thing);
}
Y me da el mismo error que en el primer ejemplo. Obtengo el mismo error al usar in TInsert
Entonces, ¿cómo podría admitir tanto la inserción como la obtención?
EDITAR: Así que he encontrado una posible solución, pero es ahora de ser óptima
interface IRepo<out TResult> where TResult : IModel {
TResult ReturnSomething();
// I need to duplicate my constraint here..
void InsertSomething<TInsert>(TInsert thing) where TInsert : IModel;
}
class Repo : IRepo<MyModel> {
public MyModel ReturnSomething() { return default(MyModel); }
// ... And here
public void InsertSomething<T>(T thing) where T: IModel { }
}
Edit2: En respuesta a Eric: Este es un ejemplo más completo de lo que yo Estoy tratando de lograr. Realmente me gustaría la covarianza para poder agrupar las instancias IRepo, y aún quiero que tengan métodos de agregar/actualizar usando el modelo como instancia. Entiendo que no puedo obtener seguridad de tipo de tiempo de compilación para agregar elementos, pero para este caso de uso, solo necesito leer los elementos.
interface IModel { }
class SomeModel : IModel { }
class OtherModel : IModel { }
interface IRepo<T>
{
T ReturnSomething();
void AddSomething(T thing);
}
interface ISubRepo<T> : IRepo<T> where T : IModel { }
class SomeSubRepo : ISubRepo<SomeModel> {
public SomeModel ReturnSomething() { return default(SomeModel); }
public void AddSomething(SomeModel thing) { }
}
class OtherSubRepo : ISubRepo<OtherModel> {
public OtherModel ReturnSomething() { return default(OtherModel); }
public void AddSomething(OtherModel thing) { }
}
class Program {
static void Main(string[] args)
{
ISubRepo<IModel>[] everyone = new ISubRepo<IModel>[] {
new SomeSubRepo(),
new OtherSubRepo()
};
WorkOnAll(everyone);
}
static void WorkOnAll(IEnumerable<ISubRepo<IModel>> everyone)
{
foreach(ISubRepo<IModel> repo in everyone) {
IModel model = repo.ReturnSomething();
// Etc.
}
}
}
Me resulta difícil entender exactamente cuál es la pregunta. ¿Estás preguntando por qué una pieza particular de código no funciona? Si es así, ¿puede proporcionar solo ese fragmento de código junto con una breve descripción de por qué cree que debería comportarse de manera diferente? –
@Eric: He agregado un ejemplo más completo. Espero que esto aclare un poco. – simendsjo