C# 为什么不是';t型推理在这段代码中工作?

C# 为什么不是';t型推理在这段代码中工作?,c#,generics,c#-4.0,type-inference,C#,Generics,C# 4.0,Type Inference,假设我有一个如下所示的服务接口: public interface IFooService { FooResponse Foo(FooRequest request); } public class RepositoryBase<TService> { private Func<TService> serviceFactory; public RepositoryBase(Func<TService> serviceFactory)

假设我有一个如下所示的服务接口:

public interface IFooService
{
    FooResponse Foo(FooRequest request);
}
public class RepositoryBase<TService>
{
    private Func<TService> serviceFactory;

    public RepositoryBase(Func<TService> serviceFactory)
    {
        this.serviceFactory = serviceFactory;
    }

    public TResponse Invoke<TRequest, TResponse>(
        Func<TService, Func<TRequest, TResponse>> methodExpr,
        TRequest request)
    {
        // Do cross-cutting code

        var service = this.serviceFactory();
        var method = methodExpr(service);
        return method(request);
    }
}
public class FooRepository : BaseRepository<IFooService>
{
    // ...

    public BarResponse CallFoo(...)
    {
        FooRequest request = ...;
        var response = this.Invoke(svc => svc.Foo, request);
        return response;
    }
}
在调用此类服务的方法时,我希望满足一些交叉关注点;例如,我需要统一的请求日志、性能日志和错误处理。我的方法是使用一个带有
Invoke
方法的公共基本“Repository”类,该方法负责调用该方法并围绕该方法执行其他操作

public interface IFooService
{
    FooResponse Foo(FooRequest request);
}
public class RepositoryBase<TService>
{
    private Func<TService> serviceFactory;

    public RepositoryBase(Func<TService> serviceFactory)
    {
        this.serviceFactory = serviceFactory;
    }

    public TResponse Invoke<TRequest, TResponse>(
        Func<TService, Func<TRequest, TResponse>> methodExpr,
        TRequest request)
    {
        // Do cross-cutting code

        var service = this.serviceFactory();
        var method = methodExpr(service);
        return method(request);
    }
}
public class FooRepository : BaseRepository<IFooService>
{
    // ...

    public BarResponse CallFoo(...)
    {
        FooRequest request = ...;
        var response = this.Invoke(svc => svc.Foo, request);
        return response;
    }
}
公共类RepositoryBase
{
私人Func服务工厂;
公共存储库(Func serviceFactory)
{
this.serviceFactory=serviceFactory;
}
公共响应调用(
Func methodExpr,
TRequest请求)
{
//做横切代码
var service=this.serviceFactory();
var方法=methodExpr(服务);
返回方法(请求);
}
}
这很好。但是,我使代码更干净的整个目标因类型推断没有按预期工作而受阻。例如,如果我编写这样的方法:

public interface IFooService
{
    FooResponse Foo(FooRequest request);
}
public class RepositoryBase<TService>
{
    private Func<TService> serviceFactory;

    public RepositoryBase(Func<TService> serviceFactory)
    {
        this.serviceFactory = serviceFactory;
    }

    public TResponse Invoke<TRequest, TResponse>(
        Func<TService, Func<TRequest, TResponse>> methodExpr,
        TRequest request)
    {
        // Do cross-cutting code

        var service = this.serviceFactory();
        var method = methodExpr(service);
        return method(request);
    }
}
public class FooRepository : BaseRepository<IFooService>
{
    // ...

    public BarResponse CallFoo(...)
    {
        FooRequest request = ...;
        var response = this.Invoke(svc => svc.Foo, request);
        return response;
    }
}
公共类FooRepository:BaseRepository
{
// ...
公共部门响应CallFoo(…)
{
FooRequest请求=。。。;
var response=this.Invoke(svc=>svc.Foo,request);
返回响应;
}
}
我发现这个编译错误:

无法从用法推断方法…的类型参数。请尝试显式指定类型参数

显然,我可以通过将通话改为:

var response = this.Invoke<FooRequest, FooResponse>(svc => svc.Foo, request);
var response=this.Invoke(svc=>svc.Foo,请求);
但我想避免这种情况。有没有办法重新编写代码,以便利用类型推断

编辑:

我还应该提到,早期的一种方法是使用扩展方法;对于这种方法,类型推断是有效的:

public static class ServiceExtensions
{
    public static TResponse Invoke<TRequest, TResponse>(
        this IService service, 
        Func<TRequest, TResponse> method, 
        TRequest request)
    {
        // Do other stuff
        return method(request);
    }
}

public class Foo
{
    public void SomeMethod()
    {
        IService svc = ...;
        FooRequest request = ...;
        svc.Invoke(svc.Foo, request);
    }
}
公共静态类服务扩展
{
公共静态响应调用(
这是一项色情服务,
Func方法,
TRequest请求)
{
//做其他事情
返回方法(请求);
}
}
公开课Foo
{
公共方法()
{
IService svc=。。。;
FooRequest请求=。。。;
Invoke(svc.Foo,request);
}
}

上次编辑后,我们看到
svc.Foo
是一个方法组;这解释了类型推断失败的原因。编译器需要知道类型参数,以便为方法组转换选择正确的
Foo
重载。

为什么不直接调用该方法呢?即:

public class ClassBase<TService>
{
     protected Func<TService> _serviceFactory = null;

     public ClassBase(Func<TService> serviceFactory)
     {
         _serviceFactory = serviceFactory;
     }

     public virtual TResponse Invoke<TResponse>(Func<TService, TResponse> valueFactory)
     {
          // Do Stuff

          TService service = serviceFactory();
          return valueFactory(service);
     }
}
公共类类库
{
受保护的Func\u serviceFactory=null;
公共类库(Func serviceFactory)
{
_服务工厂=服务工厂;
}
公共虚拟响应调用(Func valueFactory)
{
//做事
t服务服务=服务工厂();
返回值工厂(服务);
}
}
那么理论上你应该能够做到这一点:

public class Sample : ClassBase<SomeService>
{
     public Bar CallFoo()
     {
          FooRequest request = ...
          var response = Invoke(svc => svc.Foo(request));
          return new Bar(response);
     }
}
公共类示例:ClassBase
{
公共酒吧CallFoo()
{
FooRequest请求=。。。
var response=Invoke(svc=>svc.Foo(request));
返回新条(响应);
}
}

您的问题的标题是“为什么类型推断在这段代码中不起作用?”让我们简单介绍一下所讨论的代码。场景是其核心:

class Bar { }

interface I
{
    int Foo(Bar bar);
}

class C
{
    public static R M<A, R>(A a, Func<I, Func<A, R>> f) 
    { 
        return default(R); 
    }
}
我们必须确定两个事实:什么是
A
R
?我们必须继续哪些信息?
newbar()
对应于
A
s=>s.Foo
对应于
Func

显然,我们可以根据第一个事实确定
A
必须是
Bar
,也可以确定
s
必须是
I
,所以我们现在知道
(is)=>s.Foo
对应于
Func

现在的问题是:我们可以通过对lambda主体中的
s.Foo
进行重载解析来推断
R
int

遗憾的是,答案是否定的。你和我可以进行这种推理,但编译器不能。当我们设计类型推理算法时,我们考虑添加这种“多级”lambda/delegate/method组推理,但认为其成本太高,无法带来好处

很抱歉,您在这里运气不好;一般来说,在C#method type推断中不会进行需要“挖掘”多个级别的函数抽象的推断

那么,在使用扩展方法时,为什么会起作用呢

因为扩展方法没有超过一个级别的功能抽象。扩展方法案例为:

class C
{
    public static R M<A, R>(I i, A a, Func<A, R> f) { ... }
}
现在我们有什么信息?我们推断
A
和以前一样是
Bar
。现在我们必须推断
R
知道
i.Foo
映射到
Func
。这是一个简单的重载解析问题;我们假装调用了
i.Foo(Bar)
让重载解析完成它的工作。重载解析返回并表示
i.Foo(Bar)
返回
int
,因此
R
int

请注意,这种涉及方法组的推理本打算添加到C#3中,但我搞砸了,我们没有及时完成。我们最终将这种推理添加到C#4中


还要注意,要使这种推断成功,必须已经推断出所有的参数类型。我们必须只推断返回类型,因为为了知道返回类型,我们必须能够进行重载解析,而要进行重载解析,我们必须知道所有的参数类型。我们不做任何废话ke“哦,方法组中只有一个方法,所以让我们跳过重载解析,让该方法自动获胜。”

我想通过