泛型和强制转换 - 不能将继承的类强制转换为基类

Jef*_*fim 26 c# generics casting

我知道这是旧的,但我仍然不太了解这些问题.任何人都可以告诉我为什么以下不起作用(抛出一个runtime关于铸造的例外)?

public abstract class EntityBase { }
public class MyEntity : EntityBase { }

public abstract class RepositoryBase<T> where T : EntityBase { }
public class MyEntityRepository : RepositoryBase<MyEntity> { }
Run Code Online (Sandbox Code Playgroud)

而现在的铸造生产线:

MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo;
Run Code Online (Sandbox Code Playgroud)

那么,任何人都可以解释这是如何无效的?而且,我没有心情去解释 - 是否有一行代码我可以用来实际进行演员表演?

Jon*_*eet 31

RepositoryBase<EntityBase>不是一个基类MyEntityRepository.您正在寻找有限范围内存在于C#中的泛型差异,但不适用于此处.

假设你的RepositoryBase<T>类有这样的方法:

void Add(T entity) { ... }
Run Code Online (Sandbox Code Playgroud)

现在考虑:

MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo; 
baseRepo.Add(new OtherEntity(...));
Run Code Online (Sandbox Code Playgroud)

现在你已经为...添加了一种不同类型的实体MyEntityRepository,这可能不对.

基本上,通用方差在某些情况下是安全的.特别是通用协方差(这是你在这里描述的)只有在你只获得API的"值"时才是安全的; 一般的逆向性(反之亦然)只有在你将值"放入"API时才是安全的(例如,可以将按区域比较任意两种形状的一般比较视为方块的比较).

在C#4中,这可用于通用接口和通用委托,而不是类 - 并且仅适用于引用类型.有关详细信息,请参阅MSDN,阅读<plug>阅读C#深度,第2版,第13章</ plug>或Eric Lippert 关于该主题的博客系列.此外,我在2010年7月在NDC进行了一小时的讨论 - 视频可在此处获得.


Dan*_*Tao 18

每当有人提出这个问题时,我都会尝试用他们的例子将其翻译成一些使用明显非法的知名课程(这就是Jon Skeet在他的答案中所做的事情 ;但我通过表演更进了一步这个翻译).

让我们来代替MyEntityRepositoryMyStringList,就像这样:

class MyStringList : List<string> { }
Run Code Online (Sandbox Code Playgroud)

现在,你似乎想要MyEntityRepository施展RepositoryBase<EntityBase>,理由是这应该是可能的,因为它MyEntity来源于EntityBase.

但是string源于object,不是吗?因此,通过这种逻辑,我们应该能够投射MyStringList到一个List<object>.

让我们看看如果我们允许那会发生什么......

var strings = new MyStringList();
strings.Add("Hello");
strings.Add("Goodbye");

var objects = (List<object>)strings;
objects.Add(new Random());

foreach (string s in strings)
{
    Console.WriteLine("Length of string: {0}", s.Length);
}
Run Code Online (Sandbox Code Playgroud)

嗯,哦.突然间,我们列举了一个List<string>,我们遇到了一个Random物体.这不好.

希望这使问题更容易理解.

  • 我已经开始在示例中更进一步 - 我曾经使用过字符串和对象,但在Eric Lippert的建议下我开始使用真实世界的对象......水果/ Apple/Banana在"你不能"方面表现良好在一堆香蕉上加一个苹果". (5认同)

Mar*_*k H 10

这需要协方差或逆变,其支持仅限于.Net,不能用于抽象类.您可以在接口上使用方差,因此您的问题的可能解决方案是创建一个IRepository,您可以使用它来代替抽象类.

    public interface IRepository<out T> where T : EntityBase { //or "in" depending on the items.
    }
    public abstract class RepositoryBase<T> : IRepository<T> where T : EntityBase {
    }
    public class MyEntityRepository : RepositoryBase<MyEntity> {
    }

    ...

    IRepository<EntityBase> baseRepo = (IRepository<EntityBase>)myEntityRepo;
Run Code Online (Sandbox Code Playgroud)