加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 为什么不在此代码中使用类型推断?

发布时间:2020-12-15 07:59:21 所属栏目:百科 来源:网络整理
导读:假设我有一个如下所示的服务接口: public interface IFooService{ FooResponse Foo(FooRequest request);} 在调用类似服务的方法时,我想解决一些贯穿各领域的问题;例如,我想要统一的请求记录,性能记录和错误处理.我的方法是使用一个带有Invoke方法的公共基
假设我有一个如下所示的服务接口:
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;
    }
}

我得到这个编译错误:

The type arguments for method … cannot be inferred from the usage. Try specifying the type arguments explicitly.

显然,我可以通过改变我的调用来修复它:

var response = this.Invoke<FooRequest,FooResponse>(svc => svc.Foo,request);

但我想避免这种情况.有没有办法重做代码,以便我可以利用类型推断?

编辑:

我还应该提一下,早期的方法是使用扩展方法;这种工作的类型推断:

public static class ServiceExtensions
{
    public static TResponse Invoke<TRequest,TResponse>(
        this IService service,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是什么?我们还有什么信息可以继续?新的Bar()对应于A并且s => s.Foo对应于Func< I,Func< A,R>>.

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

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

可悲的是,答案是否定的.你和我可以做那个推理,但编译器没有.当我们设计类型推理算法时,我们考虑添加这种“多级”lambda /委托/方法组推理,但认为它的成本太高,无法给予它带来的好处.

对不起,你在这里运气不好;一般而言,在C#方法类型推断中不会进行需要“挖掘”多个功能抽象级别的推理.

Why did this work when using extension methods,then?

因为扩展方法没有多个级别的功能抽象.扩展方法案例是:

class C
{
    public static R M<A,R>(I i,A 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中添加了这种推断.

另请注意,要使此类推断成功,必须已推断出所有参数类型.我们必须只推断返回类型,因为为了知道返回类型,我们必须能够进行重载解析,并且要做重载解析,我们必须知道所有的参数类型.我们不做任何废话,比如“哦,方法组中只有一个方法,所以让我们跳过重载决策,让这个方法自动获胜”.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读