Делегаты сравнения для сортировки и Linq-отборов в VB.NET
В последней версии VB появились ряд новых функций, а для VB примеров явно мало.К счастью, мне недавно попался проект, в котором мне встретилось огромное количество последних нововведений NET 3.5. И я решил немного просуммировать их на этой страничке.
Итак для начала предикат сравления для сортировок коллекций. Мне надо просортировать LINQ-коллекцию
Dim Z3 As System.Collections.Generic.List(Of GetLocalPriceResult)
Тут два варианта - указать непосредственно адрес функции сравнения (разместив ее внутри класса) либо указав название класса, содержащего реализацию интерфейса IComparer:
Тело функции сравнения элементов коллекции в обоих случаях одинаково и выглядит вот так:
1: Public Class LocalPrice_Comparer
2: Implements IComparer(Of GetLocalPriceResult)
3:
4: Public Function Compare(x As GetLocalPriceResult, y As GetLocalPriceResult) As Integer Implements System.Collections.Generic.IComparer(Of GetLocalPriceResult).Compare
5: If IsNumeric(x.Summ.Replace(",", "")) And IsNumeric(y.Summ.Replace(",", "")) Then
6: 'сравнение чисел
7: If CDec(x.Summ.Replace(",", "")) = CDec(y.Summ.Replace(",", "")) Then
8: Return 0
9: ElseIf CDec(x.Summ.Replace(",", "")) > CDec(y.Summ.Replace(",", "")) Then
10: Return 1
11: ElseIf CDec(x.Summ.Replace(",", "")) < CDec(y.Summ.Replace(",", "")) Then
12: Return -1
13: End If
14: ElseIf Not IsNumeric(x.Summ.Replace(",", "")) And Not IsNumeric(y.Summ.Replace(",", "")) Then
15: 'сравнение строк
16: If x.ToString = y.ToString Then
17: Return 0
18: ElseIf x.ToString > y.ToString Then
19: Return 1
20: ElseIf x.ToString < y.ToString Then
21: Return -1
22: End If
23: ElseIf IsNumeric(x.Summ.Replace(",", "")) And Not IsNumeric(y.Summ.Replace(",", "")) Then
24: 'первое число
25: Return 1
26: ElseIf Not IsNumeric(x.Summ.Replace(",", "")) And IsNumeric(y.Summ.Replace(",", "")) Then
27: 'второе число
28: Return -1
29: End If
30: End Function
Намного веселее получается, если надо просортировать типозированный массив.
Dim Y As ru.emex.EmExService.DetailItem()
В это варианте существует аж 17 вариантов указать функцию сравнения элементов массива. Пятый и шестой вариант - точно такие же как в предыщем случае сравнения коллекций.
Собственно функция сравнения в моем случае выглядела так (только в одном случае указывается класс, содержащий нужную функцию - а в другом AddressOF этой функции (а сама функция размещается в теле текущего класса:
1: Public Class EmExService_DetailItem_Comparer
2: Implements IComparer(Of ru.emex.EmExService.DetailItem)
3:
4: Public Function Compare(x As ru.emex.EmExService.DetailItem, y As ru.emex.EmExService.DetailItem) As Integer Implements System.Collections.Generic.IComparer(Of ru.emex.EmExService.DetailItem).Compare
5: If x.Prices.ResultPrice = y.Prices.ResultPrice Then
6: Return 0
7: ElseIf x.Prices.ResultPrice > y.Prices.ResultPrice Then
8: Return 1
9: ElseIf x.Prices.ResultPrice < y.Prices.ResultPrice Then
10: Return -1
11: End If
12: End Function
13: End Class
Теперь рассмотрим функцию сравнения в Linq-отборе в WHERE. Вообще условия WHERE можно составить и динамически (чаще всего требуется именно это) - но здесь я покажу самый простой вариант.
Итак, в некотороем месте сайта у меня сделан отбор:
SavedLocalZakaz = (From X In db1.EmexBasket1s Select X Where X.Archive = Archive And X.EmexNum > 0 And X.EmexComment Like AdmSearchPanel_comment And X.EmexStatus = CInt(AdmSearchPanel_status) Order By X.i Descending).ToList Return FilterForNum(SavedLocalZakaz)
И из Session мне пришел параметр, по которому надо отфильтровать эту Linq-коллекцию. Этот Session приходит не всегда, конкретно форма фильтра выглядит вот так:
и комбинаций отборов может бють много - поэтому простая статическая запись в Where тут не подойдет. К тому же условие комбинирования условий поиска сформулировано хитро - например DropdownList фильтрует независимо от текстовых полей. Поэтому сначала надо выполнить разбор что именно задано в условиях фильтрации, а потом в некоторых случаях выполнить фильтрацию:
237: If ...
238: ElseIf AdmSearchPanel_id = "" And AdmSearchPanel_num <> "" And AdmSearchPanel_comment <> "" And AdmSearchPanel_status <> "0" Then
239: 'AdmSearchPanel_num & AdmSearchPanel_comment & AdmSearchPanel_Status
240: SavedLocalZakaz = (From X In db1.EmexBasket1s Select X Where X.Archive = Archive And X.EmexNum > 0 And X.EmexComment Like AdmSearchPanel_comment And X.EmexStatus = CInt(AdmSearchPanel_status) Order By X.i Descending).ToList
241: Return FilterForNum(SavedLocalZakaz)
242: ...
243: End If
Соответственно где-то в недрах кода этой формы существует вот такой код FilterForNum:
254: Shared Function FilterForNum(SavedLocalZakaz As System.Collections.Generic.List(Of Global.Omsk.EmexBasket1)) As System.Collections.Generic.List(Of Global.Omsk.EmexBasket1)
255: Dim Z1 = SavedLocalZakaz.Where(AddressOf FilterNum)
256: If Z1 Is Nothing Then
257: Return Nothing
258: Else
259: Dim Y1 As New System.Collections.Generic.List(Of Global.Omsk.EmexBasket1)
260: For Each One In Z1
261: Y1.Add(One)
262: Next
263: Return Y1
264: End If
265: End Function
266:
Который соббственно и содержит вызов предиката WHERE запроса LINQ:
276: Public Shared Function FilterNum(One As Global.Omsk.EmexBasket1) As Boolean
277: Return (One.EmexNum.ToString.Contains(HttpContext.Current.Session("AdmSearchPanel_num")))
278: End Function
Продолжение о реализации новых возможностей NET 3.5 на VB.NET вы можете почитать в топике - Практическое применение наследования, полиморфизма, интерфейсов, дженериков и делегатов на примерах в Visual Basic .NET
|