7

I'll attempt to shorten this code example:

public interface IThing
{
    //...  Stuff
}

public class Thing1 : IThing
{  
}

public class Thing2 : IThing
{  
}

public interface IThingView<out T>
{
    ICollection<T> ViewAll();
}

public class ThingView<T> : IThingView<T>
{
    ICollection<T> ViewAll() { return new List<T>(); }  //  There's a big operation here
}

public interface IThingViewerFactory
{
    public IThingView<IThing> Build(string Which);
}

public class ThingViewerFactory
{
    public IThingView<IThing> Build(string Which)
    {
        if(Which.Equals("Thing1") { return new (IThingView<IThing>)new ThingViewer<Thing1>();}
        else { return new (IThingView<IThing>)new ThingViewer<Thing2>();}
    }
}

That's a rough idea of what I'm doing. I have a number of Thing classes that require a viewer, which will follow a comon interface. I'd like a factory to generate these by me passing in a string with the name. I keep getting a compiler error complaining:

Invalid variance: The type parameter 'T' must be invariantly valid on 'IThingView.ViewAll()'. 'T' is covariant.

I realize even if I get this to work, I'll have to do some casting afterwards... I'm fine with that. And I realize this approach is more than likely not necessary. At this point this has become more of a pride/curiosity issue.

Thanks!

TheTFo
  • 741
  • 2
  • 8
  • 25
  • 3
    Just a side note: `Which.Equals("Thing1")` will leave you vulnerable to `NullReferenceException` which is simply unprofessional. – ChaosPandion May 26 '11 at 17:40

2 Answers2

11

You cannot make a covariant ICollection<T>, since it allows you to put Ts into it.

You can make a covariant read-only collection, a contravariant write-only collection, or an invariant read-write collection.
You can't do both, or it wouldn't be typesafe.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
6

To expand on SLaks answer:
To make your code compile, change the return type of ViewAll from ICollection<T> to IEnumerable<T>.

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443