Главная страница

программирование. Руководство su P# a n Reference в herbert schildt полное руководство с 0 герберт шилдт


Скачать 3.32 Mb.
НазваниеРуководство su P# a n Reference в herbert schildt полное руководство с 0 герберт шилдт
Анкорпрограммирование
Дата25.01.2022
Размер3.32 Mb.
Формат файлаrtf
Имя файлаc-40-polnoe-rukovodstvo-2011.rtf
ТипРуководство
#341448
страница56 из 97
1   ...   52   53   54   55   56   57   58   59   ...   97

where addr.LastlndexOf(1.1)    != ‑1

group addr by addr.Substring(addr.LastlndexOf('.'))

into ws where ws.Count() > 2 select ws;

// Выполнить запрос и вывести его результаты.

Console.WriteLine("Домены самого верхнего уровня " +

"с более чем двумя членами.\п");

foreach(var sites in webAddrs) {

Console.WriteLine("Содержимое домена: " + sites.Key); foreach(var site in sites)

Console.WriteLine ("    "    +    site);

Console.WriteLine();

}

}

}

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

Домены самого верхнего уровня с более чем двумя членами.

Содержимое домена: .net hsNameB.net hsNameC.net hsNameH.net

Как следует из результата выполнения приведенной выше программы, по запросу возвращается только группа .net, поскольку это единственная группа, содержащая больше двух элементов.

Обратите особое внимание в данном примере программы на следующую последовательность операторов в формируемом запросе.

group addr by addr.Substring(addr.LastlndexOf('.'))

into ws where ws.Count() > 2 select ws;

Сначала результаты выполнения оператора group сохраняются как временные для последующей обработки оператором where. В качестве переменной диапазона в данный момент служит переменная ws. Она охватывает все группы, возвращаемые оператором group. Затем результаты запроса отбираются в операторе where с таким расчетом, чтобы в конечном итоге остались только те группы, которые содержат больше двух членов. Для этой цели вызывается метод Count () , который является методом расширения и реализуется для всех объектов типа I Enumerable. Он возвращает количество элементов в последовательности. (Подробнее о методах расширения речь пойдет далее в этой главе.) А получающаяся в итоге последовательность групп возвращается оператором select.

Применение оператора let для создания временной переменной в запросе

Иногда возникает потребность временно сохранить некоторое значение в самом запросе. Допустим, что требуется создать переменную перечислимого типа, которую можно будет затем запросить, или же сохранить некоторое значение, чтобы в дальнейшем использовать его в операторе where. Независимо от преследуемой цели, эти виды функций могут быть осуществлены с помощью оператора let. Ниже приведена общая форма оператора let:

let имя = выражение

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

В приведенном ниже примере программы демонстрируется применение оператора let для создания еще одного перечислимого источника данных. В качестве входных данных в запрос вводится массив символьных строк, которые затем преобразуются в массивы типа char. Для этой цели служит еще один метод обработки строк, называемый ToCharArray () и возвращающий массив, содержащий символы в строке. Полученный результат присваивается переменной chrArray, которая затем используется во вложенном операторе from для извлечения отдельных символов из массива. И наконец, полученные символы сортируются в запросе, и из них формируется результирующая последовательность.

// Использовать оператор let в месте с вложенным оператором from.

using System; using System.Linq;

class LetDemo {

static void Main() {

string[] strs = { "alpha", "beta", "gamma" };

// Сформировать запрос на получение символов, возвращаемых из // строк в отсортированной последовательности. Обратите внимание // на применение вложенного оператора from, var chrs = from str in strs

let chrArray = str.ToCharArray()

from ch in chrArray orderby ch select ch;

Console.WriteLine("Отдельные символы, отсортированные по порядку:");

// Выполнить запрос и вывести его результаты, foreach(char с in chrs) Console.Write(с + " ");

Console.WriteLine();

}

}

Вот к какому результату приводит выполнение этой программы.

Отдельные символы, отсортированные по порядку: aaaaabeghlmmpt

Обратите внимание в данном примере программы на то, что в операторе let переменной chrArray присваивается ссылка на массив, возвращаемый методом str.

ToCharArray().

let chrArray = str.ToCharArray()

После оператора let переменная chrArray может использоваться в остальных операторах, составляющих запрос. А поскольку все массивы в C# преобразуются в тип IEnumerable, то переменную chrArray можно использовать в качестве источника данных для запроса во втором, вложенном операторе from. Именно это и происходит в рассматриваемом здесь примере, где вложенный оператор from служит для перечисления в массиве отдельных символов, которые затем сортируются по нарастающей и возвращаются в виде конечного результата.

Оператор let может также использоваться для хранения неперечислимого значения. В качестве примера ниже приведен более эффективный вариант формирования запроса в программе IntoDemo из предыдущего раздела.

var webAddrs = from addr in websites

let idx = addr.LastlndexOf('.') where idx != ‑1

group addr by addr.Substring(idx) into ws where ws.Count() > 2 select ws;

В этом варианте индекс последнего вхождения символа точки в строку присваивается переменной idx. Данное значение затем используется в методе Substring (). Благодаря этому исключается необходимость дважды искать символ точки в строке.

Объединение двух последовательностей с помощью оператора join

Когда приходится иметь дело с базами данных, то зачастую требуется формировать последовательность, увязывающую данные из разных источников. Например, в Интернет‑магазине может быть организована одна база данных, связывающая наименование товара с его порядковым номером, и другая база данных, связывающая порядковый номер товара с состоянием его запасов на складе. В подобной ситуации может возникнуть потребность составить список, в котором состояние запасов товаров на складе отображается по их наименованию, а не порядковому номеру. Для этой цели придется каким‑то образом "увязать" данные из двух разных источников (баз данных). И это нетрудно сделать с помощью такого средства LINQ, как оператор join.

Ниже приведена общая форма оператора join (совместно с оператором from).

from переменная_диапазона_А in источник_данных_А join переменная_диапазона_В in источник_данных_В

on переменная_диапазона_А. свойство equals переменная_диапазона_В . свойство

Применяя оператор join, следует иметь в виду, что каждый источник должен содержать общие данные, которые можно сравнивать. Поэтому в приведенной выше форме этого оператора источник_данных_А и источник_данных_В должны иметь нечто общее, что подлежит сравнению. Сравниваемые элементы данных указываются в части on данного оператора. Поэтому если переменная_диапазона_А. свойство и переменная_диапазона_А . свойство равны, то эти элементы данных "увязываются" успешно. По существу, оператор join выполняет роль своеобразного фильтра, отбирая только те элементы данных, которые имеют общее значение.

Как правило, оператор join возвращает последовательность, состоящую из данных, полученных из двух источников. Следовательно, с помощью оператора j о in можно сформировать новый список, состоящий из элементов, полученных из двух разных источников данных. Это дает возможность организовать данные по‑новому.

Ниже приведена программа, в которой создается класс Item, инкапсулирующий наименование товара и его порядковый номер. Затем в этой программе создается еще один класс InStockStatus, связывающий порядковый номер товара с булевым свойством, которое указывает на наличие или отсутствие товара на складе. И наконец, в данной программе создается класс Temp с двумя полями: строковым (string) и булевым (bool). В объектах этого класса будут храниться результаты запроса. В этом запросе оператор join используется для получения списка, в котором наименование товара связывается с состоянием его запасов на складе.

// Продемонстрировать применение оператора join.

using System; using System.Linq;

// Класс, связывающий наименование товара с его порядковым номером, class Item {

public string Name { get; set; } public int ItemNumber { get; set; }

public Item(string n, int inum) {

Name = n;

ItemNumber = inum;

}

}

// Класс, связывающий наименование товара с состоянием его запасов на складе, class InStockStatus {

public int ItemNumber { get; set; } public bool InStock { get; set; }

public InStockStatus (int n, bool b) {

ItemNumber = n;

InStock = b;

}

}

// Класс, инкапсулирующий наименование товара и // состояние его запасов на складе, class Temp {

public string Name { get; set; } public bool InStock { get; set; }

public Temp(string n, bool b) {

Name = n;

InStock = b;

}

}

class JoinDemo {

static void Main() {

Item[] items = {

new Item("Кусачки", 1424), new Item("Тиски", 7892), new Item("Молоток", 8534), new Item("Пила", 6411)

};

InStockStatus[] statusList = {

new InStockStatus(1424, true), new InStockStatus(7892, false), new InStockStatus(8534, true),

new InStockStatus(6411, true)    4

};

// Сформировать запрос, объединяющий объекты классов Item //и InStockStatus для составления списка наименований товаров // и их наличия на складе. Обратите внимание на формирование // последовательности объектов класса Temp, var inStockList = from item in items

join entry in statusList

on item.ItemNumber equals entry.ItemNumber select new Temp(item.Name, entry.InStock);

9

Console.WriteLine("Товар\^Наличие\п");

// Выполнить запрос и вывести его результаты. foreach(Temp t in inStockList)

Console.WriteLine("{0}\t{l}", t.Name, t.InStock);

}‘

Товар    Наличие

Кусачки True Тиски ‑ False Молоток True Пила    True

Для того чтобы стал понятнее принцип действия оператора join, рассмотрим каждую строку запроса из приведенной выше программы по порядку. Этот запрос начинается, как обычно, со следующего оператора from.

var inStockList = from item in items

В этом операторе указывается переменная диапазона item для источника данных items, который представляет собой массив объектов класса Item. В классе Item инкапсулируются наименование товара и порядковый номер товара, хранящегося на складе.

Далее следует приведенный ниже оператор join.

join entry in statusList

on item.ItemNumber equals entry.ItemNumber

В этом операторе указывается переменная диапазона entry для источника данных statusList, который представляет собой массив объектов класса InStockStatus, связывающего порядковый номер товара с состоянием его запасов на складе. Следовательно, у массивов items и statusList имеется общее свойство: порядковый номер товара. Именно это свойство используется в части on/equals оператора join для описания связи, по которой из двух разных источников данных выбираются наименования товаров, когда их порядковые номера совпадают.

И наконец, оператор select возвращает объект класса Temp, содержащий наименование товара и состояние его запасов на складе.

select new Temp(item.Name, entry.InStock);

Таким образом, последовательность результатов, получаемая по данному запросу, состоит из объектов типа Temp.

Рассмотренный здесь пример применения оператора join довольно прост. Тем не менее этот оператор поддерживает и более сложные операции с источниками данных. Например, используя совместно операторы into и join, можно создать групповое объединение, чтобы получить результат, состоящий из первой последовательности и группы всех совпадающих элементов из второй последовательности. (Соответствующий пример будет приведен далее в этой главе.) Как правило, время и усилия, затраченные на полное освоение оператора join, окупаются сторицей, поскольку он дает возможность распознавать данные во время выполнения программы. Это очень ценная возможность. Но она становится еще ценнее, если используются анонимные типы, о которых речь пойдет в следующем разделе.

Анонимные типы

В C# предоставляется средство, называемое анонимным типом и связанное непосредственно с LINQ. Как подразумевает само название, анонимный тип представляет собой класс, не имеющий имени. Его основное назначение состоит в создании объекта, возвращаемого оператором select. Результатом запроса нередко оказывается последовательность объектов, которые составляются из членов, полученных из двух или более источников данных (как, например, в операторе join), или же включают в себя подмножество членов из одного источника данных. Но в любом случае тип возвращаемого объекта зачастую требуется только в самом запросе и не используется в остальной части программы. Благодаря анонимному типу в подобных случаях отпадает необходимость объявлять класс, который предназначается только для хранения результата запроса.

Анонимный тип объявляется с помощью следующей общей формы:

new { имя_А = значение_А, имя_В = значение_В, ...    }

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

new { Count = 10, Max = 100, Min = 0 }

В данном примере создается класс с тремя открытыми только для чтения свойствами: Count, Мах и Min, которым присваиваются значения 10, 100 и 0 соответственно. К этим свойствам можно обращаться по имени из другого кода. Следует заметить, что в анонимном типе используются инициализаторы объектов для установки их полей и свойств в исходное состояние. Как пояснялось в главе 8, инициализаторы объектов обеспечивают инициализацию объекта без явного вызова конструктора. Именно это и требуется для анонимных типов, поскольку явный вызов конструктора для них невозможен. (Напомним, что у конструкторов такое же имя, как и у их класса. Но у анонимного класса нет имени, а значит, и нет возможности вызвать его конструктор.)

Итак, у анонимного типа нет имени, и поэтому для обращения к нему приходится использовать неявно типизированную переменную. Это дает компилятору возможность вывести надлежащий тип. В приведенном ниже примере объявляется переменная шуОЬ, которой присваивается ссылка на объект, создаваемый в выражении анонимного типа.

var myOb = new { Count = 10, Max = 100, Min = 0 }

Это означает, что следующие операторы считаются вполне допустимыми.

Console.WriteLine("Счет равен " + myOb.Count);

if(i <= myOb.Max && i >= myOb.Min) // ...

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

Термин анонимный тип не совсем оправдывает свое название. Ведь тип оказывается анонимным только для программирующего, но не для компилятора, который присваивает ему внутреннее имя. Следовательно, анонимные типы не нарушают принятые в C# правила строгого контроля типов.

Для того чтобы стало более понятным особое назначение анонимных типов, рассмотрим переделанную версию прорраммы из предыдущего раздела, посвященного оператору join. Напомним, что в этой программе класс Temp требовался для инкапсуляции результата, возвращаемого оператором join. Благодаря применению

анонимного типа необходимость в этом классе‑заполнителе отпадает, а исходный код программы становится менее громоздким. Результат выполнения программы при этом не меняется.

// Использовать анонимный тип для усовершенствования // программы, демонстрирующей применение оператора join‑.

using System; using System.Linq;

// Класс, связывающий наименование товара с его порядковым номером, class Item {
1   ...   52   53   54   55   56   57   58   59   ...   97


написать администратору сайта