#c# #.net #xml #xsd
#c# #.net #xml #xsd
Вопрос:
У меня есть файл xsd, из которого я создаю класс C #. Чтобы упростить обслуживание, я хотел бы определить перечисление только в файле xsd, чтобы, когда мне нужно было изменить перечисление, мне нужно было обновлять его только в одном месте. Я знаю, как создать перечисление, но когда генерируется код C #, мне нужно, чтобы члены перечисления имели пользовательские значения, чтобы результат был похож на:
public enum SetupTypeEnum {
None = 0,
NewInstall = 1,
Modify = 2,
Upgrade = 4,
Uninstall = 8
}
Есть ли какой-либо способ написать xsd для достижения этой цели?
Комментарии:
1. Вы пишете свой собственный инструмент для генерации кода C # или есть какой-то стандартный инструмент, который генерирует код C # из XSD?
2. Прямо сейчас я использую xsd.exe . У меня действительно нет времени на создание собственного инструмента.
Ответ №1:
Вы можете добавлять аннотации, специфичные для генерации кода (это работает только для svcutil, а не для xsd.exe ) в ваш xsd-файл. Определение xsd для вашего перечисления будет примерно таким:
<xs:simpleType name="SetupTypeEnum">
<xs:restriction base="xs:string">
<xs:enumeration value="None">
<xs:annotation>
<xs:appinfo>
<EnumerationValue xmlns="http://schemas.microsoft.com/2003/10/Serialization/">0</EnumerationValue>
</xs:appinfo>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="NewInstall">
<xs:annotation>
<xs:appinfo>
<EnumerationValue xmlns="http://schemas.microsoft.com/2003/10/Serialization/">1</EnumerationValue>
</xs:appinfo>
</xs:annotation>
</xs:enumeration>
...
</xs:restriction>
</xs:simpleType>
Эти аннотации позволяют явно определять числовое значение каждого значения перечисления. Вы можете найти пример на этой странице msdn, если выполните поиск по «EnumerationValue».
Обновление: Джон Сондерс правильно указывает в своем комментарии, что это не сработает, если вы используете xsd.exe . Однако, если вы используете svcutil.exe чтобы создать код на C #, тогда аннотации будут работать.
Пример использования svcutil.exe
:
svcutil /dconly "D:test.xsd" /o:"D:test.cs"
Если вы используете svcutil
вместо xsd.exe
, то сгенерированный код будет немного отличаться. Наиболее важным отличием является то, что svcutil
будут генерироваться атрибуты для DataContractSerialization вместо XmlSerialization.
Комментарии:
1. -1: интересная ссылка, и я не знал об этой функции; но если OP использует XSD.EXE , затем он использует сериализацию XML и
EnumerationValue
является функцией сериализатора контрактов на передачу данных.2. @John: Да, это правда, что это для сериализатора контрактов с данными, я думал, что это тоже сработает
xsd.exe
. Однако, если вы используетеsvcutil
для создания кода xsd-файл, аннотации будут работать.3. Причина, по которой я использую xsd, заключается в том, что я не понимал, что могу сделать это любым другим способом. Хотя это может в конечном итоге потребовать больше кода, чем я хотел бы вложить в него, ответ на самом деле идеально подходит для того, что я ищу.
Ответ №2:
Понятие «перечисление» в XSD не имеет ничего общего с понятием «перечисление» в C #.
«перечисление» в XML-схеме — это способ ограничения возможных лексических значений типа перечисляемым списком значений. Например:
<xs:simpleType name="SummerMonth">
<xs:restriction base="xs:gMonth">
<xs:enumeration value="--07"/>
<xs:enumeration value="--08"/>
<xs:enumeration value="--09"/>
</xs:restriction>
</xs:simpleType>
Этот тип ограничивает пространство значений набором «летних» месяцев (июль, август и сентябрь).
Очевидно, что это не соответствует «перечислению» на C # или любом другом языке программирования, который я знаю.
Комментарии:
1. Хотя я понимаю, о чем вы говорите, xsd.exe преобразует перечисление xsd в перечисление C #, так что есть некоторая корреляция. Это просто не так глубоко, как я надеялся.
2. Он преобразует некоторые перечисления xsd в
enum
. И даже там, только со значениями по умолчанию.
Ответ №3:
Я считаю, что перечисления XSD являются более чистой реализацией перечислений, чем .ЧИСТЫЕ перечисления в том смысле, что они не нуждаются и не поддерживают числовые значения, связанные с перечисляемыми именами. Конечно, сгенерированный код, будучи .ЧИСТЫЙ код будет связывать числовое значение с каждым именованным значением внутри, но это деталь реализации, которая не присуща природе перечисления, как определено стандартом XSD. В этой чистой реализации перечисления я считаю, что правильным способом связать явные числовые значения с каждым перечисляемым именем было бы определить отдельную коллекцию / класс, который связывает перечисленные значения с числовыми значениями. Или определите дополнительные перечисленные значения, которые представляют поддерживаемые вами объединенные значения (NewInstallOrModify ).
Редактировать:
Вот пример того, как может выглядеть конвертер.
// Generated code
public enum SetupTypeEnum
{
None,
NewInstall,
Modify,
Upgrade,
Uninstall
}
// End generated code
public struct IntMappedEnum<T> where T : struct
{
public readonly int originalValue;
public IntMappedEnum(T value)
{
originalValue = (int)Enum.ToObject(typeof(T), value);
}
public IntMappedEnum(int originalValue)
{
this.originalValue = originalValue;
}
public static implicit operator int(IntMappedEnum<T> value)
{
return 1 << value.originalValue;
}
public static implicit operator IntMappedEnum<T>(T value)
{
return new IntMappedEnum<T>(value);
}
public static implicit operator IntMappedEnum<T>(int value)
{
int log;
for (log = 0; value > 1; value >>= 1)
log ;
return new IntMappedEnum<T>(log);
}
public static explicit operator T(IntMappedEnum<T> value)
{
T resu<
Enum.TryParse<T>(value.originalValue.ToString(), out result);
return resu<
}
}
class Program
{
static void Main(string[] args)
{
SetupTypeEnum s = SetupTypeEnum.Uninstall;
IntMappedEnum<SetupTypeEnum> c = s;
int n = c;
IntMappedEnum<SetupTypeEnum> c1 = n;
SetupTypeEnum s1 = (SetupTypeEnum)c1;
Console.WriteLine("{0} => {1} => {2}", s, n, s1);
}
}
Редактировать 2:
Если ваше перечисление начинается с 0 (как в вашем примере), эти два изменения необходимы для моего примера:
Обновленный конвертер int:
public static implicit operator int(IntMappedEnum<T> value)
{
return (value.originalValue == 0)?0:1 << (value.originalValue - 1);
}
Строка после int log
должна быть:
for (log = 0; value > 0; value >>= 1)
Комментарии:
1. Спасибо. Есть ли у вас ссылка, которая показывала бы образец класса, который связывал бы значения?
2. @Drew Burchett: это зависит от того, как вы собираетесь его использовать, но я добавил код, который работает для вашего примера и предполагает, что вы хотите, чтобы другие перечисления были представлены аналогичным образом со степенями 2.