Szkolenie
Moduł
Wykonywanie operacji na tablicach przy użyciu metod pomocników w języku C# - Training
Użyj metod pomocnika języka C#, aby wykonywać operacje odwrotne, zmiany rozmiaru, dzielenia i sprzężenia na tablicach.
Ta przeglądarka nie jest już obsługiwana.
Przejdź na przeglądarkę Microsoft Edge, aby korzystać z najnowszych funkcji, aktualizacji zabezpieczeń i pomocy technicznej.
Language-Integrated Query (LINQ) udostępnia funkcje zapytań na poziomie języka oraz interfejs API funkcji wyższego poziomu do języka C# i Visual Basic, które umożliwiają pisanie ekspresowego kodu deklaratywnego.
Jest to składnia zapytań na poziomie języka:
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)
Jest to ten sam przykład przy użyciu interfejsu IEnumerable<T>
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))
Imagine masz listę zwierząt domowych, ale chcesz przekonwertować ją na słownik, w którym można uzyskać dostęp do zwierzaka bezpośrednio według jego RFID
wartości.
Jest to tradycyjny kod imperatywne:
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
Intencją kodu nie jest utworzenie nowego Dictionary<int, Pet>
kodu i dodanie go za pomocą pętli. Ma to na celu przekonwertowanie istniejącej listy na słownik. LINQ zachowuje intencję, podczas gdy kod imperatywnego nie.
Jest to równoważne wyrażenie LINQ:
var petLookup = pets.ToDictionary(pet => pet.RFID);
Dim petLookup = pets.ToDictionary(Function(pet) pet.RFID)
Kod korzystający z LINQ jest cenny, ponieważ równoczesne pole działania między intencją a kodem podczas rozumowania jako programista. Innym bonusem jest zwięzłość kodu. Imagine zmniejszenie dużej części bazy kodu o 1/3, jak pokazano powyżej. Słodka transakcja, prawda?
W przypadku znacznego fragmentu oprogramowania w środowisku naturalnym wszystko obraca się wokół radzenia sobie z danymi ze źródła (Bazy danych, JSON, XML itd.). Często wiąże się to z uczeniem nowego interfejsu API dla każdego źródła danych, co może być irytujące. LINQ upraszcza to, abstrakując wspólne elementy dostępu do danych w składni zapytania, która wygląda tak samo niezależnie od wybranego źródła danych.
Spowoduje to znalezienie wszystkich elementów XML o określonej wartości atrybutu:
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
Pisanie kodu w celu ręcznego przechodzenia przez dokument XML w celu wykonania tego zadania byłoby znacznie trudniejsze.
Interakcja z kodem XML nie jest jedyną rzeczą, którą można zrobić z dostawcami LINQ. Linq to SQL to dość gołe kości Object-Relational Mapper (ORM) dla bazy danych serwera MSSQL. Biblioteka Json.NET zapewnia wydajne przechodzenie dokumentów JSON za pośrednictwem LINQ. Ponadto jeśli nie ma biblioteki, która robi to, czego potrzebujesz, możesz również napisać własnego dostawcę LINQ!
Dlaczego warto używać składni zapytania? Jest to pytanie, które często pojawia się. W końcu następujący kod:
var filteredItems = myItems.Where(item => item.Foo);
Dim filteredItems = myItems.Where(Function(item) item.Foo)
jest o wiele bardziej zwięzłe niż to:
var filteredItems = from item in myItems
where item.Foo
select item;
Dim filteredItems = From item In myItems
Where item.Foo
Select item
Czy składnia interfejsu API nie jest tylko bardziej zwięzłym sposobem wykonywania składni zapytania?
Nie. Składnia zapytania umożliwia użycie klauzuli let , która umożliwia wprowadzenie i powiązanie zmiennej w zakresie wyrażenia przy użyciu jej w kolejnych fragmentach wyrażenia. Odtworzenie tego samego kodu z tylko składnią interfejsu API można wykonać, ale najprawdopodobniej doprowadzi do kodu, który jest trudny do odczytania.
Więc to błaga pytanie, czy po prostu używasz składni zapytania?
Odpowiedź na to pytanie brzmi tak , jeśli:
Odpowiedź na to pytanie nie brzmi, jeśli...
Aby uzyskać naprawdę kompleksową listę przykładów LINQ, odwiedź stronę 101 LINQ Samples (Przykłady LINQ 101).
Poniższe przykłady to szybka demonstracja niektórych podstawowych elementów LINQ. Nie jest to w żaden sposób kompleksowe, ponieważ LINQ zapewnia więcej funkcji niż to, co zostało tutaj zaprezentowane.
// 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)
// 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)
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())
// 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())
// 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)
Na koniec bardziej zaawansowany przykład: określanie, czy wartości właściwości dwóch wystąpień tego samego typu są równe (pożyczone i zmodyfikowane z tego wpisu 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 lub Parallel LINQ to aparat wykonywania równoległego dla wyrażeń LINQ. Innymi słowy, regularne wyrażenie LINQ może być trywialnie zrównane w dowolnej liczbie wątków. Jest to realizowane za pośrednictwem wywołania AsParallel()
poprzedzającego wyrażenie.
Rozważ następujące źródła:
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)
}
Ten kod będzie partycjonować facebookUsers
między wątkami systemowymi w razie potrzeby, sumując sumę polubień dla każdego wątku równolegle, sumując wyniki obliczone przez każdy wątek i projekt, które powodują miły ciąg.
W formularzu diagramu:
Równoległe zadania związane z procesorem CPU, które można łatwo wyrazić za pośrednictwem LINQ (innymi słowy, są czystymi funkcjami i nie mają efektów ubocznych) są doskonałym kandydatem do PLINQ. W przypadku zadań, które mają efekt uboczny, rozważ użycie biblioteki równoległej zadań.
Szkolenie
Moduł
Wykonywanie operacji na tablicach przy użyciu metod pomocników w języku C# - Training
Użyj metod pomocnika języka C#, aby wykonywać operacje odwrotne, zmiany rozmiaru, dzielenia i sprzężenia na tablicach.