0

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.

Daniel L.
  • 437
  • 3
  • 15
  • You have not included all the types in your post. Several types are missing, for example, `IFoobarWriter` and a few others. – CodingYoshi Jul 28 '17 at 17:13
  • Typographical errors on my part. IFoobarWriter<> == IFooWriter<>. MyFoobity is just a concrete implementation of IFoobar. It does not contribute to the problem. – Daniel L. Jul 28 '17 at 17:28
  • @DavidG marking from IFoobarStore to IFoobarStore produces compiler error CS0266. I will update my exampple. – Daniel L. Jul 28 '17 at 17:30
  • 1
    _"Typographical errors on my part"_ -- please **fix** the typos. For example, you write _"IFoobarStore interacts with any derived IFoo"_, but the code suggests you mean to write _"with any derived **IFoobar**"_. Your problem description is very hard to follow, with the inconsistencies in. You need to look at it carefully, putting yourself in the position of someone who hasn't seen _any_ of this code yet, and make sure everything makes sense. – Peter Duniho Jul 28 '17 at 17:31
  • All that said, if I understand the problem description correctly, you're not going to be able to do what you want. Contravariance means your `IFoobarWriter` implementation's parameter is `IFoo` or "larger" (where "larger" means "encompasses more types"); the implementation can't expect something more-derived than `IFoo`, because of course any code using the interface can only guarantee an `IFoo`. This is an all-too-common generic type variance question...I don't have time to look up the duplicate now, but I'm sure you're not the first person to try this and fail. – Peter Duniho Jul 28 '17 at 17:37
  • I can accept it has to do with my lack of practice using variance on interfaces. I'm not looking for a copy-and-paste or textbook answer. What I'd like to see are alternate approaches. – Daniel L. Jul 28 '17 at 17:42
  • _"alternate approaches"_ -- alternate approaches to what? The fact the code won't compile is telling you that what you're doing isn't safe. Alternate approaches to do literally what you're asking would also be not safe. So the only way to find alternate approaches that work would be for you to change the problem statement and figure out what _safe_ design you want to implement instead. We can't do that for you; there are too many possible answers. – Peter Duniho Jul 28 '17 at 23:03
  • I realize this is an odd and somewhat difficult problem to resolve. I've learned that Stackoverflow isn't the place for these sorts of questions. I have a solution and will answer my own question shortly. Thank you for your input. – Daniel L. Jul 31 '17 at 16:56

1 Answers1

0

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.

Daniel L.
  • 437
  • 3
  • 15
  • There's no reason to even have the type be generic at all, if you're never going to use the generic type argument. – Servy Jul 31 '17 at 17:07