Русские Блоги
TypeConverter — герой интерпретатора XAML. Он проделал большую работу. С момента рождения WPF он участвовал почти во всех операциях XAML. Хотя TypeConverter в UWP полностью отступил за кулисы, даже настраиваемый TypeConverver невозможен, но понимание принципов TypeConverter всегда помогает понять, как работает синтаксический анализатор XAML.
2. TypeConverter в .Net
TypeConverter существовал в ранней версии .NET. Он может преобразовывать значения одного типа в другие типы. Обычно используется для преобразования между типами данных и строками.
Предположим, вы хотите реализовать функцию GetValue, которая преобразует строку в целевой тип:
У этой функции много очевидных проблем: избыточность кода, не так много типов поддержки, сложность в обслуживании, несоответствие принципу открытого и закрытого и т. Д. использовать Convert.ChangeType Код можно реорганизовать следующим образом:
С помощью всего одной строчки кода это выглядит идеально. Но если подумать об этом дальше, аннотация класса Convert — это «преобразование одного базового типа данных в другой базовый тип данных». Другими словами, он поддерживает только базовые типы. Фактически, исходный код функции ChangeType является только расширенной версией GetValue, описанной выше:
Для решения этой проблемы используется типичный сценарий приложения TypeConverter. Используйте TypeConverter, чтобы восстановить эту функцию следующим образом:
TypeConverter GetConverter(Type type) Верните TypeConverter указанного типа. Этот метод может выполнять поиск соответствующего TypeConverterAttribute. Если TypeConverterAttribute не найден, код просматривает иерархию базового класса класса, пока не найдет примитивный тип. При использовании TypeConverter не нужно беспокоиться о слишком малом количестве типов данных, которые можно преобразовать.В BCL реализовано большое количество классов, наследующих TypeConverter, что в основном встречается в повседневной жизни. В дополнение к этим реализованным TypeConverter вы также можете реализовать свой собственный TypeConverter без каких-либо проблем с масштабируемостью.
Стоит отметить, что если используется неправильная строка, Convert.ChangeType только запрашивает «неправильный формат входной строки». Сообщение об ошибке TypeConverter более подробно: «a не является допустимым значением для Decimal».
3. TypeConverter в WPF
XAML — это, по сути, XML, и все содержимое атрибутов является строками. Если тип соответствующего атрибутаВстроенные типы XAML(То есть Boolea, Char, String, Decimal, Single, Double, Int16, Int32, Int64, TimeSpan, Uri, Byte, Array и другие типы), синтаксический анализатор XAML напрямую преобразует строку в соответствующее значение и присваивает его атрибуту; для других типов, Парсеру XAML требуется больше работы.
Например, «Auto» и «*» в приведенном выше XAML, синтаксический анализатор XAML проанализирует их на GridLength.Auto и new GridLength (1, GridUnitType.Star) и назначит им высоту, что эквивалентно этому коду:
Чтобы выполнить эту работу, синтаксическому анализатору XAML требуется помощь TypeConverter. Парсер XAML находит TypeConverter в два этапа:
1. Проверьте TypeConverterAttribute в объявлении атрибута.
2. Если в объявлении атрибута нет атрибута TypeConverterAttribute, проверьте атрибут TypeConverterAttribute в объявлении типа.
Приоритет TypeConverterAttribute в объявлении атрибута выше, чем у объявления типа. Если атрибут TypeConverterAttribute, соответствующий типу, не может быть найден на двух вышеупомянутых шагах, синтаксический анализатор XAML сообщит об ошибке: значение атрибута «*» недопустимо. После обнаружения TypeConverter, указанного в TypeConverterAttribute, анализатор XAML вызывает его object ConvertFromString(string text) Функция преобразует строку в значение атрибута.
В WPF есть много встроенных TypeConverter, но иногда вам все равно нужно настраивать TypeConverter.Один тип трудно построить непосредственно из строки, а другой — для упрощения XAML.
Допустим, есть три класса Email, Receiver, ReceiverCollection, структура следующая:
Код для создания объекта электронной почты и заполнения списка получателей в XAML выглядит следующим образом:
Синтаксис настолько сложен, что вам нужно подумать о настройке ReceiverCollectionConverter прямо сейчас. Основные шаги по настройке TypeConverter следующие:
- Создайте класс, унаследованный от TypeConverter;
- Перегрузка virtual bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType);
- Перегрузка virtual bool CanConvertTo(ITypeDescriptorContext context, Type destinationType);
- Перегрузка virtual object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value);
- Перегрузка virtual object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType);
- Используйте TypeConverterAttribute, чтобы указать TypeConverter, доступный для анализатора XAML;
код показан ниже:
В результате приведенный выше XAML можно упростить до единого кода:
В дополнение к TypeConverterAttribute может быть объявлен в типе, он также может быть объявлен в атрибуте, и объявление атрибута имеет более высокий приоритет:
4. TypeConverter в UWP
TypeConverter полностью отступил за кулисами UWP. Чтобы добиться упрощенного эффекта XAML от ReceiverCollectionConverter, описанного выше, вы можете использоватьCreateFromStringAttribute(Доступно после обновления Aniverssary (14393), но кажется, что об ошибках часто сообщают. Лучше напрямую обновить до Creators Update (15063)):
Эффект CreateFromStringAttribute аналогичен TypeConverterAttribute, но его можно использовать только для классов, но не для атрибутов. Даже если эта схема компенсации предусмотрена, невозможность настроить TypeConverter по-прежнему оказывает большое влияние на UWP. UWP имеетВнутренние типы данных XAMLКонцепция (то есть типы данных, которые могут использоваться непосредственно в XAML) содержит только Boolean, String, Double, Int32, а встроенного TypeConverter очень мало, что приводит к отсутствию поддержки decimal.
Интересно то, что панель свойств Visual Studio наивно думала, что она поддерживает прямой ввод Decimal, и даже представление дизайна может отображаться нормально, но сообщается об ошибках компиляции. Ссылаясь на пакет NuGet System.ComponentModel.TypeConverter, можно добавить даже TypeConverterAttribute, но этот атрибут не имеет фактического эффекта.
Когда вы видите сообщение об ошибке, как показано на рисунке выше, можно понять, что UWP не имеет соответствующего TypeConverter и может присваивать значения только атрибутам в CodeBehind. Если вам необходимо присвоить десятичное значение в XAML, вы можете использовать Binding.
5. Вывод
Поскольку в статье о локализации упоминается TypeConverter, бывает, что TypeConverter используется в текущей работе, поэтому я хотел написать статью, чтобы представить эту концепцию. Оказывается, что TypeConverter UWP нельзя использовать напрямую. Эта концепция очень важна для понимания анализатора XAML. Это содержание WPF для обсуждения.
15.9.1. Конвертеры
Конвертер – это особый случай функции-члена класса, реализующий определенное пользователем преобразование объекта в некоторый другой тип. Конвертер объявляется в теле класса путем указания ключевого слова operator , за которым следует целевой тип преобразования.
Имя, находящееся за ключевым словом, не обязательно должно быть именем одного из встроенных типов. В показанном ниже классе Token определено несколько конвертеров. В одном из них для задания имени типа используется typedef tName , а в другом – тип
typedef char *tName; class Token < public:
Token( char *, int );
// другие открытые члены private:
SmallInt val; char *name;
Обратите внимание, что определения конвертеров в типы SmallInt и int одинаковы. Конвертер Token::operator int() возвращает значение члена val . Поскольку val
имеет тип SmallInt , то неявно применяется SmallInt::operator int() для преобразования val в тип int . Сам Token::operator int() неявно употребляется компилятором для преобразования объекта типа Token в значение типа int . Например,
С++ для начинающих
этот конвертер используется для неявного приведения фактических аргументов t1 и t2
void print( int i )
cout << «print( int ) : » << i << endl;
Token t1( «integer constant», 127 );
Token t2( «friend», 255 );
типа Token к типу int формального параметра функции print() :
print( int ) : 127
После компиляции и запуска программа выведет такие строки:
print( int ) : 255
Общий вид конвертера следующий:
где type может быть встроенным типом, типом класса или именем typedef . Конвертеры, в которых type – тип массива или функции, не допускаются. Конвертер должен быть функцией-членом. В его объявлении не должны задаваться ни тип возвращаемого
operator int( SmallInt & );
// ошибка: задан тип возвращаемого значения
int operator int();
operator int( int = 0 );
// ошибка: задан список параметров
значения, ни список параметров:
Конвертер вызывается в результате явного преобразования типов. Если преобразуемое значение имеет тип класса, у которого есть конвертер, и в операции приведения указан тип этого конвертера, то он и вызывается:
С++ для начинающих
Token tok( «function», 78 );
// функциональная нотация: вызывается Token::operator SmallInt()
SmallInt tokVal = SmallInt( tok );
// static_cast: вызывается Token::operator tName()
char *tokName = static_cast< char * >( tok );
У конвертера Token::operator tName() может быть нежелательный побочный эффект. Попытка прямого обращения к закрытому члену Token::name помечается компилятором как ошибка:
char *tokName = tok.name; // ошибка: Token::name — закрытый член
Однако наш конвертер, разрешая пользователям непосредственно изменять Token::name , делает как раз то, от чего мы хотели защититься. Скорее всего, это не годится. Вот,
Token tok( «function», 78 );
char *tokName = tok; // правильно: неявное преобразование
например, как могла бы произойти такая модификация:
*tokname = ‘P’; // но теперь в члене name находится Punction!
Мы н амереваемся разрешить доступ к преобразованному объекту класса Token только
typedef const char *cchar; class Token <
// ошибка: преобразование char* в const char* не допускается char *pn = tok;
для чтения. Следовательно, конвертер должен возвращать тип const char* : const char *pn2 = tok; // правильно
Другое решение – заменить в определении Token тип char* на тип string из стандартной библиотеки C++:
С++ для начинающих
Token( string, int );
// другие открытые члены private:
SmallInt val; string name;
Семантика конвертера Token::operator string() состоит в возврате копии значения (а не указателя на значение) строки, представляющей имя лексемы. Это предотвращает случайную модификацию закрытого члена name класса Token .
Должен ли целевой тип точно соответствовать типу конвертера? Например, будет ли в
extern void calc( double ); Token tok( «constant», 44 );
// Вызывается ли оператор int()? Да
// применяется стандартное преобразование int —> double
следующем коде вызван конвертер int() , определенный в классе Token ?
Если целевой тип (в данном случае double ) не точно соответствует типу конвертера (в нашем случае int ), то конвертер все равно будет вызван при условии, что существует последовательность стандартных преобразований, приводящая к целевому типу из типа конвертера. (Эти последовательности описаны в разделе 9.3.) При обращении к функции calc() вызывается Token::operator int() для преобразования tok из типа Token в
тип int . Затем для приведения результата от типа int к типу double применяется стандартное преобразование.
Вслед за определенным пользователем преобразованием допускаются только стандартные. Если для достижения целевого типа необходимо еще одно пользовательское преобразование, то компилятор не применяет никаких преобразований. Предположим, что в классе Token не определен operator int() , тогда следующий вызов будет
extern void calc( int ); Token tok( «pointer», 37 );
// если Token::operator int() не определен,
// то этот вызов приводит к ошибке компиляции
Если конвертер Token::operator int() не определен, то приведение tok к типу int потребовало бы вызова двух определенных пользователем конвертеров. Сначала фактический аргумент tok надо было бы преобразовать из типа Token в тип SmallInt с помощью конвертера
Какие есть виды детекта типов у конвертера
В предыдущих темах было рассмотрено создание элементов в XAML. Например, мы могли бы определить кнопку следующим образом:
С помощью атрибутов мы можем задать различные свойства кнопки. Height и Width являются простыми свойствами. Они хранят числовое значение. А например, свойства HorizontalAlignment или Background являются более сложными по своей структуре. Так, если мы будем определять эту же кнопку в коде c#, то нам надо использовать следующий набор инструкций:
Чтобы выровнять кнопку по центру, применяется перечисление HorizontalAlignment, а для установки фонового цвета — класс SolidColorBrush. Хотя в коде XAML мы ничего такого не увидели и устанавливали эти свойства гораздо проще с помощью строк: Background=»Red» . Дело в том, что по отношению к коду XAML применяются специальные объекты — type converter или конвертеры типов, которые могут преобразовать значения из XAML к тем типам тех объектов, которые используются в коде C#.
В WPF имеются встроенные конвертеры для большинства типов данных: Brush, Color, FontWeight и т.д. Все конвертеры типов явлются производными от класса TypeConverter . Например, конкретно для преобразования значения Background=»Red» в объект SolidColorBrush используется производный класс BrushConverter . При необходимости можно создать свои конвертеры для каких-то собственных типов данных.
Фактически установка значения в XAML Background=»Red» сводилась бы к следующему вызову в коде c#:
В данном случае программа пытается получить конвертер для типа Brush (базового класса для SolidColorBrush) и затем преобразовать строку «Red» в конкретный цвет. Для получения нужного конвертера, программа обращается к метаданных класса Brush. В частности, он имеет следующий атрибут:
Данный атрибут и позволяет системе определить, какой тип конвертера использовать.
В то же время мы можем более явно использовать эти объекты в коде XAML:
Преимуществом такого подхода является то, что у объектов мы можем установить дополнительные параметры.
Конвертеры данных
Все величины реального мира имеют аналоговый характер. Мы можем представить эти величины электрически в виде аналоговых сигналов. Аналоговый сигнал является изменяющимся во времени сигналом, который имеет любое количество значений (вариаций) для данного временного интервала.
В отличие от этого, цифровой сигнал внезапно изменяется от одного уровня к другому и будет иметь только конечное число значений (вариаций) для данного временного интервала.
В этой главе рассматриваются типы преобразователей данных и их спецификации.
Типы преобразователей данных
Электронные схемы, которые могут работать с аналоговыми сигналами, называются аналоговыми схемами. Аналогично, электронные схемы, которые могут работать с цифровыми сигналами, называются цифровыми схемами. Конвертер данных – это электронная схема, которая преобразует данные одной формы в другую.
Есть два типа преобразователей данных –
- Аналого-цифровой преобразователь
- Цифро-аналоговый преобразователь
Если мы хотим соединить выход аналоговой схемы как вход цифровой цепи, то мы должны поместить схему сопряжения между ними. Эта схема сопряжения, которая преобразует аналоговый сигнал в цифровой сигнал, называется аналого-цифровым преобразователем .
Точно так же, если мы хотим соединить выход цифровой схемы как вход аналоговой схемы, то мы должны поместить схему сопряжения между ними. Эта схема сопряжения, которая преобразует цифровой сигнал в аналоговый сигнал, называется цифроаналоговым преобразователем .
Обратите внимание, что для некоторых аналого-цифровых преобразователей может потребоваться цифроаналоговый преобразователь в качестве внутреннего блока для их работы.
Характеристики
Ниже приведены спецификации , связанные с преобразованием данных.
- разрешение
- Время конверсии
разрешение
Разрешающая способность – это минимальная величина изменения, необходимая для напряжения аналогового входа, чтобы оно было представлено в двоичном (цифровом) выходе. Это зависит от количества бит, которые используются в цифровом выходе.
Математически разрешение может быть представлено как
где «N» – количество битов, присутствующих в цифровом выходе.
Из приведенной выше формулы видно, что существует обратная зависимость между разрешением и количеством битов. Следовательно, разрешение уменьшается с увеличением количества битов и наоборот.
Разрешение также может быть определено как отношение максимального аналогового входного напряжения, которое может быть представлено в двоичном и эквивалентном двоичном числе.
Математически разрешение может быть представлено как
$$ Разрешение = \ гидроразрыва
$ V_
«N» – это количество битов, которые присутствуют на цифровом выходе.
Время конверсии
Время, необходимое преобразователю данных для преобразования данных (информации) одной формы в эквивалентные данные в другой форме, называется временем преобразования . Поскольку у нас есть два типа преобразователей данных, существует два типа времени преобразования следующим образом
- Время аналого-цифрового преобразования
- Время цифро-аналогового преобразования
Время, необходимое аналого-цифровому преобразователю (АЦП) для преобразования аналогового входного напряжения в его эквивалентный двоичный (цифровой) выход, называется временем аналого-цифрового преобразования . Это зависит от количества бит, которые используются в цифровом выходе.
Время, необходимое для цифро-аналогового преобразователя (ЦАП) для преобразования двоичного (цифрового) входа в его эквивалентное аналоговое выходное напряжение, называется временем цифро-аналогового преобразования . Это зависит от количества бит, которые присутствуют на двоичном (цифровом) входе.