假设我有一个服务接口(interface),如下所示:
public interface IFooService
{
FooResponse Foo(FooRequest request);
}
在此类服务上调用方法时,我想解决一些跨领域的问题。例如,我想要统一的请求日志记录,性能日志记录和错误处理。我的方法是使用
Invoke
方法创建一个通用的“Repository”基类,该基类负责调用该方法并对其进行其他处理。我的基类如下所示: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;
}
}
我收到此编译错误:
显然,可以通过将调用更改为以下内容来解决此问题:
var response = this.Invoke<FooRequest, FooResponse>(svc => svc.Foo, request);
但我想避免这种情况。有没有办法重新编写代码,以便我可以利用类型推断?
编辑:
我还应该提到一种较早的方法是使用扩展方法。为此工作的类型推断:
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);
}
}
最佳答案
您的问题的标题是“为什么在此代码中类型推断不起作用?”让我们简单地说一下有问题的代码。该场景是其核心:
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);
}
}
通话地点是
C.M(new Bar(), s => s.Foo);
我们必须确定两个事实:什么是
A
和 R
?我们需要继续什么信息? new Bar()
对应于 A
, s=>s.Foo
对应于 Func<I, Func<A, R>>
。很明显,我们可以根据第一个事实确定
A
必须是 Bar
。很明显,我们可以确定 s
必须是 I
。所以我们现在知道 (I s)=>s.Foo
对应于 Func<I, Func<Bar, R>>
。现在的问题是:我们可以通过对 lambda 主体中的
R
进行重载解析来推断 int
是 s.Foo
吗?可悲的是,答案是否定的。你和我可以做那个推断,但编译器不能。当我们设计类型推断算法时,我们考虑添加这种“多级”lambda/delegate/method group inference,但认为它的成本太高,无法获得它所带来的好处。
对不起,你在这里倒霉了;通常,在 C# 方法类型推断中不会进行需要“挖掘”多于一层功能抽象的推断。
因为扩展方法没有多于一层的功能抽象。扩展方法案例是:
class C
{
public static R M<A, R>(I i, A a, Func<A, R> f) { ... }
}
带有调用站点
I i = whatever;
C.M(i, new Bar(), i.Foo);
现在我们有什么信息?我们像以前一样推断
A
是 Bar
。现在,我们必须知道R
映射到i.Foo
,才能推断出Func<Bar, R>
是什么。这是一个简单的重载解析问题;我们假装调用了 i.Foo(Bar)
并让重载解析来完成它的工作。重载解析返回并说 i.Foo(Bar)
返回 int
,所以 R
是 int
。请注意,这种推理(涉及方法组)本打算添加到 C# 3 中,但我搞砸了,我们没有及时完成。我们最终将这种推理添加到 C# 4。
还要注意,要使这种推断成功, 必须已经推断出所有参数类型 。我们必须只推断返回类型,因为为了知道返回类型,我们必须能够进行重载决议,而要进行重载决议,我们必须知道所有参数类型。我们不会做任何废话,比如“哦,方法组中只有一个方法,所以让我们跳过重载解析,让该方法自动获胜”。
关于c# - 为什么在这段代码中类型推断不起作用?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10236479/