LINQ'e genel bakış

Language-Integrated Sorgu (LINQ) dil düzeyinde sorgulama özelliklerine ek olarak C# ve Visual Basic'ye daha yüksek sıralarda bir işlev API'si sağlar. Bu API, bildirime açık bir kod yazmanız için olanak sağlar.

Dil düzeyinde sorgu söz dizimi

Bu, dil düzeyinde sorgu söz dizimidir:

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)

Bu, API'yi kullanan IEnumerable<T> örnekle aynıdır:

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 ifade edicidir

Imagine bir evcil hayvan listeniz var ancak bunu değerine göre bir evcil hayvana doğrudan erişebilirsiniz. RFID

Bu, geleneksel bir bütünsel koddur:

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

Kodun amacı yeni bir oluşturmak ve döngü aracılığıyla buna eklemek değil var olan bir listeyi sözlük Dictionary<int, Pet> haline dönüştürmektir! LINQ, amacı korurken, koruyucu kod bunu korumaz.

Bu eşdeğer LINQ ifadesidir:

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

LINQ kullanan kod, programcı olarak gerekçe oluşturmada amaç ve kod arasındaki oyun alanını çift hale geldiğinden değerlidir. Bir diğer ek olarak koda daha fazla zaman vardır. Imagine kod tabanının büyük bölümlerini yukarıda olduğu gibi 1/3 azaltabilirsiniz. Harika anlaşma, değil mi?

LINQ sağlayıcıları veri erişimini basitleştirir

Vahşi doğada önemli bir yazılım öbekleri için her şey, bazı kaynaklarda (Veritabanları, JSON, XML vb.) verilerle ilgilenmektir. Bu genellikle her veri kaynağı için yeni bir API öğrenmeyi içerir ve bu da rahatsız edici olabilir. LINQ, veri erişiminin ortak öğelerini, hangi veri kaynağını seçerse seçerek aynı görünen bir sorgu söz dizimlerine soyutlar.

Bu, belirli bir öznitelik değerine sahip tüm XML öğelerini bulur:

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

Bu görevi yapmak için XML belgesinde el ile geçiş yapmak için kod yazmak çok daha zor olabilir.

LINQ Sağlayıcıları ile tek şey XML ile etkileşim kurmak değildir. Linq to SQL, MSSQL Sunucu Veritabanı için Object-Relational Mapper'ın (ORM) oldukça çıplak bir örnektir. Json.NET kitaplığı LINQ aracılığıyla verimli JSON Belgesi geçişi sağlar. Ayrıca, ihtiyacınız olan şeyi veren bir kitaplık yoksa kendi LINQ Sağlayıcınızı da yazabilirsiniz!

Sorgu söz dizimi kullanma nedenleri

Sorgu söz dizimi neden kullanılır? Bu genellikle ortaya çıkan bir soru. Sonuçta aşağıdaki kod:

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

şundan çok daha kısadır:

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

API söz dizimi, sorgu söz dizimlerini kullanmanın daha kısa bir yolu değil mi?

Hayır. Sorgu söz dizimi, ifadenin sonraki parçalarında kullanarak ifade kapsamında bir değişken tanıtmaya ve bağlamaya olanak sağlayan let yan tümcesi kullanımına olanak sağlar. Aynı kodu yalnızca API söz dizimi ile yeniden oluşturmak yapılabilir, ancak büyük olasılıkla zor okunan koda yol açabilirsiniz.

Bu nedenle sorusunu yanıt olarak sorgu söz dizimlerini mi kullanabilirsiniz?

Şu durumda bu sorunun yanıtı evet olur:

  • Mevcut kod tabanınız zaten sorgu söz dizimini kullanır.
  • Karmaşıklık nedeniyle sorgularınız içindeki değişkenlerin kapsamını tanımlamanız gerekir.
  • Sorgu söz dizimini tercih edersiniz ve kod tabanınıza dikkati dağıtmaz.

Bu sorunun yanıtı...

  • Mevcut kod tabanınız zaten API söz dizimi kullanıyor
  • Sorgularınız içindeki değişkenlerin kapsamını tanımlamanıza gerek yoktur
  • API söz dizimini tercih edersiniz ve kod tabanınızı dağıtmaz

Temel LINQ

LINQ örneklerinin gerçekten kapsamlı bir listesi için 101 LINQ Örnekleri'ne ziyaret edin.

Aşağıdaki örnekler LINQ'nin temel parçalardan bazılarının hızlı bir gösterimidir. LINQ burada sergilenenden daha fazla işlev sağladığından bu hiçbir şekilde kapsamlı değildir.

Bread ve bread - Where , Select ve 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)

Liste listesini düzlük oluşturma

// 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)

İki küme arasında birlik (özel karşılaştırıcı ile)

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())

İki küme arasındaki kesişim

// 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())

Sıralama

// 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)

Örnek özelliklerinin eşitliği

Son olarak, daha gelişmiş bir örnek: aynı türe sahip iki örneğin özelliklerinin değerlerinin eşit olup olmadığını belirleme (Bu StackOverflowgönderisinde Ödünç alınmış ve değiştirilmiş):

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 veya Paralel LINQ, LINQ ifadeleri için bir paralel yürütme altyapısıdır. Başka bir deyişle, normal bir LINQ ifadesi herhangi bir sayıda iş parçacığı arasında önemsiz bir şekilde paralel hale getirildi. Bu, ifadeden önceki AsParallel() çağrısıyla başarılı olur.

Aşağıdaki topluluklara bir göz atın:

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)
}

Bu kod, gerektiğinde sistem iş parçacıkları arasında bölüm oluşturur, her bir iş parçacığındaki toplam like'ları paralel olarak toplar, her iş parçacığı tarafından hesaplanan sonuçları ve iyi bir dizeye neden olan facebookUsers projeyi toplar.

Diyagram formunda:

PLINQ diyagramı

LINQ aracılığıyla kolayca ifade edilebilir paralelleştirilebilir CPU'ya bağlı işler (başka bir deyişle, saf işlevlerdir ve yan etkileri yoktur) PLINQ için harika bir adaydır. Yan etkisi olan işler için Görev Paralel Kitaplığını kullanmayı göz önünde bulundurabilirsiniz.

Diğer kaynaklar

  • 101 LINQ Örnekleri
  • Linqpad,bir oyun alanı ortamı ve C#/F#/Visual Basic için veritabanı sorgulama altyapısı
  • EduLinq, LINQ-to-objects'in nasıl uygulandığını öğrenmek için bir e-kitap