Matt Murrell
Matt Murrell

Reputation: 2351

Generic Covariance and Casting to SuperType

I have an OO problem that I think can be tied back to Generic Covariance. I am trying to build a modular system for importing different types of records... Module contains the common methods, and the SalesModule contains the functions that handle specific logic...

public interface IImportable { ... void BuildSqlDataRecord(); ...  }
public class Sales : IImportable { ... }
public interface IModule<out T> where T : IImportable
{
    void Import(IEnumerable<T> list);  // Error Occurs here...
    IEnumerable<T> LoadFromTextFile(TextReader sr);
}
public abstract class Module<T> : IModule<T> where T : IImportable
{ 
    public void Import(IEnumerable<T> list) { ... T.BuildSqlDataRecord(); ... } 
    public IEnumerable<T> LoadFromTextFile(TextReader sr) { ... }
}
public class SalesModule : Module<Sales>
{
    public override void BuildSqlDataRecord() { ... }; 
}

and in the other function:

//Module<IImportable> module = null;
IModule<IImportable> module = null;
if(file.Name == "SALES")
    module = new SalesModule();
else
    module = new InventoryModule();

var list = module.LoadFromTextFile(sr);
module.Import(list);  

How do I declare the module such that I can call the overridden methods?

Upvotes: 3

Views: 1145

Answers (2)

Eric Lippert
Eric Lippert

Reputation: 660040

public interface IModule<out T> where T : IImportable
{
    void Import(IEnumerable<T> list);  // Error Occurs here...
    IEnumerable<T> LoadFromTextFile(TextReader sr);
}

The error is correct. We chose "out" as the keyword that indicates covariance as a reminder to you that T can only appear in "output" positions. In the line you highlight, T appears as an input.

T must not be an input because... well, suppose it was allowed and see what bad things happen:

IModule<Giraffe> gm = GetMeAModuleOfGiraffes();
IModule<Animal> am = gm; // Legal because of covariance.
IEnumerable<Tiger> tigers = GetMeASequenceOfTigers();
IEnumerable<Animal> animals = tigers; // Legal because of covariance.
am.Import(animals); // Uh oh.

You just imported a list of tigers into a module that only knows how to handle giraffes.

To prevent this, the step that has to be made illegal is the very first one. The type declaration is illegal with "out".

How do I declare the module such that I can call the overridden methods?

You have to declare the interface so that it obeys the rules of covariance. How you do that is up to you, but start by not putting any "out" parameters into "input" positions.

Upvotes: 10

Rich O&#39;Kelly
Rich O&#39;Kelly

Reputation: 41757

You'll need to use an interface for your module:

public interface IModule<out T> where T : IImportable 
{
  void DoStuff();
  void DoOtherStuff();
}

Then you'll be able to declare your module like so:

IModule<IImportable> = null;

See here for the MSDN documentation for the out generic mdoifier.

Upvotes: 3

Related Questions