我知道这是旧的,但我仍然不太了解这些问题。任何人都可以告诉我为什么以下不起作用(抛出关于投射的runtime
例外)?
public abstract class EntityBase { }
public class MyEntity : EntityBase { }
public abstract class RepositoryBase<T> where T : EntityBase { }
public class MyEntityRepository : RepositoryBase<MyEntity> { }
现在是铸造生产线:
MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo;
那么,任何人都可以解释这是如何无效的?并且,我没有心情去解释 - 是否有一行代码我可以用来实际演绎?
答案 0 :(得分:31)
RepositoryBase<EntityBase>
不是 MyEntityRepository
的基类。您正在寻找有限范围内存在于C#中的泛型差异,但此处不适用。
假设您的RepositoryBase<T>
类有这样的方法:
void Add(T entity) { ... }
现在考虑:
MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo;
baseRepo.Add(new OtherEntity(...));
现在你已经为MyEntityRepository
添加了一种不同类型的实体......而这可能不对。
基本上,通用方差在某些情况下是安全的。特别是通用的协方差(这是你在这里描述的)只有在你只获得API的“out”值时才是安全的。通用逆向(其他方式相反)只有在你将值“放入”API时才是安全的(例如,一般比较可以比较任何两个形状的面积可以被视为一个比较广场)。
在C#4中,这可用于通用接口和通用委托,而不是类 - 并且仅适用于引用类型。有关详细信息,请参阅MSDN,阅读&lt; plug&gt;阅读C# in Depth, 2nd edition,第13章&lt; / plug&gt;或者Eric Lippert关于这个主题的blog series。此外,我在2010年7月在国家数据中心进行了一小时的讨论 - 视频可用here。
答案 1 :(得分:17)
每当有人提出这个问题时,我都会尝试将他们的例子转化为使用明显非法的更为知名的类(这就是Jon Skeet has done in his answer;但是我更进一步了执行此翻译)。
我们将MyEntityRepository
替换为MyStringList
,如下所示:
class MyStringList : List<string> { }
现在,您似乎希望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);
}
糟糕,突然我们在List<string>
上进行了枚举,我们遇到了Random
个对象。那不好。
希望这会使问题更容易理解。
答案 2 :(得分:9)
这需要协方差或逆变,其支持仅限于.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;