I have an interface which defines a reader and a writer for any IFoo.
public interface IFoobarStore<out E>
where E : class, IFoobar
{
IFoobarReader<E> GetReader();
IFoobarWriter<E> GetWriter();
}
IFoobarStore is covariant. IFoobarStore interacts with any derived IFoo. As such, any more derived IFoo should be assignable to a more derived IFoo type argument.
// DerivedFoobityStore.cs
public sealed class DerivedFoobityStore
: IFoobarStore<MyFoobity>
{
// implementation follows
}
If IFoobarStore were defined as being variant with IFoobarStore<E>
instead of IFoobarStore<out E>
, the following would produce compiler error CS0266.
IFoobarStore<IFoo> myGenericStore = new DerivedFoobityStore();
The reader is defined as covariant as well. It should allow reading derived IFoo objects from somewhere.
using System.Collections.Generic;
public interface IFoobarReader<out E>
where E : class, IFoo
{
IEnumerable<E> GetAll();
IEnumerable<E> GetBy(params object[] vars);
E GetSingle(object uniqueIdentifier);
}
IFoobarWriter exposes members used for standard CRUD operations on any IFoo.
public interface IFoobarWriter<in E>
where E : class, IFoo
{
void Add(E foo);
int Delete(E foo);
E Update(E foo);
}
Since every operation has a single argument of type E (any class derived from IFoo), IFoobarWriter must be flagged as contravariant.
When I compile my code I receive this error:
Invalid variance: The type parameter 'E' must be contravariantly valid on 'IFoobarStore<E>.GetWriter()'. 'E' is covariant.
How can I better refactor this code so it compiles successfully?
For the moment I got around it by refactoring IFoobarWriter to work with an object instead of an IFoo.
public interface IFoobarWriter<out E>
where E : class, IFoo
{
void Add(object foo);
int Delete(object foo);
object Update(object foo);
}
This renders the basic premise of IFoobarWriter obsolete.
The solution was to remove E as an acceptable argument for instance member methods of IFoobarWriter.
public interface IFoobarWriter<out E>
where E : class, IFoo
{
void Add(IFoo foo);
int Delete(IFoo foo);
object Update(IFoo foo);
}
By having Add, Delete, and Update accept IFoo
they effectively limit the types they can work on (as opposed to setting the argument to object
) well enough for certain business requirements.
Having the type parameter E for IFoobarWriter remain covariant allows it to remain a part of the IFoobarStore interface.