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

     2023-03-31     167

关键词:

【中文标题】泛型和强制转换 - 不能将继承的类强制转换为基类【英文标题】:Generics and casting - cannot cast inherited class to base class 【发布时间】:2010-08-20 07:06:11 【问题描述】:

我知道这已经过时了,但我仍然不太了解这些问题。谁能告诉我为什么以下方法不起作用(抛出关于投射的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;

那么,任何人都可以解释这是无效的吗?而且,我没有心情解释 - 有没有一行代码我可以用来实际执行此转换?

【问题讨论】:

查看C# Covariance and Contravariance FAQ 感谢大家的回答。简而言之 - 我现在使用基本接口 (RepositoryBase : IRepository) 解决了这个问题。结果我只需要在我得到的实例上执行函数,让类本身处理其他事情。 【参考方案1】:

RepositoryBase&lt;EntityBase&gt; 不是 MyEntityRepository 的基类。您正在寻找在 C# 中有限存在但不适用于此处的 泛型变量

假设你的 RepositoryBase&lt;T&gt; 类有这样的方法:

void Add(T entity)  ... 

现在考虑:

MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo; 
baseRepo.Add(new OtherEntity(...));

现在您已向 MyEntityRepository... 添加了不同类型的实体,这是不对的。

基本上,泛型方差仅在某些情况下是安全的。特别是通用的协方差(这就是你在这里描述的)只有当你只从 API 中获取值时才是安全的;通用逆变(反之亦然)只有在您只将值“放入”API 时才是安全的(例如,可以按区域比较任何两个形状的一般比较可以被视为比较正方形)。

在 C# 4 中,这可用于泛型接口和泛型委托,而不是类 - 并且仅适用于引用类型。有关详细信息,请参阅 MSDN,阅读 阅读 C# in Depth, 2nd edition,第 13 章 或 Eric Lippert 的 blog series 关于该主题。另外,我在 2010 年 7 月的 NDC 上就这个问题进行了一个小时的讨论 - 视频可在 here 获得。

【讨论】:

感谢 Jon 的解释和非常有趣的材料的链接。我一定会考虑阅读这本书并观看视频。 你的视频链接好像坏了。 @ZacharyBurns:已修复,谢谢。我有点惊讶它仍然可用:)【参考方案2】:

每当有人问这个问题时,我都会尝试以他们的示例并将其转换为使用更知名的显然非法的类的东西(这就是Jon Skeet has done in his answer;但我通过执行此操作更进一步翻译)。

让我们将MyEntityRepository 替换为MyStringList,如下所示:

class MyStringList : List<string>  

现在,您似乎希望将MyEntityRepository 转换为RepositoryBase&lt;EntityBase&gt;,原因是这应该是可能的,因为MyEntity 派生自EntityBase

但是string 派生自object,不是吗?所以按照这个逻辑,我们应该能够将MyStringList 转换为List&lt;object&gt;

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

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&lt;string&gt; 时遇到了Random 对象。这不好。

希望这能让问题更容易理解。

【讨论】:

我已经开始在示例中更进一步 - 我曾经使用字符串和对象,但在 Eric Lippert 的建议下,我开始使用现实世界的对象......水果/苹果/香蕉效果很好用“你不能在一堆香蕉中加一个苹果”的说法。【参考方案3】:

这需要协变或逆变,其支持在.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;

【讨论】:

使用受保护的继承公开基类转换函数

...或私有。但是,我确实希望将子对象隐式转换为基类。此强制转换的默认实现在大多数情况下是隐藏的,因为 查看详情

无法将参数 1 从派生指针转换为基类指针引用

】无法将参数1从派生指针转换为基类指针引用【英文标题】:Cannotconvertparameter1fromderivedpointertobaseclasspointerreference【发布时间】:2014-03-3108:37:55【问题描述】:我有继承自基类的类,然后我有方法,除了作为对基类的参数指针引... 查看详情

虚拟多重继承和强制转换

】虚拟多重继承和强制转换【英文标题】:Virtualmultipleinheritanceandcasting【发布时间】:2013-12-2320:00:48【问题描述】:我尝试创建一个继承自多个类的类,如下所示,得到一个“钻石”(D继承自B和C。B和C都继承自A实际上): A/\\... 查看详情

将子类转换为基类?

】将子类转换为基类?【英文标题】:Convertingsubclasstobaseclass?【发布时间】:2018-04-1002:34:55【问题描述】:我正在解决一个问题,其中我们有一个由节点组成的二叉搜索树。节点的属性包括字符串、指向左节点的指针和指向右节... 查看详情

关于泛型那些事?

关于泛型那些事? 1.关于泛型和子类继承:      对于以前关于继承而言,其中子类和父类可以进行类型转换,这就是我们常说的类型转换(向上转型和向下转型)。其中我们知道将父类转换为子类需要进行强制... 查看详情

c++强制类型转换(代码片段)

...t用来进用行比较“自然”和低风险的转换,比如整型和实数型、字符型之间互相转换。static_cast不能来在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,也不能用于不同类型的引用之间的转换。#inc... 查看详情

<继承问题>可以把基类对象赋值给子类对象么?

...来的basea;deriveb;a=b;//可行那么b=a可以么?不可以,子类可以强制转换为基类,是因为子类继承自基类(你这里则是b继承自a),所以a的一切属性等b都有,而反之则不会成立,所以无法强制类型转换。参考技术A按lz的说法,b是a的子... 查看详情

不明白java中的泛型和抽象类有啥区别,感觉他们作用一样啊,为啥要用2种方法呢

参考技术A泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。其实泛型正体现了java的多态性抽象类是一个类只能被继承。如果一个类里包含了一个或多个抽象方法,类就必须... 查看详情

使用纯多态性和继承从基类调用派生类上的函数而不进行强制转换?

...使用纯多态性和继承从基类调用派生类上的函数而不进行强制转换?【英文标题】:Usingpurelypolymorphismandinheritencetocallfunctionsonaderivedclassfrombaseclasswithoutcasting?【发布时间】:2020-03-0412:38:14【问题描述】:我的问题是:我有一个纸... 查看详情

typeof 泛型和强制类型 [重复]

】typeof泛型和强制类型[重复]【英文标题】:typeofgenericandcastedtype[duplicate]【发布时间】:2019-07-1613:41:30【问题描述】:假设我们有通用方法:publicvoidGenericMethod<T>(Titem)vartypeOf=typeof(T);vargetType=item.GetType();我们使用以下参数调... 查看详情

将派生类转换为基类

】将派生类转换为基类【英文标题】:CastingDerivingClassasBaseClass【发布时间】:2017-01-0801:26:10【问题描述】:假设我有这个:使用系统;公开课程序公共静态无效Main()BaseClassbc=newDerivedClass();bc.Method1();bc.Method2();Console.WriteLine(bc.GetTyp... 查看详情

您可以强制从抽象基类继承的类仅具有在基本情况下定义的公共方法吗?

】您可以强制从抽象基类继承的类仅具有在基本情况下定义的公共方法吗?【英文标题】:Canyouforceclassesinheritingfromabstractbaseclasstoonlyhavethepublicmethodsdefinedinbasecase?【发布时间】:2015-03-0708:40:06【问题描述】:是否可以有一个抽象... 查看详情

将派生类转换为基类

】将派生类转换为基类【英文标题】:Castderivedclasstobase【发布时间】:2015-12-1414:02:46【问题描述】:在这里(B::get())发生什么类型的演员?classApublic:A():a(0)inta;;classB:publicApublic:A*get()returnthis;//isthisC-stylecast?;intmain()Bb;cout<<b.g... 查看详情

接口上的 C# 泛型隐式强制转换失败

】接口上的C#泛型隐式强制转换失败【英文标题】:C#genericimplicitcastonInterfacefailed【发布时间】:2010-11-2520:17:41【问题描述】:为什么下面的不能编译?当T是一个接口时,导致编译器认为它不能从Container&lt;T&gt;转换为T的接... 查看详情

java示例代码_使用通配符将泛型传递给函数,而无需强制转换

java示例代码_使用通配符将泛型传递给函数,而无需强制转换 查看详情

泛型:强制转换和值类型,为啥这是非法的?

】泛型:强制转换和值类型,为啥这是非法的?【英文标题】:Generics:castingandvaluetypes,whyisthisillegal?泛型:强制转换和值类型,为什么这是非法的?【发布时间】:2021-09-1106:10:34【问题描述】:为什么这是编译时错误?publicTCastTo... 查看详情

将指针从派生类转换为基类的最佳方法

】将指针从派生类转换为基类的最佳方法【英文标题】:Bestapproachforcastingpointertomethodfromderivedtobaseclass【发布时间】:2019-05-2315:59:13【问题描述】:我们有一个旨在通用的基类ByteCode。ByteCode的子级应该编写如下形式的方法:voidm(... 查看详情

c#类型转换

...从派生类转换为基类。显式类型转换-显式类型转换,即强制类型转换。显式转换需要强制转换运算符,而且强制转换会造成数据丢失。下面的实例显示了一个显式的类型转换:namespaceTypeC 查看详情