Не будучи знакомым с OCaml или Haxe и не будучи достаточно умным, чтобы понять другие объяснения, я пошел и посмотрел документацию по перечислению Haxe - бит «Параметры типа перечисления» внизу кажется быть соответствующей частью.
Мое понимание основано на следующем:
«Обычное» перечисление - это, по сути, значение, которое ограничено вещами, которые вы определили в своем определении перечисления. Пример C #:
enum Color{ Red, Green, Yellow, Blue };
Color c = Color.Red;
c
может быть Red
, Green
, Yellow
или Blue
, но не более.
В Haxe вы можете добавлять сложные типы к перечислениям, например, на странице Contrived:
enum Cell<T>{
empty;
cons( item : T, next : Cell<T> )
}
Cell<int> c = <I don't know>;
То, что это выглядит , означает, что c
ограничено либо литеральным значением empty
(как наши старомодные перечисления C #), либо это может быть также сложный тип cons(item, next)
где item
является T
и next
является Cell<T>
.
Даже не используя это, похоже, что он генерирует некоторые анонимные типы (например, как это делает компилятор C #, когда вы делаете new { Name='Joe'}
.
Всякий раз, когда вы «обращаетесь» к значению перечисления, вы должны объявлять item
и next
, когда делаете это, и похоже, что они привязываются к временным локальным переменным.
Пример Haxe - вы можете видеть, что next используется как временная локальная переменная для извлечения данных из анонимной структуры cons:
switch( c ) {
case empty : 0;
case cons(item,next): 1 + cell_length(next);
}
Честно говоря, это поразило меня, когда я «нажал» на то, что, казалось, делал. Это кажется невероятно мощным, и я понимаю, почему вы искали подобную функцию в C #.
Перечисления C # во многом совпадают с перечислениями C / ++, из которых они были изначально скопированы. По сути, это хороший способ сказать #define Red 1
, так что компилятор может выполнять сравнения и хранение с целыми числами вместо строк, когда вы передаете Color
объекты вокруг.
Моя попытка сделать это в C # будет состоять в использовании обобщений и интерфейсов. Примерно так:
public interface ICell<T> {
T Item{ get; set; }
ICell<T>{ get; set; }
}
class Cons<T> : ICell<T> {
public T Item{ get; set; } /* C#3 auto-backed property */
public Cell<T> Next{ get; set; }
}
class EmptyCell<T> : ICell<T>{
public T Item{ get{ return default(T); set{ /* do nothing */ }; }
public ICell<T> Next{ get{ return null }; set{ /* do nothing */; }
}
Тогда у вас может быть List<ICell<T>>
, который будет содержать элементы и следующую ячейку, и вы можете вставить EmptyCell
в конце (или просто указать ссылку Next
, явно установленную на ноль).
Преимущества заключаются в том, что, поскольку EmptyCell
не содержит переменных-членов, для него не потребуется никакого пространства для хранения (например, empty
в Haxe), тогда как ячейка Cons
будет.
Компилятор также может встроить / оптимизировать методы в EmptyCell
, так как они ничего не делают, поэтому может быть увеличение скорости по сравнению с просто Cons
с его данными члена, установленными в нуль.
Я действительно не знаю. Я приветствовал бы любые другие возможные решения, поскольку я не особенно горжусь своим: -)