в

Kazan Dev Alliance

Казанское Сообщество Разработчиков Программного Обеспечения

Персональный блог Александра Шера

Professional blog for .Net programmers. Main topics: Microsoft ASP.NET AJAX, WPF/E, OOP, Refactoring

Знакомство со Script# - часть 3: используем foreach и создаем свои события

Первые попытки использовать выражение foreach в Script# могут привести к генерации JS-скрипта, который не будет работать. Кроме того, в документации указано, что цикл foreach нельзя использовать с массивами, поскольку они не реализуют интерфейс IEnumerable. Тем не менее, реализация foreach в Script# полностью покрывает функционал выражения for..in в JS. Нужно лишь правильно им пользоваться.

Итак, сперва рассмотрим различия между конструкциями foreach в С# (ведь Script# - это просто компилятор для C#) и for..in в JS.

Во-первых, коду на C#

foreach (object obj in col) {

    object val = obj;

}

соответствует код на JS

for (var key in col) {

    var val = col[key];

}

Т.е., если в C# foreach перечисляет элементы коллекции, то в JS for..in перечисляет строковые ключи.

Во-вторых, foreach в C# может работать только с объектами, реализующими интерфейс IEnumerable, а for..in в JS позволяет пройтись по всем пользовательским членам экземпляра класса.

Теперь разберемся с тем, как for..in в JS работает с массивами. Вот небольшой пример JS-кода:

var a = [1, 2, 3];

var s1 = 0;

var s2 = 0;

for (var i = 0; i < a.length; i++) {

    s1 += i;

}

for (var i in a) {

    s2 += i;

}

alert("sum1: "+s1);

alert("sum2: "+s2);

Результат его выполнения, возможно, кого-то удивит. По идее, s1 и s2 должны быть равны. Однако, в первом случае i – это число, а во втором – строка. Таким образом, внутри конструкции for..in массив (так же как и любой другой JS-объект) воспринимается как словарь, для которого ключом является строка (а не число).

Учитывая все вышенаписанное, принципы использования foreach в Script# выглядят вполне логичными.

В пространстве имен System есть класс Dictionary, который реализует интерфейс IEnumerable, что позволяет использовать его в foreach, а также содержит статический метод GetDictionary, с помощью которого можно получить «Dictionary-обертку» для любого объекта. При генерации JS-кода он преобразуется в Object, так что никаких отдельных классов не создается.

В качестве элемента Dictionary выступает тип DictionaryEntry, почти что точная копия своего «однофамильца» из FCL, с тем лишь отличием, что свойство Key имеет тип string, а не object:

[imported]

public sealed class DictionaryEntry {

    internal DictionaryEntry();

 

    [IntrinsicProperty]

    public string Key { get; }

    [IntrinsicProperty]

    public object Value { get; }
}

В результате, код на Script#:

foreach (DictionaryEntry entry in Dictionary.GetDictionary(obj)) {

    string key = entry.Key;

    object value = entry.Value;

}

будет преобразован в такой JS-код:

var $dict1 = obj;

for (var $key2 in $dict1) {

    var entry = { key: $key2, value: $dict1[$key2] };

    var key = entry.key;

    var value = entry.value;
}

Таким образом, использование foreach вместе с классами Dictionary и DictionaryEntry полностью покрывает функционал цикла for..in.

Теперь перейдем ко второму вопросу – создание собственных событий. Как уже было сказано ранее, из-за необходимости обеспечивать обратную совместимость, к типу Object не было добавлено никаких полей и методов, кроме статических. Т.е. информацию о событиях хранить по умолчанию негде. Поэтому, чтобы создать свое событие, нужно сперва добавить поле-хранилище. Для этого в MS AJAX Library существует тип Sys.EventHandlerList:

using Sys;

namespace Samples {

    public class ClassWithEvent {

        private EventHandlerList events;

Теперь мы можем определить событие:

        private const string SampleEventKey = "EventKey";

        public event EventHandler SampleEvent {

            add {

                events.AddHandler(SampleEventKey, value);

            }

            remove {

                events.RemoveHandler(SampleEventKey, value);

            }
        }

и метод для его вызова:

        protected virtual void OnSampleEvent (EventArgs e) {

            EventHandler ev = (EventHandler)events.GetHandler(SampleEventKey);

            if (ev != null) {

                ev(this, e);
            }
        }

Естественно, вместо EventArgs и EventHandler можно использовать любые другие классы, в том числе и свои (при этом класс аргументов будет сгенерирован в JS, а класс делегата – нет, поскольку в Microsoft AJAX Library делегаты не строго типизированные).

Комментарии

Нет комментариев

Оставить комментарий

(required)  
(optional)
(required)  

© 2007 Kazan Developers Community and Post`s Authors