在EF Core中使用“包含”(或“然后包含”)时,为什么不需要指定类型?

时间:2019-11-06 19:57:19

标签: c#

在查看EF Core的源代码时,我看到一个声明如下的Include函数:

public static IIncludableQueryable<TEntity, TProperty> Include<TEntity, TProperty>(
  [NotNull] this IQueryable<TEntity> source,
  [NotNull] Expression<Func<TEntity, TProperty>> navigationPropertyPath)
  where TEntity : class
{ }

另外一个像这样的人:

public static IQueryable<TEntity> Include<TEntity>(
  [NotNull] this IQueryable<TEntity> source,
  [NotNull, NotParameterized] string navigationPropertyPath)
  where TEntity : class
{ )

但是,当实际调用这些函数时,我们可以不指定类型而直接调用它们,为什么?

_context.Users.Include( u => u.PropertyFromAnotherTable)
              .Where( u => u.Age > 25 )
              .ToListAsync( cancellationToken );

1 个答案:

答案 0 :(得分:3)

因为类型是从上下文中推断的,也就是说,编译器发现您给的参数明确地定义了泛型类型参数。

您可以轻松地看到一个简单的版本:

    public static void TestGeneric<T>(T myParam) { }

    public static void Example()
    {
        string testString = null;
        int testInt = 0;

        TestGeneric(testString);
        TestGeneric(testInt);
    }

将鼠标悬停在Visual Studio中的两个“ TestGeneric”调用上:第一个签名为TestGeneric<string>(string),第二个签名为TestGeneric<int>(int)

请注意,类型是在编译时推断出来的(因此,泛型版本仍然是严格且精确的类型)

C#语言的此功能有很多细微之处和局限性。我现在没有所有详细信息在内存中(请参阅下面的进一步阅读部分)。

例如,如果您使用不同的子类,则类型参数的协方差和协方差等。在某些情况下,您可能会遇到歧义。编译器会抱怨,在这种情况下,您仍然需要显式提供类型参数。

我对您的情况下C#机制的猜测:

(不确定,请随时发表评论!)

_context.Users.Include( u => u.PropertyFromAnotherTable)
     .Where( u => u.Age > 25 )
     .ToListAsync( cancellationToken );

首先,由于扩展方法的第一个参数,可以很容易地推断出TEntity的{​​{1}}类型:

Include<TEntity, TProperty>

由于您在this IQueryable<TEntity> source, 上调用它,因此我假设它类似于_context.Users,因此可以将其隐式转换为DbSet<User>,因此IQueryable<User>可以是{{1} }。

然后,得出TEntity的类型应为User。因此u => u.PropertyFromAnotherTable属于User类型,并且由于它具有名为Expression<Func<User, TProperty>>的唯一属性,因此C#也可以推断u的类型。

进一步阅读:

这里有更多信息,在众多博客中仅引用了一个博客:http://joelabrahamsson.com/a-neat-little-type-inference-trick-with-c/

它提到了乔恩·斯凯特(Jon Skeet)的“ C# In Depth”这本书,如果您对我之前提到的细节以及语言的发展感兴趣,我建议您这样做。

此外,在此问答中,一些有趣的答案和Eric Lippert的博客链接也值得一读,以供参考:Why doesn't C# infer my generic types?