Поиск…


Вступление

LINQ - это аббревиатура, обозначающая L anguage IN tegrated Q uery. Это концепция, которая объединяет язык запросов, предлагая согласованную модель для работы с данными в различных источниках и форматах данных; вы используете одни и те же базовые шаблоны кодирования для запроса и преобразования данных в документы XML, базы данных SQL, наборы данных ADO.NET, коллекции .NET и любой другой формат, для которого доступен поставщик LINQ.

Синтаксис

  • Синтаксис запроса:

    • из <range variable> в <collection>
    • [from <range variable> в <collection>, ...]
    • <фильтр, объединение, группировка, агрегированные операторы, ...> <выражение lambda>
    • <select или groupBy operator> <сформулировать результат>
  • Синтаксис метода:

    • Enumerable.Aggregate (FUNC)
    • Enumerable.Aggregate (seed, func)
    • Enumerable.Aggregate (seed, func, resultSelector)
    • Enumerable.All (предикат)
    • Enumerable.Any ()
    • Enumerable.Any (предикат)
    • Enumerable.AsEnumerable ()
    • Enumerable.Average ()
    • Enumerable.Average (селектор)
    • Enumerable.Cast <результат> ()
    • Enumerable.Concat (второй)
    • Enumerable.Contains (значение)
    • Enumerable.Contains (значение, сравнение)
    • Enumerable.Count ()
    • Enumerable.Count (предикат)
    • Enumerable.DefaultIfEmpty ()
    • Enumerable.DefaultIfEmpty (DefaultValue)
    • Enumerable.Distinct ()
    • Enumerable.Distinct (Comparer)
    • Enumerable.ElementAt (индекс)
    • Enumerable.ElementAtOrDefault (индекс)
    • Enumerable.Empty ()
    • Enumerable.Except (второй)
    • Enumerable.Except (второй, сравнительный)
    • Enumerable.First ()
    • Enumerable.First (предикат)
    • Enumerable.FirstOrDefault ()
    • Enumerable.FirstOrDefault (предикат)
    • Enumerable.GroupBy (keySelector)
    • Enumerable.GroupBy (keySelector, resultSelector)
    • Enumerable.GroupBy (keySelector, elementSelector)
    • Enumerable.GroupBy (keySelector, comparer)
    • Enumerable.GroupBy (keySelector, resultSelector, comparer)
    • Enumerable.GroupBy (keySelector, elementSelector, resultSelector)
    • Enumerable.GroupBy (keySelector, elementSelector, comparer)
    • Enumerable.GroupBy (keySelector, elementSelector, resultSelector, comparer)
    • Enumerable.Intersect (второй)
    • Enumerable.Intersect (второй, сравнительный)
    • Enumerable.Join (внутренний, внешнийKeySelector, innerKeySelector, resultSelector)
    • Enumerable.Join (внутренний, внешнийKeySelector, innerKeySelector, resultSelector, comparer)
    • Enumerable.Last ()
    • Enumerable.Last (предикат)
    • Enumerable.LastOrDefault ()
    • Enumerable.LastOrDefault (предикат)
    • Enumerable.LongCount ()
    • Enumerable.LongCount (предикат)
    • Enumerable.Max ()
    • Enumerable.Max (селектор)
    • Enumerable.Min ()
    • Enumerable.Min (селектор)
    • Enumerable.OfType <TResult> ()
    • Enumerable.OrderBy (keySelector)
    • Enumerable.OrderBy (keySelector, comparer)
    • Enumerable.OrderByDescending (keySelector)
    • Enumerable.OrderByDescending (keySelector, comparer)
    • Enumerable.Range (начало, количество)
    • Enumerable.Repeat (элемент, счетчик)
    • Enumerable.Reverse ()
    • Enumerable.Select (селектор)
    • Enumerable.SelectMany (селектор)
    • Enumerable.SelectMany (collectionSelector, resultSelector)
    • Enumerable.SequenceEqual (второй)
    • Enumerable.SequenceEqual (второй, сравнительный)
    • Enumerable.Single ()
    • Enumerable.Single (предикат)
    • Enumerable.SingleOrDefault ()
    • Enumerable.SingleOrDefault (предикат)
    • Enumerable.Skip (количество)
    • Enumerable.SkipWhile (предикат)
    • Enumerable.Sum ()
    • Enumerable.Sum (селектор)
    • Enumerable.Take (количество)
    • Enumerable.TakeWhile (предикат)
    • orderedEnumerable.ThenBy (keySelector)
    • orderedEnumerable.ThenBy (keySelector, comparer)
    • orderedEnumerable.ThenByDescending (keySelector)
    • orderedEnumerable.ThenByDescending (keySelector, comparer)
    • Enumerable.ToArray ()
    • Enumerable.ToDictionary (keySelector)
    • Enumerable.ToDictionary (keySelector, elementSelector)
    • Enumerable.ToDictionary (keySelector, comparer)
    • Enumerable.ToDictionary (keySelector, elementSelector, comparer)
    • Enumerable.ToList ()
    • Enumerable.ToLookup (keySelector)
    • Enumerable.ToLookup (keySelector, elementSelector)
    • Enumerable.ToLookup (keySelector, comparer)
    • Enumerable.ToLookup (keySelector, elementSelector, comparer)
    • Enumerable.Union (второй)
    • Enumerable.Union (второй, сравнительный)
    • Enumerable.Where (предикат)
    • Enumerable.Zip (второй, resultSelector)

замечания

Чтобы использовать запросы LINQ, вам необходимо импортировать System.Linq .

Синтаксис метода более мощный и гибкий, но Синтаксис запроса может быть более простым и более знакомым. Все запросы, написанные в синтаксисе Query, транслируются в функциональный синтаксис компилятором, поэтому производительность одинакова.

Объекты запроса не оцениваются до тех пор, пока они не будут использованы, поэтому их можно изменить или добавить без штрафа за производительность.

куда

Возвращает подмножество элементов, для которых указанный предикат является истинным.

List<string> trees = new List<string>{ "Oak", "Birch", "Beech", "Elm", "Hazel", "Maple" };

Синтаксис метода

// Select all trees with name of length 3
var shortTrees = trees.Where(tree => tree.Length == 3); // Oak, Elm

Синтаксис запроса

var shortTrees = from tree in trees
                 where tree.Length == 3
                 select tree; // Oak, Elm

Выбрать - Преобразование элементов

Select позволяет применить преобразование к каждому элементу в любой структуре данных, реализующей IEnumerable.

Получение первого символа каждой строки в следующем списке:

List<String> trees = new List<String>{ "Oak", "Birch", "Beech", "Elm", "Hazel", "Maple" };

Использование регулярного (лямбда) синтаксиса

//The below select stament transforms each element in tree into its first character.
IEnumerable<String> initials = trees.Select(tree => tree.Substring(0, 1));
foreach (String initial in initials) {
    System.Console.WriteLine(initial);
}

Выход:

О
В
В
Е
ЧАС
M

Живая демонстрация на .NET скрипке

Использование LINQ Query Syntax

initials = from tree in trees
           select tree.Substring(0, 1);

Цепочные методы

Многие функции LINQ работают на IEnumerable<TSource> а также возвращают IEnumerable<TResult> . Параметры типа TSource и TResult могут или не могут ссылаться на один и тот же тип в зависимости от рассматриваемого метода и любых переданных ему функций.

Вот несколько примеров этого:

public static IEnumerable<TResult> Select<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TResult> selector
)

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, int, bool> predicate
)

public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector
)

В то время как для некоторых цепочек методов может потребоваться весь набор, который должен быть обработан до перехода, LINQ использует преимущество отложенного исполнения , используя MSDN возврата доходности, который создает перечислимый и перечислитель за кулисами. Процесс привязки в LINQ по существу создает перечислимый (итератор) для исходного набора, который откладывается, до тех пор, пока не будет осуществлено перечислимое перечисление .

Это позволяет этим функциям свободно подключаться wiki , где одна функция может действовать непосредственно на результат другого. Этот стиль кода может использоваться для выполнения многих операций, основанных на последовательностях, в одном выражении.

Например, можно комбинировать Select , Where и OrderBy для преобразования, фильтрации и сортировки последовательности в одном выражении.

var someNumbers = { 4, 3, 2, 1 };

var processed = someNumbers
        .Select(n => n * 2)   // Multiply each number by 2
        .Where(n => n != 6)   // Keep all the results, except for 6
        .OrderBy(n => n);     // Sort in ascending order

Выход:

2
4
8

Живая демонстрация на .NET скрипке

Любые функции, которые расширяют и возвращают общий тип IEnumerable<T> могут быть использованы в виде цепочечных предложений в одном выражении. Этот стиль свободного программирования является мощным, и его следует учитывать при создании собственных методов расширения .

Диапазон и повторение

Статические методы Range и Repeat для Enumerable могут использоваться для генерации простых последовательностей.

Спектр

Enumerable.Range() генерирует последовательность целых чисел, заданную начальным значением и счетчиком.

// Generate a collection containing the numbers 1-100 ([1, 2, 3, ..., 98, 99, 100])
var range = Enumerable.Range(1,100);

Живая демонстрация на .NET скрипке

Повторение

Enumerable.Repeat() генерирует последовательность повторяющихся элементов с заданным элементом и количеством требуемых повторений.

// Generate a collection containing "a", three times (["a","a","a"])
var repeatedValues = Enumerable.Repeat("a", 3);

Живая демонстрация на .NET скрипке

Пропустить и принять

Метод Skip возвращает коллекцию, исключая количество элементов из начала исходной коллекции. Количество исключенных элементов - это число, указанное в качестве аргумента. Если в коллекции меньше элементов, чем указано в аргументе, возвращается пустая коллекция.

Метод Take возвращает коллекцию, содержащую несколько элементов из начала исходной коллекции. Количество включенных элементов - это число, указанное в качестве аргумента. Если в коллекции меньше элементов, чем указано в аргументе, то возвращенная коллекция будет содержать те же элементы, что и исходная коллекция.

var values = new [] { 5, 4, 3, 2, 1 };

var skipTwo        = values.Skip(2);         // { 3, 2, 1 }
var takeThree      = values.Take(3);         // { 5, 4, 3 }
var skipOneTakeTwo = values.Skip(1).Take(2); // { 4, 3 }
var takeZero       = values.Take(0);         // An IEnumerable<int> with 0 items

Живая демонстрация на .NET скрипке

Skip и Take обычно используются вместе для получения результатов с разбивкой по страницам, например:

IEnumerable<T> GetPage<T>(IEnumerable<T> collection, int pageNumber, int resultsPerPage) {
    int startIndex = (pageNumber - 1) * resultsPerPage;
    return collection.Skip(startIndex).Take(resultsPerPage);
}

Предупреждение: LINQ to Entities поддерживает только пропущенные запросы . Если вы попытаетесь использовать Skip без заказа, вы получите NotSupportedException с сообщением «Метод« Пропустить »поддерживается только для отсортированного ввода в LINQ to Entities. Метод« OrderBy »должен быть вызван перед методом« Пропустить ».

Сначала FirstOrDefault, Last, LastOrDefault, Single и SingleOrDefault

Все шесть методов возвращают одно значение типа последовательности и могут быть вызваны с предикатом или без него.

В зависимости от количества элементов, соответствующих predicate или, если predicate не указан, количество элементов в исходной последовательности, они ведут себя следующим образом:

Первый()

  • Возвращает первый элемент последовательности или первый элемент, соответствующий предоставленному predicate .
  • Если последовательность не содержит элементов, сообщение InvalidOperationException с сообщением: «Последовательность не содержит элементов».
  • Если последовательность не содержит элементов, соответствующих предоставленному predicate , InvalidOperationException с сообщением «Последовательность не содержит соответствующего элемента».

пример

// Returns "a":
new[] { "a" }.First();

// Returns "a":
new[] { "a", "b" }.First();

// Returns "b":
new[] { "a", "b" }.First(x => x.Equals("b"));

// Returns "ba":
new[] { "ba", "be" }.First(x => x.Contains("b"));

// Throws InvalidOperationException:
new[] { "ca", "ce" }.First(x => x.Contains("b"));

// Throws InvalidOperationException:
new string[0].First();

Живая демонстрация на .NET скрипке

FirstOrDefault ()

  • Возвращает первый элемент последовательности или первый элемент, соответствующий предоставленному predicate .
  • Если последовательность не содержит элементов или не содержит элементов, соответствующих предоставленному predicate , возвращает значение по умолчанию для типа последовательности, используя default(T) по default(T) .

пример

// Returns "a":
new[] { "a" }.FirstOrDefault();

// Returns "a":
new[] { "a", "b" }.FirstOrDefault();

// Returns "b":
new[] { "a", "b" }.FirstOrDefault(x => x.Equals("b"));

// Returns "ba":
new[] { "ba", "be" }.FirstOrDefault(x => x.Contains("b"));

// Returns null:
new[] { "ca", "ce" }.FirstOrDefault(x => x.Contains("b"));

// Returns null:
new string[0].FirstOrDefault();

Живая демонстрация на .NET скрипке

Прошлой()

  • Возвращает последний элемент последовательности или последний элемент, соответствующий предоставленному predicate .
  • Если последовательность не содержит элементов, InvalidOperationException с сообщением «Последовательность не содержит элементов».
  • Если последовательность не содержит элементов, соответствующих предоставленному predicate , InvalidOperationException с сообщением «Последовательность не содержит соответствующего элемента».

пример

// Returns "a":
new[] { "a" }.Last();

// Returns "b":
new[] { "a", "b" }.Last();

// Returns "a":
new[] { "a", "b" }.Last(x => x.Equals("a"));

// Returns "be":
new[] { "ba", "be" }.Last(x => x.Contains("b"));

// Throws InvalidOperationException:
new[] { "ca", "ce" }.Last(x => x.Contains("b"));

// Throws InvalidOperationException:
new string[0].Last(); 

LastOrDefault ()

  • Возвращает последний элемент последовательности или последний элемент, соответствующий предоставленному predicate .
  • Если последовательность не содержит элементов или не содержит элементов, соответствующих предоставленному predicate , возвращает значение по умолчанию для типа последовательности, используя default(T) по default(T) .

пример

// Returns "a":
new[] { "a" }.LastOrDefault();

// Returns "b":
new[] { "a", "b" }.LastOrDefault();

// Returns "a":
new[] { "a", "b" }.LastOrDefault(x => x.Equals("a"));

 // Returns "be":
new[] { "ba", "be" }.LastOrDefault(x => x.Contains("b"));

// Returns null:
new[] { "ca", "ce" }.LastOrDefault(x => x.Contains("b")); 

// Returns null:
new string[0].LastOrDefault();

Не замужем()

  • Если последовательность содержит ровно один элемент или ровно один элемент, соответствующий предоставленному predicate , этот элемент возвращается.
  • Если последовательность не содержит элементов или не содержит элементов, соответствующих предоставленному predicate , InvalidOperationException с сообщением «Последовательность не содержит элементов».
  • Если последовательность содержит более одного элемента или более одного элемента, соответствующего предоставленному predicate , InvalidOperationException с сообщением «Последовательность содержит более одного элемента».
  • Примечание. Чтобы оценить, содержит ли последовательность только один элемент, необходимо перечислить не более двух элементов.

пример

// Returns "a":
new[] { "a" }.Single();

// Throws InvalidOperationException because sequence contains more than one element:
new[] { "a", "b" }.Single();

// Returns "b":
new[] { "a", "b" }.Single(x => x.Equals("b"));

// Throws InvalidOperationException:
new[] { "a", "b" }.Single(x => x.Equals("c"));

// Throws InvalidOperationException:
new string[0].Single(); 

// Throws InvalidOperationException because sequence contains more than one element:
new[] { "a", "a" }.Single();

SingleOrDefault ()

  • Если последовательность содержит ровно один элемент или ровно один элемент, соответствующий предоставленному predicate , этот элемент возвращается.
  • Если последовательность не содержит элементов или не содержит элементов, соответствующих предоставленному predicate , возвращается default(T) .
  • Если последовательность содержит более одного элемента или более одного элемента, соответствующего предоставленному predicate , InvalidOperationException с сообщением «Последовательность содержит более одного элемента».
  • Если последовательность не содержит элементов, соответствующих предоставленному predicate , возвращает значение по умолчанию для типа последовательности, используя default(T) по default(T) .
  • Примечание. Чтобы оценить, содержит ли последовательность только один элемент, необходимо перечислить не более двух элементов.

пример

// Returns "a":
new[] { "a" }.SingleOrDefault();

// returns "a"
new[] { "a", "b" }.SingleOrDefault(x => x == "a"); 

// Returns null:
new[] { "a", "b" }.SingleOrDefault(x => x == "c");

// Throws InvalidOperationException:
new[] { "a", "a" }.SingleOrDefault(x => x == "a");

// Throws InvalidOperationException:
new[] { "a", "b" }.SingleOrDefault();

// Returns null:
new string[0].SingleOrDefault();

рекомендации

  • Хотя вы можете использовать FirstOrDefault , LastOrDefault или SingleOrDefault , чтобы проверить , содержит ли последовательность какие - либо элементы, Any или Count являются более надежными. Это связано с тем, что возвращаемое значение по default(T) из одного из этих трех методов не доказывает, что последовательность пуста, поскольку значение первого / последнего / единственного элемента последовательности может быть равно по default(T)

  • Решите, какие методы больше всего подходят для вашего кода. Например, используйте Single только в том случае, если вы должны убедиться, что в коллекции есть один элемент, соответствующий вашему предикату, в противном случае используйте First ; как Single throw исключение, если последовательность имеет более одного соответствующего элемента. Это, конечно же, относится и к «* OrDefault» -конвертерам.

  • Что касается эффективности: хотя часто бывает необходимо убедиться, что есть только один элемент ( Single ) или один или ноль ( SingleOrDefault ), возвращаемый запросом, оба этих метода требуют больше, а часто и всей коллекции для проверки, чтобы гарантировать отсутствие второго ответа на запрос. Это не похоже на поведение, например, метода First , который может быть удовлетворен после нахождения первого совпадения.

Кроме

Метод Except возвращает набор элементов, которые содержатся в первой коллекции, но не содержатся во втором. По умолчанию IEqualityComparer используется для сравнения элементов в двух наборах. Существует перегрузка, которая воспринимает IEqualityComparer как аргумент.

Пример:

int[] first = { 1, 2, 3, 4 };
int[] second = { 0, 2, 3, 5 };

IEnumerable<int> inFirstButNotInSecond = first.Except(second);
// inFirstButNotInSecond = { 1, 4 }

Выход:

1
4

Живая демонстрация на .NET скрипке

В этом случае. .Except(second) исключает элементы, содержащиеся в массиве second , а именно 2 и 3 (0 и 5 не содержатся в first массиве и пропускаются).

Обратите внимание, что Except означает Distinct (т. Е. Удаляет повторяющиеся элементы). Например:

int[] third = { 1, 1, 1, 2, 3, 4 };

IEnumerable<int> inThirdButNotInSecond = third.Except(second);
// inThirdButNotInSecond = { 1, 4 }

Выход:

1
4

Живая демонстрация на .NET скрипке

В этом случае элементы 1 и 4 возвращаются только один раз.


Реализация IEquatable или предоставление функции IEqualityComparer позволит использовать другой метод для сравнения элементов. Обратите внимание, что метод GetHashCode также должен быть переопределен, чтобы он возвращал идентичный хэш-код для object который идентичен в соответствии с реализацией IEquatable .

Пример с IEquatable:

class Holiday : IEquatable<Holiday>
{
    public string Name { get; set; }

    public bool Equals(Holiday other)
    {
        return Name == other.Name;
    }

    // GetHashCode must return true whenever Equals returns true.
    public override int GetHashCode()
    {
        //Get hash code for the Name field if it is not null.
        return Name?.GetHashCode() ?? 0;
    }
}

public class Program
{
    public static void Main()
    {
        List<Holiday> holidayDifference = new List<Holiday>();

        List<Holiday> remoteHolidays = new List<Holiday>
        {
            new Holiday { Name = "Xmas" },
            new Holiday { Name = "Hanukkah" },
            new Holiday { Name = "Ramadan" }
        };

        List<Holiday> localHolidays = new List<Holiday>
        {
            new Holiday { Name = "Xmas" },
            new Holiday { Name = "Ramadan" }
        };

        holidayDifference = remoteHolidays
            .Except(localHolidays)
            .ToList();

        holidayDifference.ForEach(x => Console.WriteLine(x.Name));
    }
}

Выход:

ханука

Живая демонстрация на .NET скрипке

SelectMany: Сглаживание последовательности последовательностей

var sequenceOfSequences = new [] { new [] { 1, 2, 3 }, new [] { 4, 5 }, new [] { 6 } };
var sequence = sequenceOfSequences.SelectMany(x => x);
// returns { 1, 2, 3, 4, 5, 6 }

Используйте SelectMany() если у вас есть, или вы создаете последовательность последовательностей, но вы хотите, чтобы результат был как одна длинная последовательность.

В LINQ Query Синтаксис:

var sequence = from subSequence in sequenceOfSequences
               from item in subSequence
               select item;

Если у вас есть коллекция коллекций и вы хотите одновременно работать с данными из родительской и дочерней коллекции, это также возможно с помощью SelectMany .

Давайте определим простые классы

public class BlogPost
{
    public int Id { get; set; }
    public string Content { get; set; }
    public List<Comment> Comments { get; set; }
}

public class Comment
{
    public int Id { get; set; }
    public string Content { get; set; }
}

Предположим, что мы имеем следующую коллекцию.

List<BlogPost> posts = new List<BlogPost>()
{
    new BlogPost()
    {
        Id = 1,
        Comments = new List<Comment>()
        {
            new Comment()
            {
                Id = 1,
                Content = "It's really great!",
            },
            new Comment()
            {
                Id = 2,
                Content = "Cool post!"
            }
        }
    },
    new BlogPost()
    {
        Id = 2,
        Comments = new List<Comment>()
        {
            new Comment()
            {
                Id = 3,
                Content = "I don't think you're right",
            },
            new Comment()
            {
                Id = 4,
                Content = "This post is a complete nonsense"
            }
        }
    }
};

Теперь мы хотим выбрать комментарий Content вместе с Id BlogPost связанным с этим комментарием. Для этого мы можем использовать соответствующую перегрузку SelectMany .

var commentsWithIds = posts.SelectMany(p => p.Comments, (post, comment) => new { PostId = post.Id, CommentContent = comment.Content });

Наши commentsWithIds выглядят так

{
    PostId = 1,
    CommentContent = "It's really great!"
},
{
    PostId = 1,
    CommentContent = "Cool post!"
},
{
    PostId = 2,
    CommentContent = "I don't think you're right"
},
{
    PostId = 2,
    CommentContent = "This post is a complete nonsense"
}

SelectMany

Метод linq SelectMany «flattens» IEnumerable<IEnumerable<T>> в IEnumerable<T> . Все T-элементы внутри экземпляров IEnumerable содержащиеся в исходном IEnumerable будут объединены в один IEnumerable .

var words = new [] { "a,b,c", "d,e", "f" };
var splitAndCombine = words.SelectMany(x => x.Split(','));
// returns { "a", "b", "c", "d", "e", "f" }

Если вы используете функцию селектора, которая превращает входные элементы в последовательности, результатом будут элементы тех последовательностей, которые возвращаются один за другим.

Обратите внимание, что, в отличие от Select() , количество элементов на выходе не должно быть таким же, как на входе.

Более реальный пример

class School
{
    public Student[] Students { get; set; }
}

class Student 
{
    public string Name { get; set; }
}    
  
var schools = new [] {
    new School(){ Students = new [] { new Student { Name="Bob"}, new Student { Name="Jack"} }},
    new School(){ Students = new [] { new Student { Name="Jim"}, new Student { Name="John"} }}
};
               
var allStudents = schools.SelectMany(s=> s.Students);
             
foreach(var student in allStudents)
{
    Console.WriteLine(student.Name);
}

Выход:

боб
Джек
Джим
Джон

Живая демонстрация на .NET скрипке

Все

All используется для проверки, если все элементы коллекции соответствуют условию или нет.
Смотри также: .any

1. Пустой параметр

Все : не разрешено использовать с пустыми параметрами.

2. Лямбда-выражение как параметр

All : Возвращает true если все элементы коллекции удовлетворяют лямбда-выражению и false противном случае:

var numbers = new List<int>(){ 1, 2, 3, 4, 5};
bool result = numbers.All(i => i < 10); // true
bool result = numbers.All(i => i >= 3); // false

3. Пустая коллекция

All : Возвращает true если коллекция пуста и предоставляется выражение лямбда:

var numbers = new List<int>();
bool result = numbers.All(i => i >= 0); // true

Примечание. All будут останавливать итерацию коллекции, как только она найдет элемент, не соответствующий условию. Это означает, что сбор не обязательно будет полностью перечислить; он будет только перечислить достаточно далеко, чтобы найти первый элемент, не соответствующий условию.

Сбор запросов по типу / литым элементам типа

interface IFoo { }
class Foo : IFoo { }
class Bar : IFoo { }

var item0 = new Foo();
var item1 = new Foo();
var item2 = new Bar();
var item3 = new Bar();
var collection = new IFoo[] { item0, item1, item2, item3 };

Использование OfType

var foos = collection.OfType<Foo>(); // result: IEnumerable<Foo> with item0 and item1
var bars = collection.OfType<Bar>(); // result: IEnumerable<Bar> item item2 and item3
var foosAndBars = collection.OfType<IFoo>(); // result: IEnumerable<IFoo> with all four items

Использование Where

var foos = collection.Where(item => item is Foo); // result: IEnumerable<IFoo> with item0 and item1
var bars = collection.Where(item => item is Bar); // result: IEnumerable<IFoo> with item2 and item3

Использование Cast

var bars = collection.Cast<Bar>();                // throws InvalidCastException on the 1st item
var foos = collection.Cast<Foo>();                // throws InvalidCastException on the 3rd item
var foosAndBars = collection.Cast<IFoo>();        // OK 

союз

Объединение двух коллекций для создания отдельной коллекции с использованием сопоставителя равенства по умолчанию

int[] numbers1 = { 1, 2, 3 };
int[] numbers2 = { 2, 3, 4, 5 };

var allElement = numbers1.Union(numbers2);   // AllElement now contains 1,2,3,4,5

Живая демонстрация на .NET скрипке

JOINS

Соединения используются для объединения разных списков или таблиц, содержащих данные с помощью общего ключа.

Как и в SQL, в LINQ поддерживаются следующие типы соединений:
Внутренний, левый, правый, крест и полный внешний вид .

В приведенных ниже примерах используются следующие два списка:

var first = new List<string>(){ "a","b","c"}; // Left data
var second = new List<string>(){ "a", "c", "d"}; // Right data

(Внутреннее соединение

var result = from f in first
             join s in second on f equals s
             select new { f, s };

var result = first.Join(second, 
                        f => f, 
                        s => s,
                        (f, s) => new { f, s });

// Result: {"a","a"}
//         {"c","c"}

Левое внешнее соединение

var leftOuterJoin = from f in first
                    join s in second on f equals s into temp
                    from t in temp.DefaultIfEmpty()
                    select new { First = f, Second = t};

// Or can also do:
var leftOuterJoin = from f in first
                    from s in second.Where(x => x == f).DefaultIfEmpty()
                    select new { First = f, Second = s};

// Result: {"a","a"}
//         {"b", null}  
//         {"c","c"}  


// Left outer join method syntax
var leftOuterJoinFluentSyntax = first.GroupJoin(second,
                                      f => f,
                                      s => s,
                                      (f, s) => new { First = f, Second = s })
                                   .SelectMany(temp => temp.Second.DefaultIfEmpty(),
                                      (f, s) => new { First = f.First, Second = s });

Правостороннее соединение

var rightOuterJoin = from s in second
                     join f in first on s equals f into temp
                     from t in temp.DefaultIfEmpty()
                     select new {First=t,Second=s};

// Result: {"a","a"}
//         {"c","c"}  
//         {null,"d"}  

Крест

var CrossJoin = from f in first
                from s in second
                select new { f, s };

// Result: {"a","a"}
//         {"a","c"}  
//         {"a","d"}  
//         {"b","a"}
//         {"b","c"}  
//         {"b","d"}  
//         {"c","a"}
//         {"c","c"}  
//         {"c","d"}

Полная внешняя связь

var fullOuterjoin = leftOuterJoin.Union(rightOuterJoin);

// Result: {"a","a"}
//         {"b", null}  
//         {"c","c"}  
//         {null,"d"}

Практический пример

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

В следующем примере используется только один класс Region , на самом деле вы бы присоединились к двум или более различным таблицам, которые содержат один и тот же ключ (в этом примере first и second соединяются с помощью общего ID ключа).

Пример. Рассмотрим следующую структуру данных:

public class Region 
{
    public Int32 ID;
    public string RegionDescription;
    
    public Region(Int32 pRegionID, string pRegionDescription=null)
    {
        ID = pRegionID; RegionDescription = pRegionDescription;
    }
}

Теперь подготовьте данные (т.е. заполните данные):

// Left data
var first = new List<Region>() 
                 { new Region(1), new Region(3), new Region(4) }; 
// Right data
var second = new List<Region>() 
                 { 
                    new Region(1, "Eastern"),  new Region(2, "Western"),
                    new Region(3, "Northern"), new Region(4, "Southern")
                 }; 

Вы можете видеть, что в этом примере first не содержится описания региона, поэтому вы хотите присоединиться к ним со second . Тогда внутреннее соединение будет выглядеть так:

// do the inner join
var result = from f in first
             join s in second on f.ID equals s.ID
             select new { f.ID, s.RegionDescription };


 // Result: {1,"Eastern"}
 //         {3, Northern}  
 //         {4,"Southern"}  

Этот результат создал анонимные объекты «на лету», и это хорошо, но мы уже создали правильный класс, поэтому мы можем указать его: вместо select new { f.ID, s.RegionDescription }; мы можем сказать, select new Region(f.ID, s.RegionDescription); , который вернет те же данные, но создаст объекты типа Region -, которые будут поддерживать совместимость с другими объектами.

Живая демонстрация на .NET скрипке

отчетливый

Возвращает уникальные значения из IEnumerable . Уникальность определяется с помощью сопоставления равенства по умолчанию.

int[] array = { 1, 2, 3, 4, 2, 5, 3, 1, 2 };

var distinct = array.Distinct();
// distinct = { 1, 2, 3, 4, 5 }

Чтобы сравнить пользовательский тип данных, нам нужно реализовать интерфейс IEquatable<T> и предоставить методы GetHashCode и Equals для типа. Или сопоставитель равенства может быть переопределен:

class SSNEqualityComparer : IEqualityComparer<Person> {
    public bool Equals(Person a, Person b) => return a.SSN == b.SSN;
    public int GetHashCode(Person p) => p.SSN;
}

List<Person> people;

distinct = people.Distinct(SSNEqualityComparer);

GroupBy одно или несколько полей

Предположим, что у нас есть модель фильма:

public class Film {
    public string Title { get; set; }
    public string Category { get; set; }
    public int Year { get; set; }
}

Категория по категориям:

foreach (var grp in films.GroupBy(f => f.Category)) {
    var groupCategory = grp.Key;
    var numberOfFilmsInCategory = grp.Count();
}

Группа по категориям и годам:

foreach (var grp in films.GroupBy(f => new { Category = f.Category, Year = f.Year })) {
    var groupCategory = grp.Key.Category;
    var groupYear = grp.Key.Year;
    var numberOfFilmsInCategory = grp.Count();
}

Использование Range с различными методами Linq

Вы можете использовать класс Enumerable вместе с запросами Linq, чтобы преобразовать для циклов в Linq один лайнер.

Выберите пример

Против этого:

var asciiCharacters = new List<char>();
for (var x = 0; x < 256; x++)
{
    asciiCharacters.Add((char)x);
}

Вы можете сделать это:

var asciiCharacters = Enumerable.Range(0, 256).Select(a => (char) a);

Где пример

В этом примере будет создано 100 номеров, и даже те будут извлечены

var evenNumbers = Enumerable.Range(1, 100).Where(a => a % 2 == 0);

Запрос заказа - OrderBy () ThenBy () OrderByDescending () ThenByDescending ()

string[] names= { "mark", "steve", "adam" };

По возрастанию:

Синтаксис запроса

var sortedNames =
    from name in names
    orderby name
    select name;

Синтаксис метода

var sortedNames = names.OrderBy(name => name);

sortedNames содержит имена в следующем порядке: «adam», «mark», «steve»

По убыванию:

Синтаксис запроса

var sortedNames =
    from name in names
    orderby name descending
    select name;

Синтаксис метода

var sortedNames = names.OrderByDescending(name => name);

sortedNames содержит имена в следующем порядке: «steve», «mark», «adam»

Заказ по нескольким полям

Person[] people =
{
    new Person { FirstName = "Steve", LastName = "Collins", Age = 30},
    new Person { FirstName = "Phil" , LastName = "Collins", Age = 28},
    new Person { FirstName = "Adam" , LastName = "Ackerman", Age = 29},
    new Person { FirstName = "Adam" , LastName = "Ackerman", Age = 15}
};

Синтаксис запроса

var sortedPeople = from person in people
                   orderby person.LastName, person.FirstName, person.Age descending
                   select person;

Синтаксис метода

 sortedPeople = people.OrderBy(person => person.LastName)
                      .ThenBy(person => person.FirstName)
                      .ThenByDescending(person => person.Age);

Результат

1. Adam Ackerman 29
2. Adam Ackerman 15
3. Phil Collins  28
4. Steve Collins 30

основы

LINQ в значительной степени полезен для запросов к коллекциям (или массивам).

Например, учитывая следующие примеры данных:

var classroom = new Classroom
{
    new Student { Name = "Alice", Grade = 97, HasSnack = true  },
    new Student { Name = "Bob",   Grade = 82, HasSnack = false },
    new Student { Name = "Jimmy", Grade = 71, HasSnack = true  },
    new Student { Name = "Greg",  Grade = 90, HasSnack = false },
    new Student { Name = "Joe",   Grade = 59, HasSnack = false }
}

Мы можем «запросить» эти данные с помощью синтаксиса LINQ. Например, чтобы получить всех студентов, которые сегодня перекусывают:

var studentsWithSnacks = from s in classroom.Students
                         where s.HasSnack
                         select s;

Или, чтобы получить студентов со степенью 90 или выше и только вернуть их имена, а не полный объект Student :

var topStudentNames = from s in classroom.Students
                      where s.Grade >= 90
                      select s.Name;

Функция LINQ состоит из двух синтаксисов, которые выполняют одни и те же функции, имеют почти идентичную производительность, но написаны очень по-разному. Синтаксис в приведенном выше примере называется синтаксисом запроса . Следующий пример, однако, иллюстрирует синтаксис метода . Те же данные будут возвращены, как в приведенном выше примере, но способ записи запроса отличается.

var topStudentNames = classroom.Students
                               .Where(s => s.Grade >= 90)
                               .Select(s => s.Name);

Группа по

GroupBy - это простой способ сортировки коллекции элементов IEnumerable<T> в отдельные группы.

Простой пример

В этом первом примере мы получаем две группы, нечетные и четные элементы.

List<int> iList = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var grouped = iList.GroupBy(x => x % 2 == 0);

//Groups iList into odd [13579] and even[2468] items 
       
foreach(var group in grouped)
{
    foreach (int item in group)
    {
        Console.Write(item); // 135792468  (first odd then even)
    }
}

Более сложный пример

Давайте возьмем в качестве примера список людей по возрасту. Во-первых, мы создадим объект Person, который имеет два свойства: имя и возраст.

public class Person
{
    public int Age {get; set;}
    public string Name {get; set;}
}

Затем мы создаем наш образец списка людей с разными именами и возрастом.

List<Person> people = new List<Person>();
people.Add(new Person{Age = 20, Name = "Mouse"});
people.Add(new Person{Age = 30, Name = "Neo"});
people.Add(new Person{Age = 40, Name = "Morpheus"});
people.Add(new Person{Age = 30, Name = "Trinity"});
people.Add(new Person{Age = 40, Name = "Dozer"});
people.Add(new Person{Age = 40, Name = "Smith"});

Затем мы создаем запрос LINQ для группировки списка людей по возрасту.

var query = people.GroupBy(x => x.Age);

Поступая таким образом, мы можем видеть возраст для каждой группы и иметь список каждого человека в группе.

foreach(var result in query)
{
    Console.WriteLine(result.Key);
                
    foreach(var person in result)
        Console.WriteLine(person.Name);
}

Это приводит к следующему результату:

20
Mouse
30
Neo
Trinity
40
Morpheus
Dozer
Smith

Вы можете играть с живой демонстрацией на .NET Fiddle

любой

Any используется для проверки того, соответствует ли какой-либо элемент коллекции условию или нет.
Смотри также: .Все , Любой и FirstOrDefault: лучшая практика

1. Пустой параметр

Any : Возвращает true если коллекция имеет какие-либо элементы и false если коллекция пуста:

var numbers = new List<int>();
bool result = numbers.Any(); // false

var numbers = new List<int>(){ 1, 2, 3, 4, 5};
bool result = numbers.Any(); //true

2. Лямбда-выражение как параметр

Any : Возвращает true если коллекция имеет один или несколько элементов, удовлетворяющих условию в выражении лямбда:

var arrayOfStrings = new string[] { "a", "b", "c" };
arrayOfStrings.Any(item => item == "a");    // true
arrayOfStrings.Any(item => item == "d");    // false

3. Пустая коллекция

Any : Возвращает false если коллекция пуста и предоставляется выражение лямбда:

var numbers = new List<int>();
bool result = numbers.Any(i => i >= 0); // false

Примечание. Any остановит итерацию коллекции, как только найдет элемент, соответствующий условию. Это означает, что сбор не обязательно будет полностью перечислить; он будет только перечислить достаточно далеко, чтобы найти первый элемент, соответствующий условию.

Живая демонстрация на .NET скрипке

ToDictionary

Метод ToDictionary() LINQ может использоваться для создания коллекции Dictionary<TKey, TElement> на основе данного источника IEnumerable<T> .

IEnumerable<User> users = GetUsers();
Dictionary<int, User> usersById = users.ToDictionary(x => x.Id);

В этом примере единственный аргумент, переданный ToDictionary имеет тип Func<TSource, TKey> , который возвращает ключ для каждого элемента.

Это краткий способ выполнить следующую операцию:

Dictionary<int, User> usersById = new Dictionary<int User>();
foreach (User u in users) 
{
  usersById.Add(u.Id, u);
}

Вы также можете передать второй параметр методу ToDictionary , который имеет тип Func<TSource, TElement> и возвращает Value добавляемое для каждой записи.

IEnumerable<User> users = GetUsers();
Dictionary<int, string> userNamesById = users.ToDictionary(x => x.Id, x => x.Name);

Также можно указать IComparer который используется для сравнения значений ключа. Это может быть полезно, когда ключ является строкой, и вы хотите, чтобы он соответствовал нечувствительности к регистру.

IEnumerable<User> users = GetUsers();
Dictionary<string, User> usersByCaseInsenstiveName = users.ToDictionary(x => x.Name, StringComparer.InvariantCultureIgnoreCase);

var user1 = usersByCaseInsenstiveName["john"];
var user2 = usersByCaseInsenstiveName["JOHN"];
user1 == user2; // Returns true

Примечание. Метод ToDictionary требует, чтобы все ключи были уникальными, не должно быть дубликатов ключей. Если есть, то возникает исключение: ArgumentException: An item with the same key has already been added. Если у вас есть сценарий, в котором вы знаете, что у вас будет несколько элементов с одним и тем же ключом, тогда вам лучше использовать ToLookup .

заполнитель

Aggregate Применяет функцию аккумулятора по последовательности.

int[] intList = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sum = intList.Aggregate((prevSum, current) => prevSum + current);
// sum = 55
  • На первом этапе prevSum = 1
  • На втором prevSum = prevSum(at the first step) + 2
  • На i-м шаге prevSum = prevSum(at the (i-1) step) + i-th element of the array
string[] stringList = { "Hello", "World", "!" };
string joinedString = stringList.Aggregate((prev, current) => prev + " " + current);
// joinedString = "Hello World !"

Вторая перегрузка Aggregate также получает параметр seed который является начальным значением аккумулятора. Это можно использовать для вычисления нескольких условий в коллекции без повторения его более одного раза.

List<int> items = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

Для сбора items мы хотим рассчитать

  1. Общее количество .Count
  2. Количество четных чисел
  3. Соберите каждый четвертый пункт

Используя Aggregate это можно сделать следующим образом:

var result = items.Aggregate(new { Total = 0, Even = 0, FourthItems = new List<int>() },
                (accumelative,item) =>
                new {
                    Total = accumelative.Total + 1,
                    Even = accumelative.Even + (item % 2 == 0 ? 1 : 0),
                    FourthItems = (accumelative.Total + 1)%4 == 0 ? 
                        new List<int>(accumelative.FourthItems) { item } : 
                        accumelative.FourthItems 
                });
// Result:
// Total = 12
// Even = 6
// FourthItems = [4, 8, 12]

Обратите внимание, что использование анонимного типа в качестве семени должно создавать новый объект для каждого элемента, потому что свойства доступны только для чтения. Используя пользовательский класс, вы можете просто назначить информацию, а new не нужен (только при задании начального параметра seed

Определение переменной внутри запроса Linq (пусть ключевое слово)

Чтобы определить переменную внутри выражения linq, вы можете использовать ключевое слово let . Обычно это делается для хранения результатов промежуточных подзапросов, например:

 int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

 var aboveAverages = from number in numbers
                     let average = numbers.Average()
                     let nSquared = Math.Pow(number,2)
                     where nSquared > average
                     select number;

 Console.WriteLine("The average of the numbers is {0}.", numbers.Average());

 foreach (int n in aboveAverages)
 {
   Console.WriteLine("Query result includes number {0} with square of {1}.", n, Math.Pow(n,2));
 }

Выход:

Среднее число - 4,5.
Результат запроса включает номер 3 с квадратом 9.
Результат запроса включает номер 4 с квадратом 16.
Результат запроса включает номер 5 с квадратом 25.
Результат запроса включает номер 6 с квадратом 36.
Результат запроса включает номер 7 с квадратом 49.
Результат запроса включает в себя номер 8 с квадратом 64.
Результат запроса включает номер 9 с квадратом 81.

Посмотреть демо

SkipWhile

SkipWhile() используется для исключения элементов до первого несоответствия (это может быть интуитивно понятным для большинства)

int[] list = { 42, 42, 6, 6, 6, 42 };
var result = list.SkipWhile(i => i == 42); 
// Result: 6, 6, 6, 42

DefaultIfEmpty

DefaultIfEmpty используется для возврата элемента по умолчанию, если последовательность не содержит элементов. Этот Элемент может быть По умолчанию Тип или пользовательского экземпляра этого Типа. Пример:

var chars = new List<string>() { "a", "b", "c", "d" };

chars.DefaultIfEmpty("N/A").FirstOrDefault(); // returns "a";

chars.Where(str => str.Length > 1)
     .DefaultIfEmpty("N/A").FirstOrDefault(); // return "N/A"

chars.Where(str => str.Length > 1)
        .DefaultIfEmpty().First(); // returns null;

Использование в левых соединениях :

С DefaultIfEmpty традиционная Linq Join может вернуть объект по умолчанию, если совпадение не найдено. Таким образом, выступая в качестве левого соединения SQL. Пример:

var leftSequence = new List<int>() { 99, 100, 5, 20, 102, 105 };
var rightSequence = new List<char>() { 'a', 'b', 'c', 'i', 'd' };

var numbersAsChars = from l in leftSequence
                     join r in rightSequence
                     on l equals (int)r into leftJoin
                     from result in leftJoin.DefaultIfEmpty('?')
                     select new
                     {
                         Number = l,
                         Character = result
                     };

foreach(var item in numbersAsChars)
{
    Console.WriteLine("Num = {0} ** Char = {1}", item.Number, item.Character);
}

ouput: 

Num = 99         Char = c
Num = 100        Char = d
Num = 5          Char = ?
Num = 20         Char = ?
Num = 102        Char = ?
Num = 105        Char = i

В случае, когда используется DefaultIfEmpty (без указания значения по умолчанию), и в результате не будет соответствующих элементов в правой последовательности, вы должны убедиться, что объект не имеет null до доступа к его свойствам. В противном случае это приведет к NullReferenceException . Пример:

var leftSequence = new List<int> { 1, 2, 5 };
var rightSequence = new List<dynamic>()
    {
        new { Value = 1 },
        new { Value = 2 },
        new { Value = 3 },
        new { Value = 4 },
    };

var numbersAsChars = (from l in leftSequence
                        join r in rightSequence
                        on l equals r.Value into leftJoin
                        from result in leftJoin.DefaultIfEmpty()
                        select new
                        {
                            Left = l,
                            // 5 will not have a matching object in the right so result 
                            // will be equal to null. 
                            // To avoid an error use:
                            //    -  C# 6.0 or above - ?. 
                            //    -  Under           - result == null ? 0 : result.Value
                            Right = result?.Value
                        }).ToList();

SequenceEqual

SequenceEqual используется для сравнения двух последовательностей IEnumerable<T> друг с другом.

int[] a = new int[] {1, 2, 3};
int[] b = new int[] {1, 2, 3};
int[] c = new int[] {1, 3, 2};

bool returnsTrue = a.SequenceEqual(b);
bool returnsFalse = a.SequenceEqual(c);

Count и LongCount

Count возвращает количество элементов в IEnumerable<T> . Count также предоставляет необязательный параметр предиката, который позволяет вам фильтровать элементы, которые вы хотите подсчитать.

int[] array = { 1, 2, 3, 4, 2, 5, 3, 1, 2 };

int n = array.Count(); // returns the number of elements in the array
int x = array.Count(i => i > 2); // returns the number of elements in the array greater than 2

LongCount работает так же, как и Count но имеет тип возврата long и используется для подсчета последовательностей IEnumerable<T> , которые длиннее int.MaxValue

int[] array = GetLargeArray();

long n = array.LongCount(); // returns the number of elements in the array
long x = array.LongCount(i => i > 100); // returns the number of elements in the array greater than 100

Поэтапное построение запроса

Поскольку LINQ использует отложенное выполнение , мы можем иметь объект запроса, который фактически не содержит значений, но возвращает значения при оценке. Таким образом, мы можем динамически строить запрос на основе нашего потока управления и оценивать его, как только мы закончим:

IEnumerable<VehicleModel> BuildQuery(int vehicleType, SearchModel search, int start = 1, int count = -1) {
    IEnumerable<VehicleModel> query = _entities.Vehicles
        .Where(x => x.Active && x.Type == vehicleType)
        .Select(x => new VehicleModel {
            Id = v.Id,
            Year = v.Year,
            Class = v.Class,
            Make = v.Make,
            Model = v.Model,
            Cylinders = v.Cylinders ?? 0
        });

Мы можем условно применять фильтры:

    if (!search.Years.Contains("all", StringComparer.OrdinalIgnoreCase))
        query = query.Where(v => search.Years.Contains(v.Year));

    if (!search.Makes.Contains("all", StringComparer.OrdinalIgnoreCase)) {
        query = query.Where(v => search.Makes.Contains(v.Make));
    }

    if (!search.Models.Contains("all", StringComparer.OrdinalIgnoreCase)) {
        query = query.Where(v => search.Models.Contains(v.Model));
    }

    if (!search.Cylinders.Equals("all", StringComparer.OrdinalIgnoreCase)) {
        decimal minCylinders = 0;
        decimal maxCylinders = 0;
        switch (search.Cylinders) {
            case "2-4":
                maxCylinders = 4;
                break;
            case "5-6":
                minCylinders = 5;
                maxCylinders = 6;
                break;
            case "8":
                minCylinders = 8;
                maxCylinders = 8;
                break;
            case "10+":
                minCylinders = 10;
                break;
        }
        if (minCylinders > 0) {
            query = query.Where(v => v.Cylinders >= minCylinders);
        }
        if (maxCylinders > 0) {
            query = query.Where(v => v.Cylinders <= maxCylinders);
        }
    }

Мы можем добавить запрос сортировки к запросу на основе условия:

    switch (search.SortingColumn.ToLower()) {
        case "make_model":
            query = query.OrderBy(v => v.Make).ThenBy(v => v.Model);
            break;
        case "year":
            query = query.OrderBy(v => v.Year);
            break;
        case "engine_size":
            query = query.OrderBy(v => v.EngineSize).ThenBy(v => v.Cylinders);
            break;
        default:
            query = query.OrderBy(v => v.Year); //The default sorting.
    }

Наш запрос может быть определен для начала из заданной точки:

    query = query.Skip(start - 1);

и определен для возврата определенного количества записей:

    if (count > -1) {
        query = query.Take(count);
    }
    return query;
}

Когда у нас есть объект запроса, мы можем оценить результаты с помощью цикла foreach или одного из методов LINQ, который возвращает набор значений, таких как ToList или ToArray :

SearchModel sm;

// populate the search model here
// ...

List<VehicleModel> list = BuildQuery(5, sm).ToList();

застежка-молния

Метод расширения Zip действует на две коллекции. Он объединяет каждый элемент в двух сериях в зависимости от положения. С помощью экземпляра Func мы используем Zip для обработки элементов из двух коллекций C # в парах. Если серия отличается по размеру, дополнительные элементы большей серии будут игнорироваться.

Чтобы взять пример из книги «C # в двух словах»,

int[] numbers = { 3, 5, 7 };
string[] words = { "three", "five", "seven", "ignored" };
IEnumerable<string> zip = numbers.Zip(words, (n, w) => n + "=" + w);

Выход:

3 = три
5 = пять
7 = семь

Посмотреть демо

GroupJoin с переменной внешней дальностью

Customer[] customers = Customers.ToArray();
Purchase[] purchases = Purchases.ToArray();

var groupJoinQuery =
    from c in customers
    join p in purchases on c.ID equals p.CustomerID
    into custPurchases
    select new
    {
        CustName = c.Name,
        custPurchases
    };

ElementAt и ElementAtOrDefault

ElementAt вернет элемент с индексом n . Если n не входит в диапазон перечислимого, генерируется ArgumentOutOfRangeException .

int[] numbers  = { 1, 2, 3, 4, 5 };
numbers.ElementAt(2);  // 3
numbers.ElementAt(10); // throws ArgumentOutOfRangeException

ElementAtOrDefault вернет элемент с индексом n . Если n не входит в диапазон перечислимого, возвращает значение по default(T) .

int[] numbers  = { 1, 2, 3, 4, 5 };
numbers.ElementAtOrDefault(2);  // 3
numbers.ElementAtOrDefault(10); // 0 = default(int)

И ElementAt и ElementAtOrDefault оптимизированы для того, когда источником является IList<T> и нормальная индексация будет использоваться в этих случаях.

Обратите внимание, что для ElementAt , если предоставленный индекс больше, чем размер IList<T> , список должен (но технически не гарантирован) генерировать ArgumentOutOfRangeException .

Линк-квантификаторы

Операции квантора возвращают логическое значение, если некоторые или все элементы в последовательности удовлетворяют условию. В этой статье мы увидим некоторые общие сценарии LINQ to Objects, где мы можем использовать эти операторы. В LINQ можно использовать 3 операции Quantifiers:

All - используется для определения того, удовлетворяют ли все элементы в последовательности условию. Например:

int[] array = { 10, 20, 30 }; 
   
// Are all elements >= 10? YES
array.All(element => element >= 10); 
   
// Are all elements >= 20? NO
array.All(element => element >= 20);
    
// Are all elements < 40? YES
array.All(element => element < 40);

Any - используется для определения того, удовлетворяют ли какие-либо элементы в последовательности условию. Например:

int[] query=new int[] { 2, 3, 4 }
query.Any (n => n == 3);

Contains - используется для определения того, содержит ли последовательность указанный элемент. Например:

//for int array
int[] query =new int[] { 1,2,3 };
query.Contains(1);

//for string array
string[] query={"Tom","grey"};
query.Contains("Tom");

//for a string
var stringValue="hello";
stringValue.Contains("h");

Объединение нескольких последовательностей

Рассмотрим объекты Customer , Purchase and PurchaseItem следующим образом:

public class Customer
{
   public string Id { get; set } // A unique Id that identifies customer    
   public string Name  {get; set; }
}

public class Purchase
{
   public string Id { get; set }
   public string CustomerId {get; set; }
   public string Description { get; set; }
}

public class PurchaseItem
{
   public string Id { get; set }
   public string PurchaseId {get; set; }
   public string Detail { get; set; }
}

Рассмотрим следующие данные выборки для вышеуказанных объектов:

var customers = new List<Customer>()             
 {
    new Customer() {
        Id = Guid.NewGuid().ToString(),
        Name = "Customer1"            
    },
            
    new Customer() {
        Id = Guid.NewGuid().ToString(),
        Name = "Customer2"            
    }
 };        
    
 var purchases = new List<Purchase>() 
 {
     new Purchase() {                
         Id = Guid.NewGuid().ToString(),
         CustomerId = customers[0].Id,
         Description = "Customer1-Purchase1"            
     },

     new Purchase() {
         Id = Guid.NewGuid().ToString(),
         CustomerId = customers[0].Id,
         Description = "Customer1-Purchase2"            
     },
     
     new Purchase() {
         Id = Guid.NewGuid().ToString(),
         CustomerId = customers[1].Id,
         Description = "Customer2-Purchase1"            
     },

     new Purchase() {
         Id = Guid.NewGuid().ToString(),
         CustomerId = customers[1].Id,
         Description = "Customer2-Purchase2"            
     }
  };
    
 var purchaseItems = new List<PurchaseItem>() 
 {
     new PurchaseItem() {                
         Id = Guid.NewGuid().ToString(),
         PurchaseId= purchases[0].Id,
         Detail = "Purchase1-PurchaseItem1"            
     },

     new PurchaseItem() {                
         Id = Guid.NewGuid().ToString(),
         PurchaseId= purchases[1].Id,
         Detail = "Purchase2-PurchaseItem1"            
     },
     
     new PurchaseItem() {                
         Id = Guid.NewGuid().ToString(),
         PurchaseId= purchases[1].Id,
         Detail = "Purchase2-PurchaseItem2"            
     },

     new PurchaseItem() {                
         Id = Guid.NewGuid().ToString(),
         PurchaseId= purchases[3].Id,
         Detail = "Purchase3-PurchaseItem1"
     }
 };

Теперь рассмотрим ниже запрос linq:

var result = from c in customers
            join p in purchases on c.Id equals p.CustomerId           // first join
            join pi in purchaseItems on p.Id equals pi.PurchaseId     // second join
            select new
            {
               c.Name, p.Description, pi.Detail
            };

Чтобы вывести результат вышеуказанного запроса:

foreach(var resultItem in result)
{
    Console.WriteLine($"{resultItem.Name}, {resultItem.Description}, {resultItem.Detail}");
}

Результатом запроса будет:

Customer1, Customer1-Purchase1, Purchase1-PurchaseItem1

Customer1, Customer1-Purchase2, Purchase2-PurchaseItem1

Customer1, Customer1-Purchase2, Purchase2-PurchaseItem2

Customer2, Customer2-Purchase2, Purchase3-PurchaseItem1

Живая демонстрация на .NET скрипке

Присоединение к нескольким клавишам

  PropertyInfo[] stringProps = typeof (string).GetProperties();//string properties
  PropertyInfo[] builderProps = typeof(StringBuilder).GetProperties();//stringbuilder properties
    
    var query =
        from s in stringProps
        join b in builderProps
            on new { s.Name, s.PropertyType } equals new { b.Name, b.PropertyType }
        select new
        {
            s.Name,
            s.PropertyType,
            StringToken = s.MetadataToken,
            StringBuilderToken = b.MetadataToken
        };

Обратите внимание, что анонимные типы в join выше должны содержать одинаковые свойства, поскольку объекты считаются равными, только если все их свойства равны. В противном случае запрос не будет компилироваться.

Выбрать с помощью Func selector - Использовать для ранжирования элементов

В случае перегрузок методов Select также передается index текущего элемента в select коллекции. Это несколько его применений.

Получите «номер строки» элементов

var rowNumbers = collection.OrderBy(item => item.Property1)
                           .ThenBy(item => item.Property2)
                           .ThenByDescending(item => item.Property3)
                           .Select((item, index) => new { Item = item, RowNumber = index })
                           .ToList();

Получить ранг элемента в своей группе

var rankInGroup = collection.GroupBy(item => item.Property1)
                            .OrderBy(group => group.Key)
                            .SelectMany(group => group.OrderBy(item => item.Property2)
                                                   .ThenByDescending(item => item.Property3)
                                                   .Select((item, index) => new 
                                                   { 
                                                       Item = item, 
                                                       RankInGroup = index 
                                                   })).ToList();

Получить рейтинг групп (также известный в Oracle как dense_rank)

var rankOfBelongingGroup = collection.GroupBy(item => item.Property1)
                            .OrderBy(group => group.Key)
                            .Select((group, index) => new
                            {
                                Items = group,
                                Rank = index
                            })
                            .SelectMany(v => v.Items, (s, i) => new
                            {
                                Item = i,
                                DenseRank = s.Rank
                            }).ToList();

Для тестирования вы можете использовать:

public class SomeObject
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
    public int Property3 { get; set; }

    public override string ToString()
    {
        return string.Join(", ", Property1, Property2, Property3);
    }
}

И данные:

List<SomeObject> collection = new List<SomeObject>
{
    new SomeObject { Property1 = 1, Property2 = 1, Property3 = 1},
    new SomeObject { Property1 = 1, Property2 = 2, Property3 = 1},
    new SomeObject { Property1 = 1, Property2 = 2, Property3 = 2},
    new SomeObject { Property1 = 2, Property2 = 1, Property3 = 1},
    new SomeObject { Property1 = 2, Property2 = 2, Property3 = 1},
    new SomeObject { Property1 = 2, Property2 = 2, Property3 = 1},
    new SomeObject { Property1 = 2, Property2 = 3, Property3 = 1}
};

TakeWhile

TakeWhile возвращает элементы из последовательности, если условие истинно

int[] list = { 1, 10, 40, 50, 44, 70, 4 };
var result = list.TakeWhile(item => item < 50).ToList();
// result = { 1, 10, 40 }

сумма

Метод расширения Enumerable.Sum вычисляет сумму числовых значений.

В случае, если элементы коллекции сами являются числами, вы можете рассчитать сумму напрямую.

int[] numbers = new int[] { 1, 4, 6 };
Console.WriteLine( numbers.Sum() ); //outputs 11

Если тип элементов является сложным типом, вы можете использовать выражение лямбда для указания значения, которое должно быть рассчитано:

var totalMonthlySalary = employees.Sum( employee => employee.MonthlySalary );

Метод расширения суммы может вычисляться со следующими типами:

  • Int32
  • Int64
  • не замужем
  • двойной
  • Десятичный

Если ваша коллекция содержит типы с нулевым значением, вы можете использовать оператор null-coalescing для установки значения по умолчанию для нулевых элементов:

int?[] numbers = new int?[] { 1, null, 6 };
Console.WriteLine( numbers.Sum( number => number ?? 0 ) ); //outputs 7

ToLookup

ToLookup возвращает структуру данных, которая позволяет индексировать. Это метод расширения. Он создает экземпляр ILookup, который может быть проиндексирован или перечислен с использованием цикла foreach. Записи объединяются в группы по каждому ключу. - dotnetperls

string[] array = { "one", "two", "three" };
//create lookup using string length as key
var lookup = array.ToLookup(item => item.Length);

//join the values whose lengths are 3
Console.WriteLine(string.Join(",",lookup[3]));
//output: one,two

Другой пример:

int[] array = { 1,2,3,4,5,6,7,8 };
//generate lookup for odd even numbers (keys will be 0 and 1)
var lookup = array.ToLookup(item => item % 2);

//print even numbers after joining
Console.WriteLine(string.Join(",",lookup[0]));
//output: 2,4,6,8

//print odd numbers after joining
Console.WriteLine(string.Join(",",lookup[1]));
//output: 1,3,5,7

Создайте собственные операторы Linq для IEnumerable

Одна из замечательных особенностей Linq заключается в том, что ее так легко расширить. Вам просто нужно создать метод расширения , аргументом которого является IEnumerable<T> .

public namespace MyNamespace
{
    public static class LinqExtensions
    {
        public static IEnumerable<List<T>> Batch<T>(this IEnumerable<T> source, int batchSize)
        {
            var batch = new List<T>();
            foreach (T item in source)
            {
                batch.Add(item);
                if (batch.Count == batchSize)
                {
                    yield return batch;
                    batch = new List<T>();
                }
            }
            if (batch.Count > 0)
                yield return batch;
        }
    }
}

Этот пример разбивает элементы в IEnumerable<T> на списки фиксированного размера, последний список содержит оставшиеся элементы. Обратите внимание, как объект, к которому применяется метод расширения, передается в ( source аргумента) в качестве исходного аргумента с использованием this ключевого слова. Затем ключевое слово yield используется для вывода следующего элемента в выходном IEnumerable<T> прежде чем продолжить выполнение с этой точки (см. Ключевое слово yield ).

Этот пример будет использоваться в вашем коде следующим образом:

//using MyNamespace;
var items = new List<int> { 2, 3, 4, 5, 6 };
foreach (List<int> sublist in items.Batch(3))
{
    // do something
}

В первом цикле подписок будет {2, 3, 4} и вторым {5, 6} .

Пользовательские методы LinQ можно комбинировать со стандартными методами LinQ. например:

//using MyNamespace;
var result = Enumerable.Range(0, 13)         // generate a list
                       .Where(x => x%2 == 0) // filter the list or do something other
                       .Batch(3)             // call our extension method
                       .ToList()             // call other standard methods

Этот запрос вернет четные числа, сгруппированные партиями с размером 3: {0, 2, 4}, {6, 8, 10}, {12}

Помните, что вам нужно using MyNamespace; чтобы получить доступ к методу расширения.

Использование SelectMany вместо вложенных циклов

Учитывая 2 списка

var list1 = new List<string> { "a", "b", "c" };
var list2 = new List<string> { "1", "2", "3", "4" };

если вы хотите вывести все перестановки, вы можете использовать вложенные циклы, например

var result = new List<string>();
foreach (var s1 in list1)
    foreach (var s2 in list2)
        result.Add($"{s1}{s2}");

Используя SelectMany, вы можете выполнить ту же операцию, что и

var result = list1.SelectMany(x => list2.Select(y => $"{x}{y}", x, y)).ToList();

Любая и первая (OrDefault) - лучшая практика

Я не буду объяснять, что делает Any и FirstOrDefault , потому что в них уже есть два хороших примера. Для получения дополнительной информации см. Раздел « Все и первый», «FirstOrDefault», «Last», «LastOrDefault», «Single» и «SingleOrDefault» .

Я часто вижу код, который следует избегать ,

if (myEnumerable.Any(t=>t.Foo == "Bob"))
{
    var myFoo = myEnumerable.First(t=>t.Foo == "Bob");
    //Do stuff
}

Его можно было бы написать более эффективно

var myFoo = myEnumerable.FirstOrDefault(t=>t.Foo == "Bob");
if (myFoo != null)
{
    //Do stuff
}

Используя второй пример, коллекция выполняется только один раз и дает тот же результат, что и первый. Та же идея может быть применена к Single .

GroupBy Sum и Count

Возьмем образец класса:

public class Transaction
{
    public string Category { get; set; }
    public DateTime Date { get; set; }
    public decimal Amount { get; set; }
}

Теперь рассмотрим список транзакций:

var transactions = new List<Transaction>
{
   new Transaction { Category = "Saving Account", Amount = 56, Date = DateTime.Today.AddDays(1) },
   new Transaction { Category = "Saving Account", Amount = 10, Date = DateTime.Today.AddDays(-10) },
   new Transaction { Category = "Credit Card", Amount = 15, Date = DateTime.Today.AddDays(1) },
   new Transaction { Category = "Credit Card", Amount = 56, Date = DateTime.Today },
   new Transaction { Category = "Current Account", Amount = 100, Date = DateTime.Today.AddDays(5) },
};

Если вы хотите рассчитать разумную сумму и количество баллов, вы можете использовать GroupBy следующим образом:

var summaryApproach1 = transactions.GroupBy(t => t.Category)
                           .Select(t => new
                           {
                               Category = t.Key,
                               Count = t.Count(),
                               Amount = t.Sum(ta => ta.Amount),
                           }).ToList();

Console.WriteLine("-- Summary: Approach 1 --");
summaryApproach1.ForEach(
            row => Console.WriteLine($"Category: {row.Category}, Amount: {row.Amount}, Count: {row.Count}"));

Кроме того, вы можете сделать это за один шаг:

var summaryApproach2 = transactions.GroupBy(t => t.Category, (key, t) =>
{
        var transactionArray = t as Transaction[] ?? t.ToArray();
        return new
        {
            Category = key,
            Count = transactionArray.Length,
            Amount = transactionArray.Sum(ta => ta.Amount),
        };
}).ToList();

Console.WriteLine("-- Summary: Approach 2 --");
summaryApproach2.ForEach(
row => Console.WriteLine($"Category: {row.Category}, Amount: {row.Amount}, Count: {row.Count}"));

Вывод для обоих вышеперечисленных запросов будет таким же:

Категория: Сберегательный счет, Сумма: 66, Количество: 2

Категория: Кредитная карточка, Сумма: 71, Количество: 2

Категория: Расчетный счет, Сумма: 100, Количество: 1

Демо-версия в .NET Fiddle

Задний ход

  • Инвертирует порядок элементов в последовательности.
  • Если нет элементов, вызывается ArgumentNullException: source is null.

Пример:

// Create an array.
int[] array = { 1, 2, 3, 4 };                         //Output:
// Call reverse extension method on the array.        //4
var reverse = array.Reverse();                        //3
// Write contents of array to screen.                 //2
foreach (int value in reverse)                        //1
    Console.WriteLine(value);

Пример живого кода

Помните, что Reverse() может работать в зависимости от последовательности цепочек ваших операторов LINQ.

        //Create List of chars
        List<int> integerlist = new List<int>() { 1, 2, 3, 4, 5, 6 };

        //Reversing the list then taking the two first elements
        IEnumerable<int> reverseFirst = integerlist.Reverse<int>().Take(2);
        
        //Taking 2 elements and then reversing only thos two
        IEnumerable<int> reverseLast = integerlist.Take(2).Reverse();
        
        //reverseFirst output: 6, 5
        //reverseLast output:  2, 1

Пример живого кода

Реверс () работает путем буферизации всего, а затем проходит через него назад, whitch не очень эффективен, но ни один OrderBy с этой точки зрения.

В LINQ-to-Objects выполняются операции буферизации (Reverse, OrderBy, GroupBy и т. Д.) И операции без буферизации (Where, Take, Skip и т. Д.).

Пример: Non-buffering Обратное расширение

public static IEnumerable<T> Reverse<T>(this IList<T> list) {
    for (int i = list.Count - 1; i >= 0; i--) 
        yield return list[i];
}

Пример живого кода

Этот метод может столкнуться с проблемами, если и мутировать список во время итерации.

Перечисление Перечислимого

Интерфейс IEnumerable <T> является базовым интерфейсом для всех родовых счетчиков и является квинтэссенцией части понимания LINQ. По своей сути он представляет последовательность.

Этот базовый интерфейс наследуется всеми общими наборами, такими как Collection <T> , Array , List <T> , Dictionary <TKey, TValue> Class и HashSet <T> .

В дополнение к представлению последовательности любой класс, наследующий от IEnumerable <T>, должен предоставить IEnumerator <T>. Перечислитель предоставляет итератор для перечислимого, и эти два взаимосвязанных интерфейса и идеи являются источником высказывания «перечислять перечислимое».

«Перечисление перечислимого» - важная фраза. Перечислимый - это просто структура для итерации, она не содержит никаких материализованных объектов. Например, при сортировке перечислимый может содержать критерии поля для сортировки, но использование .OrderBy() само по себе возвращает IEnumerable <T>, который знает только, как сортировать. Использование вызова, который материализует объекты, как итерации набора, называется перечислением (например .ToList() ). Процесс перечисления будет использовать перечисляемое определение того, как перемещаться по серии и возвращать соответствующие объекты (в порядке, отфильтровать, проецировать и т. Д.).

Только после перечислимого перечисления он вызывает материализацию объектов, когда метрики, такие как временная сложность (как долго это должно относиться к размеру сериала) и пространственная сложность (объем пространства, который он должен использовать, связанный с размером рядов), могут быть измеренным.

Создание собственного класса, наследующего от IEnumerable <T>, может быть немного сложным в зависимости от базовой серии, которая должна быть перечислимой. В общем, лучше всего использовать одну из существующих коллекций. Тем не менее, также можно наследовать от интерфейса IEnumerable <T>, не имея определенного массива в качестве базовой структуры.

Например, использование серии Фибоначчи в качестве базовой последовательности. Обратите внимание, что вызов Where просто строит IEnumerable , и только до тех пор, пока не будет вызван вызов для перечисления того, что перечисляемое сделано, чтобы было реализовано какое-либо из значений.

void Main()
{
    Fibonacci Fibo = new Fibonacci();
    IEnumerable<long> quadrillionplus = Fibo.Where(i => i > 1000000000000);
    Console.WriteLine("Enumerable built");
    Console.WriteLine(quadrillionplus.Take(2).Sum());
    Console.WriteLine(quadrillionplus.Skip(2).First());

    IEnumerable<long> fibMod612 = Fibo.OrderBy(i => i % 612);
    Console.WriteLine("Enumerable built");
    Console.WriteLine(fibMod612.First());//smallest divisible by 612
}

public class Fibonacci : IEnumerable<long>
{
    private int max = 90;

    //Enumerator called typically from foreach
    public IEnumerator GetEnumerator() {
        long n0 = 1;
        long n1 = 1;
        Console.WriteLine("Enumerating the Enumerable");
        for(int i = 0; i < max; i++){
            yield return n0+n1;
            n1 += n0;
            n0 = n1-n0;
        }
    }
    
    //Enumerable called typically from linq
    IEnumerator<long> IEnumerable<long>.GetEnumerator() {
        long n0 = 1;
        long n1 = 1;
        Console.WriteLine("Enumerating the Enumerable");
        for(int i = 0; i < max; i++){
            yield return n0+n1;
            n1 += n0;
            n0 = n1-n0;
        }
    }
}

Выход

Enumerable built
Enumerating the Enumerable
4052739537881
Enumerating the Enumerable
4052739537881
Enumerable built
Enumerating the Enumerable
14930352

Сила во втором наборе (fibMod612) заключается в том, что, хотя мы сделали вызов упорядочить весь наш набор чисел Фибоначчи, поскольку только одно значение было принято с использованием .First() сложность времени была O (n), поскольку только 1 значение необходимо было сравнить при выполнении алгоритма упорядочения. Это связано с тем, что наш счетчик запрашивал только 1 значение, поэтому весь перечислимый материал не должен был быть реализован. Если бы мы использовали .Take(5) вместо .First() перечислитель запросил бы 5 значений, и не более 5 значений должны были быть реализованы. По сравнению с необходимостью заказа всего набора, а затем принимать первые 5 значений, принцип сохраняет много времени и пространства выполнения.

Сортировать по

Заказывает коллекцию по заданному значению.

Когда значение представляет собой целое число , double или float начинается с минимального значения , а это означает, что вы сначала получаете отрицательные значения, чем ноль, а послесловия - положительные значения (см. Пример 1).

Когда вы заказываете char, метод сравнивает значения ascii символов для сортировки коллекции (см. Пример 2).

Когда вы сортируете строки, метод OrderBy сравнивает их, просматривая их CultureInfo, но нормально, начиная с первой буквы в алфавите (a, b, c ...).

Такой порядок называется восходящим, если вы хотите, чтобы он был наоборот, вам нужно спуститься (см. OrderByDescending).

Пример 1:

int[] numbers = {2, 1, 0, -1, -2};
IEnumerable<int> ascending = numbers.OrderBy(x => x);
// returns {-2, -1, 0, 1, 2}

Пример 2:

 char[] letters = {' ', '!', '?', '[', '{', '+', '1', '9', 'a', 'A', 'b', 'B', 'y', 'Y', 'z', 'Z'};
 IEnumerable<char> ascending = letters.OrderBy(x => x);
 // returns { ' ', '!', '+', '1', '9', '?', 'A', 'B', 'Y', 'Z', '[', 'a', 'b', 'y', 'z', '{' }

Пример:

class Person
{
   public string Name { get; set; }
   public int Age { get; set; }
}

var people = new[]
{
    new Person {Name = "Alice", Age = 25},
    new Person {Name = "Bob", Age = 21},
    new Person {Name = "Carol", Age = 43}
};
var youngestPerson = people.OrderBy(x => x.Age).First();
var name = youngestPerson.Name; // Bob

OrderByDescending

Заказывает коллекцию по заданному значению.

Когда значение представляет собой целое число , double или float начинается с максимального значения , что означает, что вы сначала получаете положительные значения, а не 0, а послесловия - отрицательные значения (см. Пример 1).

Когда вы заказываете char, метод сравнивает значения ascii символов для сортировки коллекции (см. Пример 2).

Когда вы сортируете строки, метод OrderBy сравнивает их, рассматривая их CultureInfo, но нормально, начиная с последней буквы в алфавите (z, y, x, ...).

Такой порядок называется нисходящим, если вы хотите его наоборот, вам нужно подняться (см. OrderBy).

Пример 1:

int[] numbers = {-2, -1, 0, 1, 2};
IEnumerable<int> descending = numbers.OrderByDescending(x => x);
// returns {2, 1, 0, -1, -2}

Пример 2:

char[] letters = {' ', '!', '?', '[', '{', '+', '1', '9', 'a', 'A', 'b', 'B', 'y', 'Y', 'z', 'Z'};
IEnumerable<char> descending = letters.OrderByDescending(x => x);
// returns { '{', 'z', 'y', 'b', 'a', '[', 'Z', 'Y', 'B', 'A', '?', '9', '1', '+', '!', ' ' }

Пример 3:

class Person
{
   public  string Name { get; set; }
   public  int Age { get; set; }
}

var people = new[]
{
    new Person {Name = "Alice", Age = 25},
    new Person {Name = "Bob", Age = 21},
    new Person {Name = "Carol", Age = 43}
};
var oldestPerson = people.OrderByDescending(x => x.Age).First();
var name = oldestPerson.Name; // Carol

Concat

Объединяет две коллекции (без удаления дубликатов)

List<int> foo = new List<int> { 1, 2, 3 };
List<int> bar = new List<int> { 3, 4, 5 };

// Through Enumerable static class
var result = Enumerable.Concat(foo, bar).ToList(); // 1,2,3,3,4,5

// Through extension method
var result = foo.Concat(bar).ToList(); // 1,2,3,3,4,5

Содержит

MSDN:

Определяет, содержит ли последовательность указанный элемент, используя указанный IEqualityComparer<T>

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var result1 = numbers.Contains(4); // true
var result2 = numbers.Contains(8); // false

List<int> secondNumberCollection = new List<int> { 4, 5, 6, 7 };
// Note that can use the Intersect method in this case
var result3 = secondNumberCollection.Where(item => numbers.Contains(item)); // will be true only for 4,5

Использование пользовательского объекта:

public class Person
{
   public string Name { get; set; }
}

List<Person> objects = new List<Person>
{
    new Person { Name = "Nikki"},
    new Person { Name = "Gilad"},
    new Person { Name = "Phil"},
    new Person { Name = "John"}
};

//Using the Person's Equals method - override Equals() and GetHashCode() - otherwise it
//will compare by reference and result will be false
var result4 = objects.Contains(new Person { Name = "Phil" }); // true

Использование перегрузки Enumerable.Contains(value, comparer) :

public class Compare : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.Name == y.Name;
    }
    public int GetHashCode(Person codeh)
    {
        return codeh.Name.GetHashCode();
    }
}

var result5 = objects.Contains(new Person { Name = "Phil" }, new Compare()); // true

Умное использование Contains заключается в замене нескольких предложений if на вызов Contains .

Поэтому вместо этого:

if(status == 1 || status == 3 || status == 4)
{
    //Do some business operation
}
else
{
    //Do something else
}

Сделай это:

if(new int[] {1, 3, 4 }.Contains(status)
{
    //Do some business operaion
}
else 
{
    //Do something else
}


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow