在解析重载时,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();
        }));