Преобразование перечислений xsd в C # - PullRequest
6 голосов
/ 06 октября 2011

У меня есть файл xsd, из которого я генерирую класс C #.Чтобы упростить обслуживание, я бы хотел определить перечисление в файле xsd только для того, чтобы при необходимости изменять перечисление мне приходилось обновлять его только в одном месте.Я знаю, как создать перечисление, но когда генерируется код C #, мне нужно, чтобы члены перечисления имели пользовательские значения, поэтому результат будет похож на:

public enum SetupTypeEnum {
    None = 0,
    NewInstall = 1,
    Modify = 2,
    Upgrade = 4,
    Uninstall = 8
}

Есть ли способ написатьXSD для этого?

Ответы [ 4 ]

7 голосов
/ 08 октября 2011

Вы можете добавить аннотации, специфичные для генерации кода (это работает только для 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.

3 голосов
/ 08 октября 2011

Понятие «перечисление» в 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 # или любом другом языке программирования, который я знаю.

2 голосов
/ 08 октября 2011

Я считаю, что перечисления XSD являются более пуристической реализацией перечислений, чем перечисления .NET в том смысле, что они не нужны и не поддерживают числовые значения, связанные с перечисляемыми именами. Конечно, сгенерированный код, являющийся кодом .NET, будет внутренне ассоциировать числовое значение с каждым именованным значением, но это деталь реализации, которая не присуща природе перечисления, как определено стандартом XSD. В этой пуристической реализации перечисления, я полагаю, что правильный способ связать явные числовые значения с каждым перечисляемым именем - это определить отдельную коллекцию / класс, который связывает перечислимые значения с числовыми значениями. Или определите дополнительные перечисляемые значения, которые представляют объединенные значения, которые вы поддерживаете (NewInstallOrModify).

Edit:

Вот пример того, как может выглядеть конвертер.

// 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 result;
     Enum.TryParse<T>(value.originalValue.ToString(), out result);
     return result;
  }
}

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 голос
/ 06 октября 2011
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...