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“哦,方法组中只有一个方法,所以让我们跳过重载解析,让该方法自动获胜。”我想通过