【问题标题】:How to fix Cast errors when casting down generic parameters向下转换泛型参数时如何修复转换错误
【发布时间】:2019-05-26 16:55:58
【问题描述】:

在某些情况下,我需要将对象转换为接口以满足我的需求,这隐含地需要转换泛型接口的类型参数。

示例

ICage<TAnimal>Cage 类型动物IAnimal 的接口

public interface ICage<TAnimal>
    where TAnimal : IAnimal<IOwner>

public class Cage<TAnimal> : ICage<TAnimal>
    where TAnimal : IAnimal<IOwner>

public interface IAnimal<out TOwner>
    where TOwner : IOwner

IAnimal 需要 IOwner 类型的 Owner

public abstract class Mammal<TOwner> : IAnimal<TOwner>
    where TOwner : IOwner

哺乳动物是一种拥有 IOwner 类型所有者的动物。

public class Human : IOwner

HumanIOwner 的一种类型

public class Dog<TOwner> : Mammal<TOwner>
    where TOwner : IOwner

狗是哺乳动物的一种。

现在把所有东西放在一起:

 var cage = new Cage<Mammal<IOwner>>();
 var me = new Human()
 {
     Name = "Hakim"
 };
 var dog = new Dog<Human>();
 dog.Owner = me;
 cage.Add((Mammal<IOwner>)dog);

在最后一行我收到一个编译时错误 CS0030,告诉我我无法将 Dog 转换为 Mammel。

【问题讨论】:

  • 有趣的是,今天早些时候出现了这个问题的一个版本:stackoverflow.com/questions/53960364/…。发现它与stackoverflow.com/questions/15596005/… 重复。我特别喜欢对今天问题的评论(@vc74):如果第二个在任何接口上工作,你就可以调用List&lt;Panther&gt;.Add(fish),同时PantherFish 实现IAnimal我>。你的情况更难,我想你可以有一个 Neanderthal 实现 IOwner
  • 如果您在 cage 定义中使用特定类型,它应该可以工作:var cage = new Cage&lt;Mammal&lt;Human&gt;&gt;(); 然后只需将 dog 传递给 Add 方法而不进行任何转换。或者,您可以将两个参数设为通用,例如:var cage = new Cage&lt;IAnimal&lt;IOwner&gt;&gt;();,它也应该可以工作。
  • @Rufus L: 是否使用显式转换运算符
  • 你根本不需要投射。

标签: c# generics inheritance interface


【解决方案1】:

这也是you can't cast a List&lt;string&gt; to a List&lt;object&gt;的原因。

假设在Mammal 中有一个名为Owner 的属性,如下所示:

public TOwner Owner { get; set; }

对于Mammal&lt;IOwner&gt; 的实例,这变成:

public IOwner Owner { get; set; }

dog 是一个Dog&lt;Human&gt;,它也是一个Mammal&lt;Human&gt;如果可以dog转换为Mammal&lt;IOwner&gt;,这意味着dog.Owner可以突然存储任何实现IOwner的类型。即这是可能的:

class EvilOwner : IOwner { ... }

Mammal<IOwner> mammal = (Mammal<IOwner>)dog;
mammal.Owner = new EvilOwner();

但这是不可能的,因为运行时的dogDog&lt;Human&gt;,它的OwnerHumanEvilOwner不可能存储在Human中!

我建议你做的是删除TOwner 泛型参数。如果您认为Dog&lt;Human&gt; 也是一种 Mammal&lt;IOwner&gt;,那么设计这样的类可能更有意义:

public interface IAnimal {
    IOwner Owner { get; }
}

public abstract class Mammal : IAnimal { ... }

public class Dog : Mammal { ... }

【讨论】:

  • 根据您的建议,我获得了一些灵活性,但失去了举例说明狗必须拥有人类作为其所有者的能力。 (您可以在 IAnimal 上设置 SetOwner(IOwner) 方法并执行某种类型检查/转换,但这并不理想)。
  • @Hakim 那么你不能Dog转换为Mammal&lt;IOwner&gt;。如果允许,您可以将狗的主人设置为 Human 以外的其他名称,不是吗?
  • 您绝对正确,但如果是 List<string> to List<object> 的演员,可以投射物品吗?!
  • @Hakim 我明白了。然后您必须使用您的Dog&lt;Human&gt; 实例创建Dog&lt;IOwner&gt;new 实例,就像要将List&lt;string&gt; 转换为List&lt;object&gt;,您需要创建一个new 列表。之后,投射到Mammal&lt;IOwner&gt;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-12
  • 1970-01-01
相关资源
最近更新 更多