Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
DiVM / OSISP / ОCиСП-Часть3 / Теория / Теория (ОСиСП).doc
Скачиваний:
29
Добавлен:
11.05.2015
Размер:
616.96 Кб
Скачать
  1. В с# в шаблонах введено понятие итератора.

Пример (используем оператор foreach)

List list = new List( );

foreach (object obj in list)

{

DoSomething(obj);

}

Компилятор физически превращает это в следующее:

ListEnumerator e = list.GetEnumerator( );

while (e.Movenext())

{

object obj = e.Current;

DoSomething(obj);

}

C# позволяет создавать классы, к которым применим цикл foreach. Для того чтобы создать класс, по которому можно итерировать надо в классе реализовать интерфейс IEnumerable таким образом, чтобы возвращался экземпляр класс Enumerator, в котором должны быть реализованы методы MoveNext() и Current.

Использование класса List весьма простое. Но достаточно много нужно программировать, чтобы реализовать ListEnumerator (ListEnumerator – наследник Enumerator’а)

В классе List надо реализовать метод GetEnumerator.

В .Net 2.0 для foreach более удобный синтаксис и другая семантика:

public class List

{

object [] elements;

int count;

public object foreach()

{

foreach (object obj in elements)

{

yield obj; // yield – вытолкнуть, отдать. См. далее (*)

}

}

}

Надо запрограммировать foreach() (т.е. перегрузить этот оператор) для класса, который использует обычный оператор foreach.

При определении оператора foreach появляется возможность использовать оператор yield, который приостанавливает метод и передаёт элемент в точку вызова.

Оператор yield обеспечивает «переход» на:

List list = new List();

foreach (object obj in list)

{

DoSomething(obj); // (*)

}

public class List

{

object [] elements;

int count;

public object foreach()

{

b=0;

yield elements [0]; ++b; // (2)

yield elements [1]; ++b;

}

}

List list = new List();

int a=0;

foreach (object obj in list) // (1)

{

DoSomething(); // это анонимный делегат, который получает в

// качестве аргумента объект obj

}

Цикл foreach реализуется с помощью анонимного делегата. Тело делегата, сам делегат объявляются в том месте, где используется делегат.

Оператор yield – это вызов анонимного делегата, и после вызова yield осуществляется обратный переход.

Компилятор автоматически отслеживает, какие переменные используются внутри цикла, и создаёт специальный невидимый класс, который содержит весь контекст метода. Контекст заполняется значениями (у нас - одной переменной a=0).

Когда вызовем (1), то произойдёт переход в (2), куда передаётся контекст и ещё что-то. Это что-то превращается в следующее:

class Context

{

int a;

}

Context ctx = new Context() // перед началом класса for создаётся объект

ctx.a = a;

list.foreach(new delegate (object o, context ctx))

{

DoSomething(0); // указываем тело метода

ctx.a++;

}

a = ctx.a;

Это что-то превратится в следующее:

public class List

{

void delegate ItemHandler(object item, object ctx)

public void foreach (ItemHandler d, object ctx)

{

foreach (object obj in elements)

{

d(obj, ctx); // вызов текущего объекта с контекстом

}

}

}

При работе с БД возможны следующие проблемы:

1. Использование языка SQL, отличающегося от языков программирования. Одно из направлений – приведение в соответствие типов данных языков программирования и типов SQL.

Введен тип данных

Nullablle<T>

{

T Value;

bool HasValue;

}

Переменная этого типа объявлена как:

Nullable<int> x;

В памяти представляет собой следущее

int Value

bool HasValue


Такое объявление даёт возможность присваивать переменной Х целочисленное значение, а также значение NULL:

x.HasValue=false; // присваивание значение NULL

Изменив язык, можно будет писать следующим образом:

int?x=null;

? –используется для указания того, что Nullable – тип. Компилятор превращает это в следующее:

Nullable<int>x = new Nullable <int> (false);

Класс Nullable является размерным типом.

При объявлении int?[ ] arr в памяти будет следующее:

int

int

bool

bool

Если выполняются операции с такими переменными:

Int?x =5;

Int?y =null;

Int?z = x+y // 1

В этом случае компилятор превращает строчку 1 в следующее:

z = x.HasValue && y.HasValue ? x.Value + y.Value :null

if then else

В этом случае в z будет null.

В современных БД есть признак того, что поле не может быть нулевым (null). Null запрещено как значение (т.е. идентификатор будет всегда указывать на корректную запись в таблице (другой), но каждая таблица должна иметь как минимум одну запись, которую нельзя удалить).

Для размерных типов применимо понятие null, и можно сказать, что для ссылочных понятий применимо понятие не-null. Т.е.:

Control p = default Control; // null

Т.е. для не-null указателей не проверяется (???). Тогда можно писать:

Control p = default Control;

Но ещё можно записать и так:

Control & p = default; // 2

И благодаря не-null ссылкам следующий код является безопасным:

void DoSomething (Control & p)

{

p.Do(); // надо раньше было писать if(p!=null)p.Do

}

default-объекты создаются автоматически при загрузке модуля и создаются в специальной области памяти. Эта область памяти логически считается мусором, который никогда не собирается сборщиком. Если указатель ссылается в эту область памяти, то для д.с. Это равносильно тому, что он равен null.

1. Если default-объект содержит поле, то это ссылка на самого себя.

2. Т.к. указатели всегда инициализированы default-объектом, то (???) содержит информацию о типах.

Если указатель ссылочного типа указ на объект, и применима функция typedef, и не надо проверки указателя на null. Если указатель указывает на стандартный объект, то возвратится тип (?). Например, будет возвращен Control.

Указатель всегда является валидным (действительным). По нему можно обратиться для указанного типа объекта. Если это Сontrol, то

p=GetButton();

p.Type( )->Button // вернётся Button

Указатель p можно передать в процедуру, принимающую object. Это позволяет создавать виртуальные конструкторы, которые вызываются в зависимости от типа объекта:

Construct (A); // вызовет процедуру Construct

void Construct( Object p)

{

typeof(p); // тип вернётся Control

p.MemberviseClone(); // это можно потом вызвать

clone p; // создаётся клон объекта

}

(это всё, для того чтобы не проверять указатели на null ).

Объявленные с помощью 2 объекты никогда не надо проверять на null. Для них всегда можно использовать Clone.

Если у нас есть список, то раньше, когда указатели могли иметь значение null, то null – это признак конца списка. Теперь признак конца – это default-объект. Если есть ошибка при прохождении списка, то раньше был exception, а теперь будет зацикливание.