在解析重载时,C#抛出内部推断操作使其成为Func
我碰到了一个非常恼人的类型系统 我已经将代码减少到最低要求,以显示问题的情况在解析重载时,C#抛出内部推断操作使其成为Func,c#,generics,exception,overloading,type-inference,C#,Generics,Exception,Overloading,Type Inference,我碰到了一个非常恼人的类型系统 我已经将代码减少到最低要求,以显示问题的情况 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 voi
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 relaxed
答复:
这个问题能解决吗?(不删除第一个栏,也不更改常规参数的数量)
在实际代码中,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 {}
public static Baz Bar(Func f),其中T1:IFace;
公共静态Baz栏(动作f)
其中T1:IFace
式中T2:IFace;
//其中,“Default”是一个具体类型
结构默认值{}
“真实世界”代码:是的,您可以这样做。诀窍是添加一个
返回在引发异常后执行代码>
Foo.Bar<Implements, Implements>(() =>
{ // Should still call Bar strict
var _ = new Implements();
throw new NullReferenceException();
return;
}
Foo.Bar(()=>
{//仍应称为Bar strict
var=新实现();
抛出新的NullReferenceException();
返回;
}
lambda现在将正确解析为操作
,而不是函数
发生这种情况的原因是重载解析的工作原理非常奇怪。从本质上讲,如果lambda表达式可以被视为Func
,那么它总是比操作更可取。只要所有代码路径都返回与您期望的返回类型兼容的内容,那么Func
重载将被忽略被选中。由于返回值为零,因此满足此条件,并使用了Func
重载。您可以将参数强制转换为操作
Bar<Implements, Implements>((Action)(() =>
{ // Should still call Bar strict
var _ = new Implements();
throw new NullReferenceException();
}));
Bar((动作)(()=>
{//仍应称为Bar strict
var=新实现();
抛出新的NullReferenceException();
}));
为什么需要一个Func
?什么是T
,是否有任何限制?可能没有抓住现实世界问题的关键,但你不能将参数强制转换为操作
?我将尝试在编辑中澄清……我不想让调用者承担消除歧义的责任。但这一解释让我感到困惑神经元特异性烯醇化酶
Bar<Implements, Implements>((Action)(() =>
{ // Should still call Bar strict
var _ = new Implements();
throw new NullReferenceException();
}));