假设我有一个服务接口(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);

我们必须确定两个事实:什么是 AR ?我们需要继续什么信息? new Bar() 对应于 As=>s.Foo 对应于 Func<I, Func<A, R>>

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

现在的问题是:我们可以通过对 lambda 主体中的 R 进行重载解析来推断 ints.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);

现在我们有什么信息?我们像以前一样推断 ABar。现在,我们必须知道R映射到i.Foo,才能推断出Func<Bar, R>是什么。这是一个简单的重载解析问题;我们假装调用了 i.Foo(Bar) 并让重载解析来完成它的工作。重载解析返回并说 i.Foo(Bar) 返回 int ,所以 Rint

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

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

关于c# - 为什么在这段代码中类型推断不起作用?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10236479/

10-11 02:13