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
757 views
in Technique[技术] by (71.8m points)

c# - cast of generic type fails

I am unable to cast a generic type to another generic type, besides the cast should be valid

What I want to archive is in short (for MyModel implementing IModel, and MyImplementation implementing IImplementation):

IImplementation<IModel> implementation = new MyImplementation<MyModel>();
Assert.IsNull(implementation as IImplementation<IModel>);

This is a bit confusing, as the type should be valid.

Complete conceptual model:

interface IModel {}

class MyModel : IModel {}

interface IImplementation<TModel> where TModel : IModel { }

class MyImplementation<TModel> : IImplementation<TModel>
    where TModel : IModel { }

public void CallRegister()
{
    var implementation = new MyImplementation<MyModel>();
    var instance = CastModel(implementation);
    Assert.IsNotNull(instance); //this assert fails!
}

private object CastModel<TModel>(IImplementation<TModel> implementation) where TModel : IModel
{
    return implementation as IImplementation<IModel>;
}

I need this cast to enable me to save multiple IImplementations to the same Dictionary<Type, IImplementation<IModel>>, where the key is obtained by doing typeof(TModel). To do this type safe I don't want to use a Dictionary<Type, object>.

  • Why does the cast fail? Are there additional resources to this? Its a similar question to Invalid Cast of Type Constrained C# Generic, but it is not explained why there just that it does not work.
  • What is the best way to archive a functionality similar to the dictionary as explained above if this kind of cast is not possible?
See Question&Answers more detail:os

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

1 Reply

0 votes
by (71.8m points)

Though Olivier's answer gets the idea across about why this usually goes wrong, there is a way to make this work in your program.

The feature you want is called generic interface covariance. Covariance is the property that if a Cat is an Animal, then an IFoo<Cat> is an IFoo<Animal>.

Covariance in C# only works in the following situations:

  • The "outer" type is an interface, delegate or array. No classes or structs.
  • If an interface or delegate, the type must be marked at compile time as supporting covariance. Arrays get (unsafe!) covariance for free.
  • The "inner" types -- the types that are varying -- are both reference types. You can't say that an IFoo<int> is an IFoo<object> even though an int is an object, because they are not both reference types.

To mark an interface as covariant, you put out before the declaration of the type parameter which you wish to allow to vary:

interface IImplementation<out TModel> where TModel : IModel { }

If you do that, your program will start to work.

HOWEVER, out is a reminder to you that covariance is only safe if T is used in output positions. This is legal:

interface I<out T> {
  T M();
}

This is not:

interface I<out T> {
  void M(T t);
}

In the first, T is only passed out of things. In the second, it is passed in.

In the first scenario, we cannot use covariance to introduce a type hole. We have an I<Cat> and we cast it to I<Animal>, and now M returns an Animal, but that's OK, because we already know that it will return a Cat, and a Cat is an Animal.

But in the second scenario, we have the opposite situation. If we allowed an I<Cat> to be converted to I<Animal> then we have an M that can take a Turtle, but the real implementation can only handle Cats. That's why C# will make this illegal.

So go forth and use covariance, but remember that you have to certify to the compiler that you want it, and that it is safe under all circumstances. If you don't want it, or it is not safe, then you don't get to have covariance, and you'll have to find a different solution to your problem.


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

...