[英]Which Extension Method is called
我看到了一个像 DBSet 这样的类型,它可以同时实现 IQueryable 和 IEnumerable,如下所示:
public class DbSet<TEntity> : DbQuery<TEntity>, IDbSet<TEntity>, IQueryable<TEntity>, IEnumerable<TEntity>, IEnumerable, IQueryable, ...
很明显IQueryable继承了IEnumerable,并且在System.Linq命名空间中,Enumerable静态类和IQueryable静态类都为IQueryable和IEnumerable定义了一些扩展方法操作符,比如First()、Select(),
我想知道在一些调用中
DBSet<Student> studs = dbContext.Students;
var stu = studs.First();
显然是public static TSource First<TSource>(this IQueryable<TSource> source);
调用 IQueryable 静态类中的方法。
从 C# 规范(7.6.5.2),它说:
如果在给定的命名空间或编译单元中使用命名空间指令导入的命名空间直接包含具有合格扩展方法 Mj 的非泛型类型声明 Ci,则这些扩展方法的集合是候选集。
但是在我的例子中,System.Linq 命名空间中的 Enumerable 和 IQueryable 类,我想知道在这种情况下public static T First<T>(this IQueryable<T> source)
是如何决定和调用的?
第First
是extension method
,我们在IEnumarable
和IQueryable
中IEnumarable
。
那么,问题是当我们实现所有这些接口时,会调用哪一个?
答案是:来自最派生接口的扩展方法,在这种情况下IQueryable
是继承自IEnumarable
的最派生接口。 所以它的First
扩展方法将被调用。
万物皆平等。 您的问题归结为标准的重载解析规则。
注意:阅读语言规范时很重要,不要过早结束或阅读太远。 在推广时阅读任何相关的子主题也很重要。
既然你在规范中,让我们通过它们来工作。
给定的
public abstract class DbSet<TEntity> : IQueryable<TEntity>, IAsyncEnumerable<TEntity>, IInfrastructure<IServiceProvider>, IListSource
public interface IQueryable : IEnumerable
扩展方法
namespace System.Linq
{
public static TSource First<TSource>(this IQueryable<TSource> source)
...
}
namespace System.Linq
{
public static TSource First<TSource>(this IEnumerable<TSource> source)
...
}
你的榜样
DBSet<Student> studs = dbContext.Students;
var stu = studs.First();
您应该引用的规范部分是
强调我的
7.6.5.2 扩展方法调用
...
- 从最接近的封闭命名空间声明开始,继续每个封闭命名空间声明,并以包含编译单元结束,连续尝试找到一组候选扩展方法:
- 如果给定的命名空间或编译单元直接包含具有合格扩展方法 Mj 的非泛型类型声明 Ci,那么这些扩展方法的集合就是候选集。
- 如果在给定的命名空间或编译单元中使用命名空间指令导入的命名空间直接包含具有合格扩展方法 Mj 的非泛型类型声明 Ci,则这些扩展方法的集合是候选集。
- 如果在任何封闭的命名空间声明或编译单元中找不到候选集,则会发生编译时错误。
- 否则,如 (§7.5.3) 中所述,将重载决议应用于候选集。
这考虑到一般标准的重载解析领域。
强调我的
7.5.3 过载解决
重载解析是一种绑定时间机制,用于在给定参数列表和一组候选函数成员的情况下选择要调用的最佳函数成员。 重载解析选择要在 C# 中的以下不同上下文中调用的函数成员:
...
- 给定一组适用的候选函数成员,该集合中的最佳函数成员被定位。 如果集合只包含一个函数成员,那么该函数成员就是最好的函数成员。 否则,如果使用第 7.5.3.2 节中的规则将每个函数成员与所有其他函数成员进行比较,则最佳函数成员是在给定参数列表方面优于所有其他函数成员的一个函数成员。 如果没有一个函数成员比所有其他函数成员都好,则函数成员调用是不明确的,并且会发生绑定时错误。
这导致我们
强调我的
7.5.3.2 更好的函数成员
为了确定更好的函数成员,按照参数表达式在原始参数列表中出现的顺序,构造了一个精简的参数列表 A,它只包含参数表达式本身。
...
• 否则,如果 MP 具有比 MQ 更具体的参数类型,则 MP 优于 MQ
缺点是
DbSet
实际上实现了IQueryable
而不是IEnumerable
这使得IQueryable
方法(具有确切的类型)更好地匹配
你可以自己测试一下
给定的
public static class LobExtensions
{
public static void Test1(this ILob asd) { }
}
public static class BobExtensions
{
public static void Test1(this IBob asd) { }
}
public interface ILob { }
public interface IBob : ILob { }
public class Bob : IBob { }
用法
var asd = new Bob();
asd.Test1();
结果是BobExtensions.Test1
将是最佳匹配和选择的方法
在这种情况下,将应用正常的方法重载决议。 这也在您发布的规范的同一部分中说明:
否则,重载决议将应用于候选集,如(第 7.5.3 节)中所述。 如果找不到单一的最佳方法,则会发生编译时错误。
总结过载结果的结果,这意味着在您的情况下,
public static T First<T>(this IQuerable<T> source)
被青睐
public static TFirst<T>(this IEnumerable<T> source) {
因为IQuerable<T>
继承自IEnumerable<T>
如果不是,则会在编译时导致歧义错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.