Alex Rivera | Logout

C# Casting generics (covariance and contravariance?)

Asked 2011-12-06T14:45:17.343
9

I need some advice/help on this, I can't see the wood from the trees any more.

It's a straight forward series of classes implementing some interfaces using generics.

Then I'm trying to cast the concrete types for example:

MyGenericObject<SomeObject> _obj;

IMyGenericObject<ISomeObject> _genObj = (IMyGenericObject<ISomeObject>)_obj;

// Invalid cast

I've read some articles about covariance and contravariance but not too clear why this wouldn't be possible, or how to get round it?


So, in this example:

public interface IMyObject<in T> where T : IBaseObject
{
    T Activity { get; set; }
}

wouldn't work...


....because, you can't get and set the Activity property.

In this example, I needed to do:

public interface IMyObject<out T> where T : IBaseObject
    {
        T Activity { get; }
    }

hope that helps someone, and thanks to all for help!

Edit
Report

1 Answer

12

You can only do that if you declare the interface as having a covariant (out) parameter. You can only do that if the parameter is used covariantly.

For example, if the interface IMyGenericObject<T> has a method taking a T parameter, this prevents you from declaring the parameter as covariant. Conversely, if there is a method that returns a T, that prevents you from declaring the parameter as contravariant.

EDIT

In response to your comment on SLaks's answer, I'm tempted to repeat everything Eric Lippert has ever written on co- and contravariance. See http://blogs.msdn.com/b/ericlippert/archive/tags/Covariance+and+Contravariance/ and also his answers in SO (most recently https://stackoverflow.com/a/8380213/385844)

To summarize:

You can't cast IList<string> to IList<object> because it's legal to pass a FileInfo to an IList<object>, but it is not legal to pass it to an IList<string>.

You can't cast an IList<object> to an IList<string>, because it's legal to retrieve an item from an IList<string> and assign it to a string reference, but an IList<object> might contain a FileInfo, which can't be assigned to a string reference.

EDIT 2

Since you asked for advice, it's also possible to split your interfaces into co- and contravariant parts. To continue with the list example, you could have these interfaces

public interface ICovariantList<out T>
{
    T this[int index] { get; }
    //...
}

public interface IContravariantList<in T>
{
    T this[int index] { set; 
answered 2011-12-06T14:52:49.740

Your Answer