-1

I'm trying to add a few concrete instances into collection where all the concrete instances inherit from the same interface. But it doesn't compile.

// Common interface.
public interface ISearchService<in TSearchOptions> where TSearchOptions : ISearchOptions
{ .. }

// Concrete #1.
public class FooSearchService : ISearchService<FooSearchOptions>
{ .. }

// Concrete #2.
public class BaaSearchService : ISearchService<BaaSearchOptions>
{ .. }

So with the above code, both Foo and Baa both inherit from an ISearchService<ISearchOptions> but each one has defined their own specific SearchOptions type.

So then I'm trying this (which doesn't compile) :

var searchServices = new List<ISearchService<ISearchOptions>>
{
    new FooSearchService(),
    new BaaSearchService()
};

I've written the full code up on .NET Fiddle.

The idea is that I have a common collection of concrete instances which they all have a common base interface.

I'm pretty sure that I'm failing at this because of some *variance sillyness which I just don't understand.

I thought maybe I'm trying to do too much .. so I've tried a new .NET Fiddle with something different but that also doesn't work.

I just want a mixed bag of a common generic interface.

halfer
  • 19,824
  • 17
  • 99
  • 186
Pure.Krome
  • 84,693
  • 113
  • 396
  • 647
  • 1
    I think you mean "implement a common interface". Inheritance is a class/subclass concept (search for "implements" vs "inherits"). It's best to use proper language concepts so not to confuse other readers which may be learning. – Joseph Rosson Jul 24 '16 at 03:40

2 Answers2

1

You should use out instead of in:

out means covariant which means that the argument can change from a class to one of its base classes. Think of method's return type (output), if you can take the result of the method as the base class, you can take any of its derived classes.

in means contravariant which means that the argument can change from a class to a class derived from it. Think of method's parameters (input), if your method can accept a derived class, you can pass to a method that wants a base class.

public interface ISearchService<out T> where T : ISearchOptions
{  
}

Note: Thinking of method's input and output is just my strategy to remember when to use out and in, it's also the way delegate generic type parameters work. Basically, covariant and contravariant work in the same way for both interface and delegate.

Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • U R G H :( Your answer worked ... until ... i added the following method to the interface: `Task SearchAsync(TSearchOptions searchOptions);` FML :( – Pure.Krome Jul 24 '16 at 08:59
  • @Pure.Krome: `out` means `output`, but you're using it as input which is invalid: http://stackoverflow.com/questions/5041664/t-must-be-contravariantly-valid – Khanh TO Jul 24 '16 at 09:21
  • So there's no way to do this? – Pure.Krome Jul 24 '16 at 11:06
  • @Pure.Krome: What you're trying to do does not make sense because it will lose type safety as pointed out in the link about. You could workaround this by defining an `ISearchService` interface (but it still loses type safety), see https://dotnetfiddle.net/1vbcC2. – Khanh TO Jul 24 '16 at 12:18
0

It compiles when you change the code to this:

public interface ISearchService<out TSearchOptions> where TSearchOptions : ISearchOptions
{  }
Enigmativity
  • 113,464
  • 11
  • 89
  • 172