Причины указания общих типов в методах расширения 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>