Використання лямбда-виразів в електронній таблиці в Visual C # (Sharp)

Члени даних worksheeto дуже схожі на оголошені раніше в класі листа, за винятком того, що оголошення готові до використання лямбда-виразів Під цим я маю на увазі, що завжди, коли я хочу оголосити змінну, посилатися на лямбда-вираз, то використовую тип Funco Далі наводиться исхо код для членів даних для зберігання стану осередку, обчислення стану однієї комірки і обчислення стану всіх осередків в стовпці:

BaseType[,] CellState

Func&ltIWorksheet&ltBaseType&gt, int, int, BaseType&gt[,] Cells Func&ltIWorksheet&ltBaseType&gt, int, int, BaseType&gt[] ColCells

Член даних CellState містить стан осередку листа Його тип – BaseType, тобто тип, яким був оголошений тип BaseType Члени даних cells і ColCells обвлени як лямбда-вирази з трьома параметрами і повертається значенням

Але перш ніж продовжити пояснення лямбда-виразів, я хочу трохи откло від теми, щоб проілюструвати одну проблему – як анонімні предмети дають нам одне, але поводяться зовсім по-іншому Розглянемо наступний делат (я використовую делегатів тому, що лямбда-вирази і є делегати, хоча їх синтаксис і не так елегантний),

delegate string WhatAmI()

Цього делегата ми використовуємо в грі визначення загаданого мною тварини, де тип тварини виводиться за допомогою реалізацій анонімних методів Примение анонімного методу дозволяє зухвалому коду НЕ піклуватися про тип жотня, а лише про те, щоб викликаний делегат виводив правильне імя

WhatAmI[] animals = new WhatAmI[2] string animal

animal = cow; animals [0] = delegate Про {

return animal

}

animal = &quothorse"

animals[1] = delegate*) { animal = &quot(&quot + animal + &quot)" return animal

}

Console WriteLine (&quotAnimal 1(&quot + animals [0]O + &quot) 2(&quot + animals [1]0 + &quot)&quot)

У даному прикладі створюється масив делегатів whatAmi, кожному з яких буде присвоєно анонімний метод У першому випадку це метод, який повертає значення змінної animal А в другому – це буфер, де значення змінної animal укладено в лапки У кожному випадку виконується присвоювання нового екземяра анонімного методу

Які, ви думаєте, будуть виведені значення для animal 1 і animal 2 Здавалося б, повинні бути cow і horse, відповідно, чи не так Але от яким буде детвітельний висновок:

Animal 1(horse) 2((horse))

Як бачимо, дійсний висновок відрізняється від того, який ми очікували Це демонструє, що анонімні методи, або лямбда-вирази, які не зберігають стояння Така поведінка є бажаним, т к при виконанні елементів масиву animals [0] і animals [1] вони будуть посилатися на останню версію пеменной animal

Але ситуація істотно ускладнюється, якщо один анонімний метод модіфіцірт змінну, оголошену поза його області видимості Щоб продемонстрірать, що делегат справді не має стану, модифікуємо оператор ConsoleWriteLineО, щоб першою виконує метод animals [l], який модифікує стан animal, і подивимося, який ефект це матиме на висновок:

Animal 2((horse)) 1((horse))

Ми бачимо, що т к с змінної animal були виконані маніпуляції, то перша реалізація анонімного методу бачить модифіковану версію

Але сказати, що анонімні делегати та лямбда-вирази зовсім не мають стану, буде не цілком правильно, т к можна створити анонімний делегат або лямбда-вирази, які не мають стану Для прикладу розглянемо наступну реалізацію анонімного делегата WhatAmi, який инкапсулирован в контексті фабрики:

static class Builder {

public static WhatAmi BuildWhatAml(string animal) { return delegate() {

return animal

}

}

}

На цей раз анонімний делегат створюється в контексті методу BuiidwhatAmio, де тип animal передається як параметр Код масиву, модифікований для використання фабрики, виглядає таким чином:

WhatAmI[] animals = new WhatAmI[2] string animal

animal = &quotcow"

animals[0] = BuilderBuildWhatAmI(animal) animal = &quothorse"

animals[l] = BuilderBuildWhatAmI(animal)

ConsoleWriteLine(&quotAnimal 2(&quot + animals[l]() +

&quot) 1(&quot + animals[0]() + &quot)&quot)

Це такий же код, як і в попередній версії, за винятком виклику класу Builder Щоб більше не тримати вас в напруженому очікуванні, покажемо висновок цього коду:

Animal 2(horse) l(cow)

Можна бачити, що коли анонімний делегат або лямбда-вираз використовується в контексті іншого методу і повертається зухвалому коду, то вони пріобретт стан Таким чином, ми маємо два різних види поведінки, хоча визающій код однаковий в обох випадках

Хоча деякі можуть думати, що така поведінка є неправильним, з тои зору функціонального програмування воно вірно У главі 16 буде рамотрено, як з цього поведінки можна отримати користь

Повернемося до прикладу електронної таблиці Деякі фрагменти його початково коду будуть непрацездатними з причини тільки що описаного поведінки А саме наступний фрагмент коду:

for (int row = 0 row &lt itemsLength row++) {

AssignCellCalculation(row, 1, (worksheet, cellRow, cellCol&quot) =&gt

{

return worksheetGetCellState(row, 0) – worksheetCalculate(itemsLength, 0)

}

)

}

А от із цим фрагментом ніяких проблем немає:

static class Builder {

static Func&ltIWorksheet&ltdouble&gt, int, int, double&gt BuildSubtractFromAverage(int row, double] items) {

return (worksheet, cellRow, cellCol) =&gt {

return worksheetGetCellState(row, 0) – worksheetCalculate(itemsLength,  0)

}

}

for (int row = 0 row &lt itemsLength row++) { AssignCellCalculation(row, 1,

BuilderBuildSubtractFromAverage(row,  items))

}

Різниця між цими двома фрагментами коду полягає в тому, що в одному іользуется лямбда-вираз, оголошене в контексті методу, а в другому – ні Але головна відмінність полягає в реалізації лямбда-вирази або анонімного делегата Як зазначено в чолі 9, в більш ранніх версіях мови С # необхідно було застосовувати метод класу Так от, це вимога залишається в силі і з справжніми версіями мови У даному випадку компілятор С # автоматично створив клас, Сержао методи-делегати У разі анонімного методу whatAmi фактично геріруется наступний код:

[CompilerGenerated) / / Код, згенерований компілятором private sealed class ос DisplayClass2

{

/ / Поля

public string animal

/ / Методи

public &lt&gtc DisplayClass2() public string &ltVariationl&gtb 0()

} public string &ltVariationl&gtb  1()

Згенерований клас має член даних animal, який насправді є змінною, оголошеної в контексті методу Також у ньому є два анонімних методу – ь о () і ь 1 ()

А піді всім цим код присвоює методи класу <> с DispiayClass2 елементів масиву animals Тому, коли кожна реалізація методу маніпулює перенісши animal, вона бачить ту ж саму змінну або член даних

Так чому ж одна версія анонімного методу зберігає стан, в той час як інша ні Відповідь на це питання повязане з реалізацією анонімного методу При оголошенні кожного анонімного методу в контексті методу створюється клас і його екземпляр Тому якщо кілька анонімних методів використовується в контексті методу, то всі вони поділяють один і той же екземпляр класу А коли викликається метод, створює анонімний метод, то екземпляр класу створюється при кожному виклику методу Таким чином, метод BuilderBuiidwhatAmi () створює екземпляр класу з анонімним методом, якій повертається зухвалому коду У всьому

13 Зак 555

цьому може збивати з пантелику те, що анонімний метод виходить створенням екзеляра класу, хоча новий ідентифікатор і не використовується

ПРИМІТКА

Кром е введення узагальненнями й NET, віртуальна машина NET не надто сильно ізмілась, але мова С # піддався досить значним модифікаціям Мета ю багатьох з цих змін є спрощення поширених методів програмування Це означає, що згенерований код буде вести себе подібно коду, який ва м довелося б писати самостійно, якби дана можливість була відсутня

Джерело: Гросс К С # 2008: Пер з англ – СПб: БХВ-Петербург, 2009 – 576 е: ил – (Самовчитель)

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*