Igb*_*man 18 c# generics clr runtime compiler-bug
这有点啰嗦,所以这是快速版本:
为什么这会导致运行时TypeLoadException?(并且编译器是否应该阻止我这样做?)
interface I
{
void Foo<T>();
}
class C<T1>
{
public void Foo<T2>() where T2 : T1 { }
}
class D : C<System.Object>, I { }
Run Code Online (Sandbox Code Playgroud)
如果您尝试实例化D,则会发生异常.
更长,更具探索性的版本:
考虑:
interface I
{
void Foo<T>();
}
class C<T1>
{
public void Foo<T2>() where T2 : T1 { }
}
class some_other_class { }
class D : C<some_other_class>, I { } // compiler error CS0425
Run Code Online (Sandbox Code Playgroud)
这是非法的,因为类型约束C.Foo()与那些不匹配I.Foo().它会生成编译器错误CS0425.
但我想我可能会违反规则:
class D : C<System.Object>, I { } // yep, it compiles
Run Code Online (Sandbox Code Playgroud)
通过使用ObjectT2作为约束,我否定了这个约束.我可以安全地传递任何类型D.Foo<T>(),因为一切都源于Object.
即便如此,我仍然期望得到编译器错误.在C#语言意义上,它违反了"C.Foo()上的约束必须与I.Foo()上的约束相匹配的规则",我认为编译器将成为规则的坚持者.但它确实编译.似乎编译器看到了我正在做的事情,理解它是安全的,并且视而不见.
我以为我已经离开了它,但运行时说没那么快.如果我尝试创建一个实例D,我得到一个TypeLoadException:类型'D'上的'方法'C`1.Foo'试图隐式地实现具有较弱类型参数约束的接口方法."
但这不是技术错误吗?不Object用于C<T1>否定约束C.Foo(),从而使它等于 - 不强于 - I.Foo()?编译器似乎同意,但运行时没有.
为了证明我的观点,我通过D以下等式简化了它:
interface I<T1>
{
void Foo<T2>() where T2 : T1;
}
class some_other_class { }
class C : I<some_other_class> // compiler error CS0425
{
public void Foo<T>() { }
}
Run Code Online (Sandbox Code Playgroud)
但:
class C : I<Object> // compiles
{
public void Foo<T>() { }
}
Run Code Online (Sandbox Code Playgroud)
这适用于传递给任何类型的编译和运行Foo<T>().
为什么?运行时是否存在错误,或者(更有可能)是否存在我未看到此异常的原因 - 在哪种情况下编译器不应该阻止我?
有趣的是,如果通过将约束从类移动到接口来反转场景...
interface I<T1>
{
void Foo<T2>() where T2 : T1;
}
class C
{
public void Foo<T>() { }
}
class some_other_class { }
class D : C, I<some_other_class> { } // compiler error CS0425, as expected
Run Code Online (Sandbox Code Playgroud)
我再次否定了这个限制:
class D : C, I<System.Object> { } // compiles
Run Code Online (Sandbox Code Playgroud)
这次运行得很好!
D d := new D();
d.Foo<Int32>();
d.Foo<String>();
d.Foo<Enum>();
d.Foo<IAppDomainSetup>();
d.Foo<InvalidCastException>();
Run Code Online (Sandbox Code Playgroud)
一切顺利,这对我来说非常有意义.(D在等式中有或没有)
那么为什么第一种方式会破裂呢?
附录:
我忘了补充说TypeLoadException有一个简单的解决方法:
interface I
{
void Foo<T>();
}
class C<T1>
{
public void Foo<T2>() where T2 : T1 { }
}
class D : C<Object>, I
{
void I.Foo<T>()
{
Foo<T>();
}
}
Run Code Online (Sandbox Code Playgroud)
明确地实施I.Foo()是好的.只有隐式实现会导致TypeLoadException.现在我可以这样做:
I d = new D();
d.Foo<any_type_i_like>();
Run Code Online (Sandbox Code Playgroud)
但它仍然是一个特例.尝试使用除System.Object之外的任何其他内容,这将无法编译.我觉得这样做有点脏,因为我不确定它是否故意以这种方式工作.
这是一个错误 - 请参阅从泛型接口实现泛型方法会导致 TypeLoadException和使用泛型接口和具有类型参数约束的泛型方法无法验证的代码。不过,我不清楚这是 C# 错误还是 CLR 错误。
[由OP添加:]
这是微软在您链接到的第二个线程中所说的内容(我的重点):
运行时和 C# 编译器使用的算法之间存在不匹配,以确定一组约束是否与另一组约束一样强。这种不匹配会导致 C# 编译器接受运行时拒绝的某些构造,结果就是您看到的 TypeLoadException。我们正在调查以确定此代码是否是该问题的表现。无论如何,编译器接受这样导致运行时异常的代码肯定不是“按设计”的。
问候,
Ed Maurer C# 编译器开发主管
从我加粗的部分来看,我认为他说这是一个编译器错误。那是 2007 年的事了。我想这还不够严重,不足以成为他们优先解决的问题。
| 归档时间: |
|
| 查看次数: |
2345 次 |
| 最近记录: |