Была задача описать источники данных и категории данных в этих источниках
и связать все это с метками описывающие все это в каком-либо внешнем файле...
Плюс тут в некоторой степени решена проблема (как мне кажется)
уже звучавшая на этом форуме о перекодировке значений одного enum-а
в значения другого enum-а. Более удобная чем то что там предлагали.
Необходимо всего лишь добавить Extension метод с нужной функциональностью.
using System;
using System.Console;
using Nemerle.Collections;
using Nemerle.DesignPatterns;
// Описываем два enum с атрибутами источника и сатегорий
[SourceLabel("CK")]
public enum CK
{
| Unknown
[CategoryLabel("TI")]
| TI
[CategoryLabel("TS")]
| TS
[CategoryLabel("AVG")]
| Average
[CategoryLabel("INST")]
| Instant
[CategoryLabel("INTG")]
| Integral
[CategoryLabel("DAY")]
| Days
[CategoryLabel("PLAN")]
| Plan
[CategoryLabel("TIF")]
| TIF
[CategoryLabel("UHI30")]
| UHI30
[CategoryLabel("UHIDAY")]
| UHIDay
}
[SourceLabel("INGIS")]
public enum INGIS
{
| Unknown
[CategoryLabel("I")]
| Izm
[CategoryLabel("S")]
| Sum
[CategoryLabel("DI")]
| DayIzm
[CategoryLabel("DS")]
| DaySum
[CategoryLabel("MI")]
| MonthIzm
[CategoryLabel("MS")]
| MonthSum
}
// и третий для примера и сравнения что все у нас работает правильно
public enum Check
{
| Checked
| Unchecked
}
// Класс Аттрибут для описания источника данных
// + процедура для извлечения этого аттрибута из всех enum-ов
public class SourceLabelAttribute : Attribute
{
private _label: string;
public this(label: string) {_label = label;}
public Label: string {get{_label;}}
// Сама процедура сделана как Экстеншион метод, т.е. добавляет функциональность ко всем Enum-ам
// самый обыкновенный рефлекшн ничего особенного, единственно что интересно - передача функции в Array.Find
public static GetSourceLabel(this enumObj: Enum): string
{
def attribList = enumObj.GetType().GetCustomAttributes(false);
def attrib = Array.Find(attribList,_ is SourceLabelAttribute);
if (attrib != null)
(attrib:>SourceLabelAttribute).Label
else
String.Empty
}
}
// Класс Аттрибут для описания категорий данных
// + процедура для извлечения этого аттрибута из всех enum-ов
// Отличаеться от предыдушего тем что кастомный атрибут береться не от типа, а от поля этого типа
// и немного другая процедура возврашения категории данных
public class CategoryLabelAttribute : Attribute
{
private _label: string;
public this(label: string) {_label = label;}
public Label: string {get {_label;}}
public static GetCategoryLabel(this enumObj: Enum): string
{
def attribList = enumObj.GetType().GetField(enumObj.ToString()).GetCustomAttributes(false);
def attrib = Array.Find(attribList,_ is CategoryLabelAttribute);
if (attrib != null)
String.Join("_",array[enumObj.GetSourceLabel(),(attrib:>CategoryLabelAttribute).Label])
else
String.Join("_",array[enumObj.GetSourceLabel(),"Unknown"])
}
}
// Нечтно вроде перекодировочной таблицы между метками источников и категории и самим типом enum-ов
// т.е. была нужна следующая функциональность - парсится какая-то таблица с метками источников и категорий данных
// На основе меток создаються Enum-ы, которые затем передаються в виде параметров в классы непосредственно хранящие
// извлеченные данные и работающие с этими данными
public module Category_Label
{
// для перекодировки храним связку трех значений
// имя типа Enum-а - чтобы не добавлять повторных типов
// непосредственно сам Enum
// и соотвествующую ему метку с источником и категорией данных
// можно было бы сделать и через Hashtable,
// но там был сложнее поиск существования источника данных (хотя надо будет еще раз попробовать)
private mutable l_LSC: list[string*Enum*string] = [];
// процедура регистрации типов с источниками и категориями данных
public RegSourceEnum(ti: Type): void
{
def ContainSource(sourcename)
{
l_LSC.Exists(fun(x,_,_){x == sourcename})
}
def GetLSC(en)
{
(en.GetSourceLabel(),en,en.GetCategoryLabel())
}
// необходимые проверки на правильность переданного типа
when (ti == null) throw ArgumentNullException("Должен быть передан тип");
when (!ti.IsEnum) throw ArgumentException("Должен быть указан Enum с категориями данных");
// здесь, если тип не помечен аттрибутов истоника, можно кинуть исключение, а можно просто ничего не делать
when (Array.Find(ti.GetCustomAttributes(false),_ is SourceLabelAttribute) == null) {}
// throw ArgumentException("Enum должен быть помечен аттрибутом [SourceLabel(""___"")]");
// Если типа еще нет в перекодировочной таблице - то добавляем все его категории
when (!ContainSource(ti.GetType().Name))
// пришлось делать через foreach
// потому что не через Array.ForEach ни через какие либо другие функции работы с групповыми операциями
// сделать подобное не удалось, либо выглядело это намного длиннее и менее понятно
// может кто-то предложет способ сделать это получше - хотя мне кажеться и так замечательно
foreach(val:>Enum in Enum.GetValues(ti))
l_LSC = GetLSC(val)::l_LSC
}
// Просто для отладки или еще для чего может пригодиться
// например чтобы где-нить вывести все возможные категории и источники данных и соотвествующие им метки
public CategoryLabelList: list[Enum*string]
{
get
{
l_LSC.FoldLeft([],fun((_,b,c),y){(b,c)::y})
}
}
// Получаем Enum с категорией по метке - основная задача перекодировочной таблицы
public CategoryByLabel[T](label: string): T where T: enum
{
// ищем метку и возвращаем что надо
match (l_LSC.Find(fun(a,_,b){b == label && a == typeof(T).Name}))
{
| Some((_,x,_)) => x:>T
| _ => 0:>T
}
}
// Обратная задача - по Enum-у получаем метку
// почему нельзя сделать сразу cat.GetCategoryLabel()
// потому что желательно получать метку только от зарегестрированных типов
public LabelByCategory[T](cat: T): string where T: enum
{
// ищем Enum и возврщаем метку
match(l_LSC.Find(fun(_,y,_){y.Equals(cat)}))
{
| Some((_,_,x)) => x
| _ => String.Empty
}
}
}
public module MainApp
{
Main(): void
{
// По моему второй вариант проще и в понимании и в написании
WriteLine(SourceLabelAttribute.GetSourceLabel(CK.TI));
WriteLine(CK.Unknown.GetSourceLabel());
WriteLine(INGIS.Izm.GetSourceLabel());
// Проверка enum-а с отсутствующим аттрибутом
WriteLine(Check.Checked.GetSourceLabel());
WriteLine(CategoryLabelAttribute.GetCategoryLabel(CK.Average));
WriteLine(CategoryLabelAttribute.GetCategoryLabel(INGIS.DaySum));
WriteLine(CK.TI.GetCategoryLabel());
WriteLine(CK.Unknown.GetCategoryLabel());
// Проверка enum-а с отсутствующим аттрибутом
WriteLine(Check.Unchecked.GetCategoryLabel());
// Регистрируем типы
Category_Label.RegSourceEnum(typeof(CK));
Category_Label.RegSourceEnum(typeof(INGIS));
// Две следующих регистрации будут проигнорированы
Category_Label.RegSourceEnum(typeof(INGIS));
Category_Label.RegSourceEnum(typeof(Check));
mutable cat1: CK;
mutable cat2: INGIS;
mutable str: string;
// Различные виды перекодировки
cat1 = Category_Label.CategoryByLabel("CK_TI");
str = Category_Label.LabelByCategory(cat1);
WriteLine($"$cat1 - $str");
cat1 = Category_Label.CategoryByLabel("CK_AVG");
str = Category_Label.LabelByCategory(cat1);
WriteLine($"$cat1 - $str");
// Пытаемся вернуть тип СК по метке от типа ИНГИС
// Осуществляем какой-никакой контроль типов
// т.е. возвращаем CK.Unknown
cat1 = Category_Label.CategoryByLabel("INGIS_I");
str = Category_Label.LabelByCategory(cat1);
WriteLine($"$cat1 - $str");
cat2 = Category_Label.CategoryByLabel("INGIS_I");
str = Category_Label.LabelByCategory(cat2);
WriteLine($"$cat2 - $str");
cat2 = Category_Label.CategoryByLabel("INGIS_MS");
str = Category_Label.LabelByCategory(cat2);
WriteLine($"$cat2 - $str");
cat2 = Category_Label.CategoryByLabel("INGIS_Unknown");
str = Category_Label.LabelByCategory(cat2);
WriteLine($"$cat2 - $str");
// Аналогично вышесказанному для несуществующей метки
cat2 = Category_Label.CategoryByLabel("Sasha");
str = Category_Label.LabelByCategory(cat2);
WriteLine($"$cat2 - $str");
// Для справки выводим все зарегистрированные метки и категории
WriteLine(Category_Label.CategoryLabelList)
}
}
Какие задачи не удалось решить и почему:
1. Хотелось бы иметь (не мне
) перекодировки в виде проперти...
Проблема: У статических классов не может быть индексированных проперти..
Попытался сделать Singleton — получилось, но не совсем...
ибо нельзя сделать типизированный проперти.. (или это просто я не знаю как это делаеться)
т.е. такое невозможно
public LabelByCategory[T][cat: T]: string where T: enum
Хотя надо еще попробовать такое:
public LabelByCategory.[T][cat: T]: string where T: enum
Но что-то мне подсказывает что и такое не прокатит...
30.01.07 18:28: Перенесено модератором из 'Декларативное программирование' — IT