< Назад

  10 способов улучшения программ на С# с помощью LINQ

В MSDN LINQ описывается как «синтаксис запросов, определяющий набор операторов, позволяющих выражать операции обхода, фильтрации и проекции прямым, декларативным образом в любом основанном на .NET языке программирования».

Благодаря этой технологии вы можете осуществлять запросы с применением строго типизированного синтаксиса. В LINQ есть собственный набор операций запросов, делающих этот язык столь мощным. Они находятся в пространстве имен System.Linq сборки System.Core. Все это методы расширения объектов Enumerable и Queryable. В табл. 1 приведен список стандартных операторов LINQ, сгруппированных по категориям.

 

Табл. 1
Квантификаторы
Any, All, Contains
Операторы множеств

Distinct, Union, Intersect, Except

Операторы упорядочивания
OrderBy,

 OrderByDescending,

 ThenBy,

 ThenByDescending

Операторы равенства
 

SequenceEqual

Операторы преобразования

Cast, ToArray. ToDictionary,

 

 OfType, SingleOrDefault

Операторы группировки
GroupBy
Операторы объединения
Join, GroupJoin, ToList
Операторы элементов

ElementAt, First, Last,

 

 FirstOrDefault,

 

 LastOrDefault, Single

Операторы секционирования

Skip, SkipWhile, Take,

 

TakeWhile

Операторы проекции
Select, SelectMany
Операторы агрегирования
Count, Max, Min, Sum
Операторы ограничения
Where
Операторы конкатенации
Concat
Операторы генерации
Range, Repeat

 

Можно найти достаточно много сайтов, на которых приводятся описания этих операторов и примеры их использования. В первую очередь это сайт rnsdn. microsoft.com, www.gotdotnet.ru, www.sql.ru. В этой ста­тье мы сфокусируемся лишь на тех LINQ-запросах, которые могут улучшить наши программы на С#.
LINQ делает очевидным назначение кода и помогает коллегам-разработчикам быстро вникать в нюансы кода. Несомненно, это шаг вперед после документирования кода.

 

1. Избавляемся от foreach для коллекций при фильтрации данных
Можно избежать применения foreach и выражений if в каждом foreach для фильтрации данных. В приве­денном ниже примере видно, как более простой код LINQ помогает избавиться от этих элементов.
Классический код
Users filteredUsers = new UsersQ;
foreach (User currentUser in Users)
{
If(currentUser.Active && currentUser.Enabled)
{
If(!currentUser.Loggedln)
      filteredUsers.Add(currentUser);

 }

}

 

Код LINQ

var filteredUsers = from currentUser in Users

where currentUser.Active &&

     currentUser.AllowLogin&& ! currentUser.Loggedln

     select user;

2. Выбор X строк
Когда нужно выбрать несколько строк из коллекции, мы проверяем счетчик цикла с константой и прерываем.
Классический код

Users filteredUsers = new Users();

for(int counter=0; counter < Users.Count; counter++)

{

If(Users[counter].Active && Users[counter].

Enabled)

{

If(!Users[counter].Loggedln)

filteredUsers.Add(Users[counter]);}

If (filteredUsers.Count == 10) break;

 }

Код LINQ

var filteredUsers = Users.Where(

u => u.Active && u.AllowLogin && !u.Loggedln).Take(10)

3. Получение первого элемента коллекции
Очень часто возникает необходимость выбора толь­ко первого элемента коллекции. Сначала мы смот­рим, не равна ли коллекция null, а затем смотрим на количество ее элементов — ведь коллекция может не содержать ни одного элемента. И наш классический код будет выглядеть так
List<string> adminUsers =
RetrieveUsersForAdminRole(RoleType.Admin);
User firstUser = null;
If (adminUsers != null) && (adminUsers.Count > 0)
{
firstUser = adminUsers[0];
}
Это можно заменить простым запросом LINQ:
List<string> adminUsers =
RetrieveUsersForAdminRole(RoleType.Admin);
User firstUser = adminUsers.FirstOrDefault();
Если пользователей не найдено, будет возвращено значение по умолчанию лежащего в основе типа.
4. IComparer<T> для сортировки
Вам больше не нужно писать сложные классы IComparer, чтобы сортировать данные. Теперь для упорядочения можно использовать метод OrderBy. Приведенный ниже пример запроса отличается краткостью:
var filteredUsers = Users.Where(u => u.Active && u.AllowLogin && !u.Loggedln) .OrderBy( u => u.Name);

 Функция OrderBy принимает параметр, используемый для сортировки. Если нужна множественная сортировка, нужно применить оператор ThenBy.

 var filteredUsers = Users.Where(u => u.Active && u.AllowLogin && !u.Loggedln) .OrderBy( u => u.Name).ThenBy(u => u.Location);

 Сортировать можно и в порядке убывания. В LINQ для этого есть оператор OrderByDescending.

var filteredUsers = Users.Where(u => u.Active && u.AllowLogin && !u.Loggedln) .OrderByDescending( u => u.Name) .ThenBy(u => u.Location);

5. Не используйте циклы для инициализации массивов
Не следует использовать циклы для инициализации массивов. Все мы делали это, чтобы написать быстрый код для тестирования. Теперь это можно заменить методом System.Linq.Enuemerable.Range.

 

int[] monthsInAYear = new int[12];

for (int counter = 0; counter < monthsInAYear.Length; counter++)

     {

    monthsInAYear[counter] = counter + 1;

  }

В LINQ это займет одну строку кода:

int[] monthsInAYearByLINQ = System.Linq.Enumerable.Ranged, 12).ToArray();

6. Замена двух аналогичных циклов конкатенацией

Если вам нужно в коде пройти по двум массивам, аналогичным по своей природе, можете использовать метод расширения Concat, чтобы написать лаконич­ный код. Рассмотрим пример:
int[] firstArray =
System.Linq.Enumerable. Range(1, 12).ToArray(); int[] secondArray =
System. Linq. Enumerable. Range(13, 12).ToArrayO;

  foreach (var fa in firstArray)

      {

allElements.AppendLine(string.Format("{0}", fa));

}

foreach (var sa in secondArray)

      {

    allElements.Append Line(string.Format("{0}", sa));

 }

А вот код, в котором задействован оператор Concat:
int[] firstArray =
System. Linq. Enumerable. Range(1, 12).ToArrayO; int[] secondArray =
System. Linq. Enumerable. Range(13, 12).ToArrayO;

foreach (var a in firstArray.Concat(secondArray))

{

    allElements.AppendLine(string.Format("{0}", a));

}

7. Избегайте преобразований
Преобразование — это методика, используемая для возвращения новой коллекции, получаемой за счет прохождения в цикле по другой коллекции и применения фильтра. Возьмем для примера текст, приведенный ниже. Здесь после прохождения в цикле по коллекции создается массив.
public Users[] FindUsers(RoleType r)
{
IEnumerable<Users> users = FindUsersQ;

 List<Users> filteredUsers = new List<Users>();

foreach (User u in users)

{

if(u.Role == r)

{

filteredUsers.Add(new User {

FirstName = u.FirstName,

...

}); }

 }

return filteredUsers.ToArray();

 }

Вместо того чтобы создавать временный список и затем заполнять его значениями, можно воспользоваться методами LINQ Select и ToArray для возврата результатов.
public Users[] FindUsers(RoleType r)

 {

return FindUsersQ .Select(user => new User {

FirstName = u.FirstName,

...

});

.Where(user => user.Role == r)

 .ToArray();

}

8. Ключевое слово let

Ключевое слово let позволяет создавать временные переменные в запросе LINQ, благодаря чему ваш Select становится проще читать и изменять. Вы можете предварительно определять переменные на опре­деленном этапе запроса. Эти переменные затем могут быть использованы в остальной части запроса.

Например, следующий запрос вычисляет среднее значение дважды. Второе вычисление можно не

производить, если применить оператор let. Благодаря сокращению объема вычислений запрос будет работать быстрее.

var results= from store in Stores
where store.Sales.Average(s => s.Price) > 500 select new {
Name = store.Name, StoreAveragePrice =
store.Sales.Average(s => s.Price) };

 Вот этот же фрагмент, использующий let:

var results= from store in Stores let AveragePrice =

store.Sales.Average(s => s.Price) where AveragePrice > 500

 select new {

Name = store.Name,

 StoreAveragePrice = AveragePrice

};

9. RegEx + LINQ?

Запросы LINQ могут упростить работу, если вы при­меняете регулярные выражения. Посмотрите на сле­дующий пример, в котором мы проходим по коллек­ции с использованием регулярного выражения.
Обратите внимание на то, что с отобранными при помощи RegEx результатами можно выполнять другие запросы LINQ.
List<string> examinerStatements = new List<string>();
examinerStatements.Add("Mark was present.");
examinerStatements.Add("Julie was present.");
examinerStatements.Add("John was absent");
System.Text.RegularExpressions.Regex myRegEx = new System.Text.RegularExpressions.Regex("present");
var presentStudents = examinerStatements .Where<string>(statement =>
myRegEx.IsMatch(statement)).ToList<string>();

 foreach (var examinerStatement in presentStudents)

{

        //

      }

10. Запрос к ArrayList

Наверное, вы знаете, что запросы LINQ нельзя осуществлять со списками ArrayList. Дело в том, что они

не реализовывают интерфейс System.Collections. Generic.IEnumerable<T>.

List<string ArrayList myList = new ArrayList();

 myList.Add("One");

 myList.Add("Two");

 myList.Add("Three");

var results = from value in myList

select new { value };

 В таких случаях можно использовать оператор приведения типов.

            Вот как можно изменить приведенный код:

            ArrayList myList = new ArrayList();

        myList.Add("One");

        myList.Add("Two");

        myList.Add("Three");

var results = from value in myList.Cast<string>()
    select new { value };

 

Надеюсь, эта статья научила вас чему-то новому, и десять примеров окажутся полезным в вашей дальнейшей работе.

Назад