Přehled LINQ

Language-Integrated Query (LINQ) poskytuje možnosti dotazování na úrovni jazyka a rozhraní API s vyšším pořadím pro C# a Visual Basic, které vám umožní napsat výrazný deklarativní kód.

Syntaxe dotazů na úrovni jazyka

Toto je syntaxe dotazu na úrovni jazyka:

var linqExperts = from p in programmers
                  where p.IsNewToLINQ
                  select new LINQExpert(p);
Dim linqExperts = From p in programmers
                  Where p.IsNewToLINQ
                  Select New LINQExpert(p)

Toto je stejný příklad s použitím IEnumerable<T> rozhraní API:

var linqExperts = programmers.Where(p => p.IsNewToLINQ)
                             .Select(p => new LINQExpert(p));
Dim linqExperts = programmers.Where(Function(p) p.IsNewToLINQ).
                             Select(Function(p) New LINQExpert(p))

LINQ je vyjádření

Představte si, že máte seznam domácích, ale chcete ho převést do slovníku, kde můžete získat přístup k PET přímo podle jeho RFID hodnoty.

Toto je tradiční imperativní kód:

var petLookup = new Dictionary<int, Pet>();

foreach (var pet in pets)
{
    petLookup.Add(pet.RFID, pet);
}
Dim petLookup = New Dictionary(Of Integer, Pet)()

For Each pet in pets
    petLookup.Add(pet.RFID, pet)
Next

Záměr za kódem není vytvořit nové Dictionary<int, Pet> a přidat ho do něj prostřednictvím smyčky, proto je možné převést existující seznam do slovníku. LINQ zachovává záměr, zatímco nepodmíněný kód ne.

Toto je ekvivalentní výraz LINQ:

var petLookup = pets.ToDictionary(pet => pet.RFID);
Dim petLookup = pets.ToDictionary(Function(pet) pet.RFID)

Kód, který používá LINQ, je užitečný, protože se jedná o hrací pole mezi záměrem a kódem v případě, kdy je důvodem jako programátor. Další bonus je kód zkrácení. Představte si zmenšení velkých částí základu kódu pomocí 1/3, jak bylo dokončeno. Sladká práce, která je pravá?

Poskytovatelé LINQ zjednodušují přístup k datům

V rámci značné škály softwaru na volném světě se všechno spousty práce s daty z nějakého zdroje (databáze, JSON, XML a tak dále). To často zahrnuje učení nového rozhraní API pro každý zdroj dat, což může být nepříjemné. Technologie LINQ to zjednodušuje tím, že abstrakce společné prvky přístupu k datům do syntaxe dotazu, která vypadá stejně bez ohledu na to, jaký zdroj dat vyberete.

Vyhledá všechny prvky XML s konkrétní hodnotou atributu:

public static IEnumerable<XElement> FindAllElementsWithAttribute(XElement documentRoot, string elementName,
                                           string attributeName, string value)
{
    return from el in documentRoot.Elements(elementName)
           where (string)el.Element(attributeName) == value
           select el;
}
Public Shared Function FindAllElementsWithAttribute(documentRoot As XElement, elementName As String,
                                           attributeName As String, value As String) As IEnumerable(Of XElement)
    Return From el In documentRoot.Elements(elementName)
           Where el.Element(attributeName).ToString() = value
           Select el
End Function

Psaní kódu pro ruční procházení dokumentu XML za účelem provedení této úlohy by bylo mnohem náročnější.

Interakce s XML není jedinou věcí, kterou můžete dělat s poskytovateli LINQ. LINQ to SQL je poměrně holé Object-Relational MAPOVAČ (ORM) pro databázi serveru MSSQL. Knihovna JSON.NET poskytuje efektivní procházení dokumentů JSON prostřednictvím LINQ. Kromě toho, pokud není k dispozici knihovna, kterou potřebujete, můžete také napsat vlastního poskytovatele LINQ.

Důvody pro použití syntaxe dotazu

Proč používat syntaxi dotazů? Jedná se o otázku, která se často vyskytuje. Po tom, následující kód:

var filteredItems = myItems.Where(item => item.Foo);
Dim filteredItems = myItems.Where(Function(item) item.Foo)

je mnohem výstižnější než toto:

var filteredItems = from item in myItems
                    where item.Foo
                    select item;
Dim filteredItems = From item In myItems
                    Where item.Foo
                    Select item

Není syntaxí rozhraní API jenom výstižnější způsob, jak provádět syntaxi dotazů?

Ne. Syntaxe dotazu umožňuje použití klauzule let , která umožňuje začlenit a vytvořit jeho proměnnou v rámci rozsahu výrazu a použít ji v následných částech výrazu. Reprodukce stejného kódu jenom pomocí syntaxe rozhraní API se dá provést, ale Nejpravděpodobnějším výsledkem je, že je obtížné číst kód.

Takže byste to begs na otázku, měli byste pouze použít syntaxi dotazu?

Odpověď na tuto otázku je Ano , pokud:

  • Existující základ kódu už používá syntaxi dotazu.
  • V důsledku složitosti je potřeba oborovat proměnné v rámci dotazů.
  • Dáváte přednost syntaxi dotazů a nebude se navíc z vašeho základu kódu.

Odpověď na tuto otázku není v případě. ..

  • Existující základ kódu už používá syntaxi rozhraní API.
  • V rámci dotazů nemusíte oborovat proměnné.
  • Dáváte přednost syntaxi rozhraní API a nebude se navíc z vašeho základu kódu.

Základní LINQ

Úplný seznam ukázek LINQ najdete v 101Ch ukázkách LINQ.

V následujících příkladech je Stručná ukázka některých základních částí jazyka LINQ. Tato možnost není nijak ucelená, protože LINQ poskytuje více funkcí, než se tady prezentuje.

Chléb a máslo –, Where Select a Aggregate

// Filtering a list.
var germanShepherds = dogs.Where(dog => dog.Breed == DogBreed.GermanShepherd);

// Using the query syntax.
var queryGermanShepherds = from dog in dogs
                          where dog.Breed == DogBreed.GermanShepherd
                          select dog;

// Mapping a list from type A to type B.
var cats = dogs.Select(dog => dog.TurnIntoACat());

// Using the query syntax.
var queryCats = from dog in dogs
                select dog.TurnIntoACat();

// Summing the lengths of a set of strings.
int seed = 0;
int sumOfStrings = strings.Aggregate(seed, (s1, s2) => s1.Length + s2.Length);
' Filtering a list.
Dim germanShepherds = dogs.Where(Function(dog) dog.Breed = DogBreed.GermanShepherd)

' Using the query syntax.
Dim queryGermanShepherds = From dog In dogs
                          Where dog.Breed = DogBreed.GermanShepherd
                          Select dog

' Mapping a list from type A to type B.
Dim cats = dogs.Select(Function(dog) dog.TurnIntoACat())

' Using the query syntax.
Dim queryCats = From dog In dogs
                Select dog.TurnIntoACat()

' Summing the lengths of a set of strings.
Dim seed As Integer = 0
Dim sumOfStrings As Integer = strings.Aggregate(seed, Function(s1, s2) s1.Length + s2.Length)

Sloučení seznamu seznamů

// Transforms the list of kennels into a list of all their dogs.
var allDogsFromKennels = kennels.SelectMany(kennel => kennel.Dogs);
' Transforms the list of kennels into a list of all their dogs.
Dim allDogsFromKennels = kennels.SelectMany(Function(kennel) kennel.Dogs)

Sjednocení mezi dvěma sadami (s vlastní komparátor)

public class DogHairLengthComparer : IEqualityComparer<Dog>
{
    public bool Equals(Dog a, Dog b)
    {
        if (a == null && b == null)
        {
            return true;
        }
        else if ((a == null && b != null) ||
                 (a != null && b == null))
        {
            return false;
        }
        else
        {
            return a.HairLengthType == b.HairLengthType;
        }
    }

    public int GetHashCode(Dog d)
    {
        // Default hashcode is enough here, as these are simple objects.
        return d.GetHashCode();
    }
}
...

// Gets all the short-haired dogs between two different kennels.
var allShortHairedDogs = kennel1.Dogs.Union(kennel2.Dogs, new DogHairLengthComparer());

Public Class DogHairLengthComparer
  Inherits IEqualityComparer(Of Dog)

  Public Function Equals(a As Dog,b As Dog) As Boolean
      If a Is Nothing AndAlso b Is Nothing Then
          Return True
      ElseIf (a Is Nothing AndAlso b IsNot Nothing) OrElse (a IsNot Nothing AndAlso b Is Nothing) Then
          Return False
      Else
          Return a.HairLengthType = b.HairLengthType
      End If
  End Function

  Public Function GetHashCode(d As Dog) As Integer
      ' Default hashcode is enough here, as these are simple objects.
      Return d.GetHashCode()
  End Function
End Class

...

' Gets all the short-haired dogs between two different kennels.
Dim allShortHairedDogs = kennel1.Dogs.Union(kennel2.Dogs, New DogHairLengthComparer())

Průnik dvou sad

// Gets the volunteers who spend share time with two humane societies.
var volunteers = humaneSociety1.Volunteers.Intersect(humaneSociety2.Volunteers,
                                                     new VolunteerTimeComparer());
' Gets the volunteers who spend share time with two humane societies.
Dim volunteers = humaneSociety1.Volunteers.Intersect(humaneSociety2.Volunteers,
                                                     New VolunteerTimeComparer())

Řazení

// Get driving directions, ordering by if it's toll-free before estimated driving time.
var results = DirectionsProcessor.GetDirections(start, end)
              .OrderBy(direction => direction.HasNoTolls)
              .ThenBy(direction => direction.EstimatedTime);
' Get driving directions, ordering by if it's toll-free before estimated driving time.
Dim results = DirectionsProcessor.GetDirections(start, end).
                OrderBy(Function(direction) direction.HasNoTolls).
                ThenBy(Function(direction) direction.EstimatedTime)

Rovnost vlastností instance

Nakonec pokročilejší vzorek: určení, jestli se hodnoty vlastností dvou instancí stejného typu rovnají (jsou vypůjčené a upravené z tohoto příspěvku StackOverflow):

public static bool PublicInstancePropertiesEqual<T>(this T self, T to, params string[] ignore) where T : class
{
    if (self == null || to == null)
    {
        return self == to;
    }

    // Selects the properties which have unequal values into a sequence of those properties.
    var unequalProperties = from property in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
                            where !ignore.Contains(property.Name)
                            let selfValue = property.GetValue(self, null)
                            let toValue = property.GetValue(to, null)
                            where !Equals(selfValue, toValue)
                            select property;
    return !unequalProperties.Any();
}
<System.Runtime.CompilerServices.Extension()>
Public Function PublicInstancePropertiesEqual(Of T As Class)(self As T, [to] As T, ParamArray ignore As String()) As Boolean
    If self Is Nothing OrElse [to] Is Nothing Then
        Return self Is [to]
    End If

    ' Selects the properties which have unequal values into a sequence of those properties.
    Dim unequalProperties = From [property] In GetType(T).GetProperties(BindingFlags.Public Or BindingFlags.Instance)
                            Where Not ignore.Contains([property].Name)
                            Let selfValue = [property].GetValue(self, Nothing)
                            Let toValue = [property].GetValue([to], Nothing)
                            Where Not Equals(selfValue, toValue) Select [property]
    Return Not unequalProperties.Any()
End Function

PLINQ

PLINQ nebo Paralelní LINQ je paralelní prováděcí modul pro výrazy LINQ. Jinými slovy regulární výraz LINQ může být triviální paralelní napříč libovolným počtem vláken. To je provedeno prostřednictvím volání AsParallel() předcházejícího výrazu.

Zvažte použití těchto zdrojů:

public static string GetAllFacebookUserLikesMessage(IEnumerable<FacebookUser> facebookUsers)
{
    var seed = default(UInt64);

    Func<UInt64, UInt64, UInt64> threadAccumulator = (t1, t2) => t1 + t2;
    Func<UInt64, UInt64, UInt64> threadResultAccumulator = (t1, t2) => t1 + t2;
    Func<Uint64, string> resultSelector = total => $"Facebook has {total} likes!";

    return facebookUsers.AsParallel()
                        .Aggregate(seed, threadAccumulator, threadResultAccumulator, resultSelector);
}
Public Shared GetAllFacebookUserLikesMessage(facebookUsers As IEnumerable(Of FacebookUser)) As String
{
    Dim seed As UInt64 = 0

    Dim threadAccumulator As Func(Of UInt64, UInt64, UInt64) = Function(t1, t2) t1 + t2
    Dim threadResultAccumulator As Func(Of UInt64, UInt64, UInt64) = Function(t1, t2) t1 + t2
    Dim resultSelector As Func(Of Uint64, string) = Function(total) $"Facebook has {total} likes!"

    Return facebookUsers.AsParallel().
                        Aggregate(seed, threadAccumulator, threadResultAccumulator, resultSelector)
}

Tento kód bude v případě potřeby rozdělit do facebookUsers celého systémového vlákna a celková hodnota se v každém vlákně sečte, sečte výsledky vypočítané každým vláknem a projekt, který je výsledkem skvělého řetězce.

Ve tvaru diagramu:

Diagram PLINQ

Paralelizovat úlohy vázané na procesor, které lze snadno vyjádřit prostřednictvím LINQ (jinými slovy, jsou čistě funkce a nemají žádné vedlejší účinky) jsou skvělým kandidátem na PLINQ. Pro úlohy, které mají vedlejší efekt, zvažte použití paralelní knihovny Tasks.

Další zdroje informací

  • Ukázky 101 LINQ
  • Linqpad, prostředí Playground a stroj pro dotazování databáze pro C#/F # webový Basic
  • EduLinq, pro učení, jak se implementuje LINQ-to-Objects