我碰到类型系统的一个非常烦人的极端情况。
我已将代码减少到最低要求以显示问题所在。
using System;
// Some interface or Base class, doesn't matter
public interface IFace {}
// Some class that implements/extends it
public class Implements: IFace {}
public static class Foo {
public static void Bar<T, T1>(Func<T> f) where T1: IFace {
Console.WriteLine("Bar relaxed");
var _ = f();
}
public static void Bar<T1, T2>(Action f)
where T1: IFace
where T2: IFace
{
Console.WriteLine("Bar strict");
f();
}
public static void Main() {
try {
Bar<Implements, Implements>(() => { // Should call Bar strict
var _ = new Implements();
});
Bar<Implements, Implements>(() => { // Should still call Bar strict
var _ = new Implements();
throw new NullReferenceException(); // But having unconditional throw in the method
// Makes it a `Func<T>` instead of a `Action`
});
} catch(Exception _) {}
}
}
我想要的输出是
Bar strict
Bar strict
我得到的输出是
Bar strict
Bar relaxed
回复:https://repl.it/repls/WearyImpoliteExponent
可以解决吗? (不删除第一个Bar,或更改通用参数的数量)
在实际代码中,Bar
方法都不返回void,它们返回引用通用参数的内容,并且它们的主体也不同
编辑:为澄清起见,“真实世界” Bar
方法看起来更像这样:
public static Baz<T, IFace> Bar<T, T1>(Func<T> f) where T1: IFace;
public static Baz<Default, IFace> Bar<T1, T2>(Action f)
where T1: IFace
where T2: IFace;
// Where `Default` is a concrete type
struct Default {}
是的,您可以这样做。技巧是在引发的异常之后添加return;
。
Foo.Bar<Implements, Implements>(() =>
{ // Should still call Bar strict
var _ = new Implements();
throw new NullReferenceException();
return;
}
lambda现在将正确解析为Action
而不是Func
。