Reputation: 1270
I saw a type like DBSet, it may implement both IQueryable and IEnumerable like below:
public class DbSet<TEntity> : DbQuery<TEntity>, IDbSet<TEntity>, IQueryable<TEntity>, IEnumerable<TEntity>, IEnumerable, IQueryable, ...
And obviously IQueryable inherits IEnumerable, And in System.Linq namespace, both Enumerable static class and IQueryable static class defined some extension method operators like First(), Select() for both IQueryable and IEnumerable,
I wonder in some invocation like
DBSet<Student> studs = dbContext.Students;
var stu = studs.First();
It is obviously that public static TSource First<TSource>(this IQueryable<TSource> source);
method in IQueryable static class is get called.
From C# specification(7.6.5.2), it said:
If namespaces imported by using namespace directives in the given namespace or compilation unit directly contain non-generic type declarations Ci with eligible extension methods Mj, then the set of those extension methods is the candidate set.
But in my case, Enumerable and IQueryable class both in System.Linq namespace, I wonder in this situation how is public static T First<T>(this IQueryable<T> source)
is being decided and called ?
Upvotes: 5
Views: 161
Reputation: 13652
In that case, normal method overload resolution is applied. This is also stated in the same section of the spec that you have posted:
Otherwise, overload resolution is applied to the candidate set as described in (§7.5.3). If no single best method is found, a compile-time error occurs.
To summarize the result of the overload resolution, this means that in your case,
public static T First<T>(this IQueryable<T> source)
is favored over
public static T First<T>(this IEnumerable<T> source) {
since IQueryable<T>
inherits from IEnumerable<T>
If it wouldn't, it would cause an ambiguity error during compile time.
Upvotes: 0
Reputation: 1398
The First
is an extension method
, which we have in IEnumerable
and in IQueryable
.
So, the question is which one is going to get call when we implement all these interfaces?
The answer is: the Extension method from the most derived interface, which in this case IQueryable
is the most derived interface which inherits from IEnumerable
. So it's First
extension method is going to get called.
Upvotes: 3
Reputation: 81513
All things being being equal. Your question boils down to standard overload resolution rules.
Note : It's important when reading language specifications, that you don't end too soon or read too far. It's also important to read any relevant sub topics when promoted.
Since you are in the specs, let's work our way through them.
Given
public abstract class DbSet<TEntity> : IQueryable<TEntity>, IAsyncEnumerable<TEntity>, IInfrastructure<IServiceProvider>, IListSource
public interface IQueryable : IEnumerable
Extension methods
namespace System.Linq
{
public static TSource First<TSource>(this IQueryable<TSource> source)
...
}
namespace System.Linq
{
public static TSource First<TSource>(this IEnumerable<TSource> source)
...
}
Your example
DBSet<Student> studs = dbContext.Students;
var stu = studs.First();
The part of the specs you should be quoting is
Emphasis mine
7.6.5.2 Extension method invocations
...
- Starting with the closest enclosing namespace declaration, continuing with each enclosing namespace declaration, and ending with the containing compilation unit, successive attempts are made to find a candidate set of extension methods:
- If the given namespace or compilation unit directly contains non-generic type declarations Ci with eligible extension methods Mj, then the set of those extension methods is the candidate set.
- If namespaces imported by using namespace directives in the given namespace or compilation unit directly contain non-generic type declarations Ci with eligible extension methods Mj, then the set of those extension methods is the candidate set.
- If no candidate set is found in any enclosing namespace declaration or compilation unit, a compile-time error occurs.
- Otherwise, overload resolution is applied to the candidate set as described in (§7.5.3).
Which takes into general standard overload resolution territory.
Emphasis mine
7.5.3 Overload resolution
Overload resolution is a binding-time mechanism for selecting the best function member to invoke given an argument list and a set of candidate function members. Overload resolution selects the function member to invoke in the following distinct contexts within C#:
...
- Given the set of applicable candidate function members, the best function member in that set is located. If the set contains only one function member, then that function member is the best function member. Otherwise, the best function member is the one function member that is better than all other function members with respect to the given argument list, provided that each function member is compared to all other function members using the rules in §7.5.3.2. If there is not exactly one function member that is better than all other function members, then the function member invocation is ambiguous and a binding-time error occurs.
Which leads us to
Emphasis mine
7.5.3.2 Better function member
For the purposes of determining the better function member, a stripped-down argument list A is constructed containing just the argument expressions themselves in the order they appear in the original argument list.
...
• Otherwise, if MP has more specific parameter types than MQ, then MP is better than MQ
The short of this is
DbSet
actually implements IQueryable
not IEnumerable
This makes the IQueryable
method (with the exact type) the better match
You can test it for your self
Given
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 { }
Usage
var asd = new Bob();
asd.Test1();
Result being BobExtensions.Test1
will be the best match and the chosen method
Upvotes: 2