C# 为什么可以';I隐式地将一个类转换为它的泛型基类吗? 公共抽象类基类 { 受保护的抽象InnerFooBase GetInnerFoo(); 受保护的抽象类InnerFooBase,其中TFoo:InnerBarBase { } 受保护抽象类InnerBarBase { } } 公共类实现:基本类 { 受保护的重写InnerFooBase GetInnerFoo() { 返回新的InnerFoo();//错误 } 类InnerFoo:InnerFooBase { } InnerBar类:InnerBarBase { } }
第18行给出错误“无法将类型C# 为什么可以';I隐式地将一个类转换为它的泛型基类吗? 公共抽象类基类 { 受保护的抽象InnerFooBase GetInnerFoo(); 受保护的抽象类InnerFooBase,其中TFoo:InnerBarBase { } 受保护抽象类InnerBarBase { } } 公共类实现:基本类 { 受保护的重写InnerFooBase GetInnerFoo() { 返回新的InnerFoo();//错误 } 类InnerFoo:InnerFooBase { } InnerBar类:InnerBarBase { } },c#,C#,第18行给出错误“无法将类型Implementation.InnerFoo隐式转换为Base.InnerFooBase” 我可以通过让InnerFoo从InnerBarBase派生而不是InnerBar来修复错误,但我不想这样做。我需要代码来强制执行InnerFoo和InnerBar之间的关系 InnerFoo源自InnerFooBase,InnerBar源自innerbase。如果GetInnerFoo正在查找InnerFooBase,为什么不能自动将类型转换为它们的基?请查看 如果泛型类实现
Implementation.InnerFoo
隐式转换为Base.InnerFooBase
”
我可以通过让InnerFoo
从InnerBarBase
派生而不是InnerBar
来修复错误,但我不想这样做。我需要代码来强制执行InnerFoo
和InnerBar
之间的关系
InnerFoo
源自InnerFooBase
,InnerBar
源自innerbase
。如果GetInnerFoo
正在查找InnerFooBase
,为什么不能自动将类型转换为它们的基?请查看
如果泛型类实现一个接口,则该接口的所有实例
类可以强制转换到该接口。泛型类是不变的。在里面
换句话说,如果输入参数指定了一个列表
,则
如果您试图提供
列表
在您的示例中,您希望返回类型为基类
InnerBarBase
,但您试图返回派生类InnerFoo
,以说明此操作失败的原因。我将按如下方式重命名您的类:
public abstract class Base<T>
{
protected abstract InnerFooBase<InnerBarBase<T>> GetInnerFoo();
protected abstract class InnerFooBase<TFoo> where TFoo : InnerBarBase<T>
{
}
protected abstract class InnerBarBase<TBar>
{
}
}
public class Implementation : Base<string>
{
protected override InnerFooBase<InnerBarBase<string>> GetInnerFoo()
{
return new InnerFoo(); // Error
}
class InnerFoo : InnerFooBase<InnerBar>
{
}
class InnerBar : InnerBarBase<string>
{
}
}
现在我们有了一个迷你水族馆——根据定义,它只有金鱼缸——里面有一个鲨鱼缸
编译器唯一能产生错误的地方是从迷你水族馆到鱼笼动物园的转换
如果类型是接口,类型参数是引用类型,并且接口没有“add”样式方法,并且可变参数标记为“out”,则只能执行这种转换(协变转换)
回到您的域:我们不能在需要InnerFooBase
的上下文中使用InnerFoo
。InnerFoo
是一个只使用InnerBar
s的InnerFooBase
,但我们需要一个可以使用任何innerbase
的InnerFooBase
。世界上可能有innerbase
的派生类不是InnerBar
。所以类型系统拒绝你这么做
这是多么混乱,你可能会考虑退一步,询问你的泛型类型和嵌套类型的排列是否太复杂,以至于未来程序员无法理解。没有必要试图捕获类型系统中程序行为的所有可能限制。正如我们刚才所看到的,当你尝试这样做时,有时类型系统会强制执行你不想表达的限制。
正如@Thiago提到的,这是一个协变和逆变的问题。但是您可以通过使用InnerFooBase的TFoo类型参数的“out”规范来告诉编译器它是协变的,从而绕过这个问题 i、 例如,改变 zoo.Add(new SharkTank());
受保护的抽象类InnerFooBase,其中TFoo:InnerBarBase
到
受保护的接口IINNerFooobase,其中TFoo:InnerBarBase
这个问题对于使用“out”规范有很好的解释:
请注意,InnerFooBase不再是一个抽象类,而是一个接口。这是使用“out”的一个限制。您可能需要检查自己是否负担得起这样的设计更改 协变和逆变的问题,因为你已经清楚地看到了(因为你必须通过添加“为什么”来更改标题)——你需要什么样的额外解释?没有理由小题大做。我没有看到。我无法想象您在询问之前没有搜索错误消息。。。喜欢因为假设A:B意味着T,并且可以安全地向上投票。@AlexeiLevenkov:你无法想象有人会在没有先在搜索引擎中查找问题的情况下将问题发布到StackOverflow?我可以想象。我每天都在想象。
class Animal {}
class Fish : Animal {}
class Cage<TAnimal> where TAnimal : Animal { }
class GoldfishBowl : Cage<Fish> { }
class B<TAnimal> where TAnimal : Animal
{
public class Zoo<TCage> where TCage : Cage<TAnimal>
{
public void Add(TCage cage) {}
}
}
class MiniAquarium : B<Fish>.Zoo<GoldfishBowl> { }
B<Fish>.Zoo<Cage<Fish>> zoo = new MiniAquarium();
class SharkTank : Cage<Fish> { }
zoo.Add(new SharkTank());
protected abstract class InnerFooBase<TFoo> where TFoo : InnerBarBase<T>
protected interface IInnerFooBase<out TFoo> where TFoo : InnerBarBase<T>