如何将 Func<T1,T2> 转换或转换为 Func<T1,int>

How to convert or cast a Func<T1,T2> to a Func<T1,int>

我有一个

Expression<Func<T1, T2>> source

所以我可以毫无问题地将它编译成 Func<T1, T2>:

Func<T1, T2> result = source.Compile();

但现在我有一个特殊情况,其中(给定 T2 是一个整数)我必须 return 一个 Func<T1, int>。当然我不能只投,但我也找不到其他的方法来转换它。

public Func<T1, int> GetFuncToReturnId()
{
    if (source.ReturnType != typeof(int))
    {
        throw new InvalidOperationException("source must be of return type must be of type int");
    }

    return // ??? please help !!!
}

我尝试将 Expression 的部分或已编译的 Func 添加到 Func<T1, int> 的构造函数中,但这没有帮助。我能做什么

您可以使用 Convert.ChangeType

来实现
var compiled = source.Compile();

return (T1 x) => (int) Convert.ChangeType(compiled(x), typeof(int));

或者简单地施法两次

var compiled = source.Compile();
return (T1 x) => (int) (object) compiled(x);

从你的问题中不清楚你遇到的问题到底是什么,但我可以猜测是将 Func<T1, T2> 转换为 Func<T1, int> 会产生无效的转换错误。

这是无效的原因是因为 C# 对于从类型参数到任何其他类型的任何转换是保守的。假设您有一个从 Foo 到 Bar 的用户定义转换。如果你有方法

Bar M<T>(T t) { return (Bar)t; }

那么您可能会合理地期望 M<Foo>(new Foo()) 会将用户定义的转换调用到 Bar。但 C# 泛型不是模板,不会为每个泛型实例化重新生成代码。这种转换仅在存在 identityreference 转换时才有效,C# 可防止您犯这种常见错误。

此外,任何时候您对泛型进行类型测试时,它不再是"generic"。通用代码应该以相同的方式工作 ,而不管类型参数如何 ,这就是它被称为 "generic code" 的原因。听起来你正在做的事情违背了泛型的目的。

就是说,如果您执意要这样做,有几种方法可以像这样在泛型类型之间进行引用转换:

class C<T1, T2>
{
  void M(Func<T1, int> f) {}

  // This way is wrong.
  void N1(Func<T1, T2> f) 
  {
    if (f is Func<T1, int>)
      M((Func<T1, int>)f); // Error, cannot convert
  }

  // This works.
  void N2(Func<T1, T2> f) 
  {
    var fi = f as Func<T1, int>;
    if (fi != null)
      M(fi);
  }

  // This also works.
  void N3(Func<T1, T2> f) 
  {
    if (f is Func<T1, int>)
      M((Func<T1, int>)(object)f);
  }

  // This works in C# 7; it's a more concise way to combine the previous two
  void N4(Func<T1, T2> f) 
  {
    if (f is Func<T1, int> fi)
      M(fi);
  }

}