Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
569 views
in Technique[技术] by (71.8m points)

.net - C# Casting generics (covariance and contravariance?)

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!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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; }
    void Add(T item);
    //...
}

public class SomeList<T> : ICovariantList<T>, IContravariantList<T>
{
    //...
}

This allows you to use the class covariantly or contravariantly, depending on the context.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...