Первые попытки использовать выражение 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 {
<