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>
{
}
}
Run Code Online (Sandbox Code Playgroud)
第18行给出错误"无法将类型隐式转换Implementation.InnerFoo为Base<string>.InnerFooBase<Base<string>.InnerBarBase<string>>"
我可以通过具有修复错误InnerFoo从派生InnerBarBase<string>,而不是InnerBar,但我并不想这样做.我需要的代码执行beetween的关系InnerFoo和InnerBar.
InnerFoo源自InnerFooBase<InnerBar>,InnerBar源自InnerBarBase<string>.如果GetInnerFoo正在寻找InnerFooBase<InnerBarBase<string>>,为什么这些类型不能自动转换为它们的基础?
为了说明失败的原因,我将按如下方式重命名您的类:
InnerBarBase<T> -> Cage<T>
string -> Fish
InnerBar -> GoldfishBowl
InnerFooBase -> Zoo
InnerFoo -> MiniAquarium
Run Code Online (Sandbox Code Playgroud)
我将简化您的场景以使用赋值而不是虚拟覆盖.分配时更容易看出问题.
行.我们来设置类型.
class Animal {}
class Fish : Animal {}
class Cage<TAnimal> where TAnimal : Animal { }
Run Code Online (Sandbox Code Playgroud)
笼子是可以容纳动物的东西.
class GoldfishBowl : Cage<Fish> { }
Run Code Online (Sandbox Code Playgroud)
金鱼缸是一个可以容纳鱼的笼子.
class B<TAnimal> where TAnimal : Animal
{
public class Zoo<TCage> where TCage : Cage<TAnimal>
{
public void Add(TCage cage) {}
}
}
Run Code Online (Sandbox Code Playgroud)
A B<TAnimal>.Zoo<TCage>是一系列可以容纳特定动物的笼子.请注意,我创建了一个原始程序中不存在的"添加"方法.
class MiniAquarium : B<Fish>.Zoo<GoldfishBowl> { }
Run Code Online (Sandbox Code Playgroud)
一个微型水族馆是一个只包含金鱼碗的动物园.
现在的问题是:我是否可以在我想要一个包含鱼笼的动物园的环境中使用迷你水族箱?没有!原因如下:
B<Fish>.Zoo<Cage<Fish>> zoo = new MiniAquarium();
Run Code Online (Sandbox Code Playgroud)
我们假设这是合法的.这不合法.但我们假设它是.出了什么问题?我创造了第二种鱼笼:
class SharkTank : Cage<Fish> { }
Run Code Online (Sandbox Code Playgroud)
现在,我完全合法地说:
zoo.Add(new SharkTank());
Run Code Online (Sandbox Code Playgroud)
现在我们有一个迷你水族馆 - 根据定义只包含金鱼碗 - 它里面有一个鲨鱼缸.
编译器可以产生错误的唯一地方是从MiniAquarium到鱼笼动物园的转换.
您只能进行这种转换 - 协变转换 - 如果类型是接口,类型参数是引用类型,并且接口没有"添加"样式方法,并且变化参数标记为"out".
回到您的域:我们不能InnerFoo在需要a的上下文中使用InnerFooBase<InnerBarBase<string>>.一个InnerFoo是一个InnerFooBase消耗只有 InnerBar S,但我们需要的InnerFooBase是能够消耗任何 InnerBarBase<string>.InnerBarBase<string>世界上可能存在派生类而不是InnerBar.所以类型系统拒绝让你这样做.
考虑到这是多么令人困惑,您可以考虑退一步,询问您的泛型类型和嵌套类型的安排是否过于复杂,以至于未来的程序员无法理解.没有必要尝试捕获类型系统中程序行为的所有可能限制.正如我们刚刚看到的那样,当你尝试时,类型系统有时会执行你不打算表达的限制.
| 归档时间: |
|
| 查看次数: |
429 次 |
| 最近记录: |