Причины указания общих типов в методах расширения LINQ
Просто из любопытства:
Многие методы расширения LINQ существуют как как общие, так и не общие варианты, например Any
и Any<>
, Where
и Where<>
и т.д. Написав мои запросы, я обычно использую не общие варианты, и он работает хорошо.
В каких случаях следует использовать общие методы?
--- изменить ---
P.S.: Я знаю, что вызываются только общие методы, и компилятор пытается разрешить содержимое общих скобок <>
во время компиляции.
Мой вопрос в том, каковы случаи, тогда нужно явно указать тип и не полагаться на интуицию компилятора?
Ответы
Ответ 1
Один из примеров, с которым я столкнулся сегодня:
ObjectSet<User> users = context.Users;
var usersThatMatch = criteria.Aggregate(users, (u, c) => u.Where(c));
Вышеприведенный код не будет работать, потому что метод .Where не возвращает ObjectSet<User>
. Вы можете обойти это одним из двух способов. Я мог бы называть .AsQueryable()
для пользователей, чтобы убедиться, что он строго типизирован как IQueryable, или я могу передать конкретные аргументы типа в метод Aggregate:
criteria.Aggregate<Func<User, bool>, IEnumerable<User>>(
PersonSet, (u, c) => u.Where(c));
Еще несколько более распространенных примеров - это методы Cast
и OfType
, которые не имеют никакого способа сделать вывод о том, какой тип вы хотите, и во многих случаях вызывается в общей коллекции не в общих чертах.
В общем, люди, которые разработали методы LINQ, ушли с пути, чтобы избежать необходимости использования явных типов в этих общих методах и по большей части вам не нужно. Я бы сказал, что лучше всего это знать, но не делайте этого, если не найдете нужным.
Ответ 2
Всегда. Компилятор С# достаточно умен, чтобы определить, какой тип метода основан на параметрах. Это важно, когда тип анонимный и, следовательно, не имеет имени.
obj.SomeMethod(123); //these calls are the same
obj.SomeMethod<int>(123);
obj.SomeMethod(new { foo = 123 }); //what type would I write here?!
Изменить: Чтобы быть ясным, вы всегда вызываете общий метод. Он просто выглядит как универсальный метод, поскольку компилятор и Intellisense являются умными.
Изменить: к вашему обновленному вопросу вы хотели бы быть конкретным, если хотите использовать тип, который не является типом объекта, который вы передаете. Существует два таких случая:
-
Если параметр реализует интерфейс и вы хотите работать на этом интерфейсе, а не конкретный тип, тогда вы должны указать интерфейс:
obj.DoSomething<IEnumerable<Foo>>( new List<Foo>() );
-
Если параметр неявно конвертируется в другой тип, и вы хотите использовать второй тип, вы должны указать его:
obj.DoSomethingElse<long> ( 123 ); //123 is actually an int, but convertible to long
С другой стороны, если вам нужно сделать трансляцию (или вы вставляете ее в любом случае), вам не нужно указывать:
obj.DoYetAnotherThing( (Transformed)new MyThing() ); // calls DoYetAnotherThing<Transformed>