有趣的问题。不久前,我想知道完全相同的事情。
我将展示几个例子来说明我已经走了多远。我的演示将不完整(考虑到 XML Schema 规范相当全面),但应该足以展示......
-
您可以比
xsd.exe做得更好(如果您在编写 XML Schema 时愿意遵守某些模式);和
-
XML Schema 允许不能用 C# 表达的类型声明。考虑到 XML 和 C# 是非常不同的语言,用途也大不相同,这不应该让人感到意外。
在 XML Schema 中声明接口
C# 接口可以在具有复杂类型的 XML Schema 中定义。例如:
<xsd:complexType name="IFoo" abstract="true">
<xsd:attribute name="Bar" type="xsd:string" use="required" />
<xsd:attribute name="Baz" type="xsd:int" use="optional" />
</xsd:complexType>
相当好地对应于:
interface IFoo
{
string Bar { get; set; }
int? Baz { get; set; }
}
这里的模式是抽象和命名(非匿名)复杂类型基本上是 C# 中接口的 XML Schema 等价物。
注意映射的一些问题:
-
public、internal 等 C# 访问修饰符无法在 XML Schema 中呈现。
-
您无法表达 C# 字段和 XML Schema 中的属性之间的区别。
-
您不能在 XML Schema 中定义方法。
-
您也无法表达 C# struct 和 class 之间的区别。 (XML Schema 中有简单的类型,大致对应于 .NET 值类型;但它们在 XML Schema 中的限制比复杂类型要大得多。)
-
usage="optional" 的用法可用于映射可空类型。在 XML Schema 中,您可以将字符串属性定义为可选。跨到 C# 时,会出现一些翻译损失:由于 string 是引用类型,因此不能将其声明为可为空(因为默认情况下它已经可为空)。
-
XML Schema 也允许usage="prohibited"。这又是不能用 C# 表达的东西,或者至少不能用一种很好的方式表达 (AFAIK)。
-
从我的实验来看,xsd.exe 似乎永远不会从抽象复杂类型生成 C# 接口;它将与abstract classes 保持一致。 (我猜这是为了保持翻译逻辑相当简单。)
声明抽象类
抽象类可以与接口非常相似:
<xsd:element name="FooBase" abstract="true">
<xsd:complexType>
...
</xsd:complexType>
</xsd:element>
在这里,您定义了一个将abstract 属性设置为true 的元素,并在其中嵌入了一个匿名复杂类型。
这对应于 C# 中的以下类型声明:
abstract class FooBase { ... }
声明类
同上,但省略abstract="true"。
声明实现接口的类
<xsd:complexType name="IFoo" abstract="true">
...
</xsd:complexType>
<xsd:element name="Foo" type="IFoo" />
这映射到:
interface IFoo { ... }
class Foo : IFoo { ... }
也就是说,您既定义了一个命名的抽象复杂类型(接口),又定义了一个具有该类型的命名元素。
在 XML Schema 中表达继承关系
XML Schema 中的类和接口继承可以通过类型扩展和元素替换组的组合来定义:
<xsd:element name="Base" type="base" />
<xsd:element name="Derived" substitutionGroup="Base" type="derived" />
<!-- ^^^^^^^^^^^^^^^^^^^^^^^^ -->
<xsd:complexType name="base">
<xsd:attribute name="Foo" type="xsd:boolean" use="required" />
</xsd:complexType>
<xsd:complexType name="derived">
<xsd:complexContent>
<xsd:extension base="base"> <!-- !!! -->
<xsd:attribute name="Bar" type="xsd:string" use="required" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
这映射到:
class Base
{
bool Foo { get; set; }
}
class Derived : Base
{
string Bar { get; set; }
}
注意:
-
我们再次使用命名复杂类型。但是这一次,它们没有定义abstract="true",因为我们没有定义任何 C# 接口类型。
-
注意引用:元素Derived 在Base 的替换组中;同时,复杂类型derived 是复杂类型base 的扩展。 Derived 的类型为 derived,Base 的类型为 base。
-
非抽象的命名复杂类型在 C# 中没有直接对应物。它们不是类,因为它们不能被实例化(在 XML 中,elements,而不是 types,与 F# 中的值构造函数或 C# 中的对象实例化具有大致相同的功能) ;它们也不是真正的接口,因为它们没有被声明为抽象的。
我的回答中没有涉及到的一些事情
-
展示如何在 XML Schema 中声明实现几个接口的 C# 类类型。
-
显示 XML Schema 中的复杂内容如何映射到 C#(我首先猜测 C# 中根本没有对应关系;至少在一般情况下没有)。
-
enums。 (它们在 XML Schema 中通过 enumeration 限制简单类型来实现,顺便说一句。)
-
const 类中的字段(这些可能会映射到具有fixed 值的属性)。
-
如何将xsd:choice、xsd:sequence映射到C#;如何正确映射IEnumerable<T>、ICollection<T>、IList<T>、IDictionary<TKey, TValue> 到 XML Schema?
-
XML Schema 简单类型,听起来像是 .NET 值类型的对应概念;但受到更多限制并且有不同的目的。
还有很多我没有展示的东西,但现在您可能已经看到了我的示例背后的基本模式。
要正确完成这一切,必须系统地阅读 XML Schema 规范,并了解其中提到的每个概念如何将 最佳 映射到 C#。 (也许没有单一的最佳解决方案,但有几个替代方案。)但我明确表示只展示几个有趣的例子。我希望这仍然提供足够的信息!