从声明式代码生成类






4.54/5 (10投票s)
从声明式的 XML 代码示例创建 C# 类。
引言
我发现我经常直接在 XML 中进行对象图建模,而不是先实现类或绘制类的 UML 图。 这种 XML 建模方法让我能够以更紧凑的树状结构查看对象图,并且可以调整每个类的属性,等等,直到声明式标记满足设计要求。 一旦我完成了标记,我最终不得不手动编写底层的类。 一遍又一遍。 我最终意识到,一个快速而简单的生成器,它可以接收我的示例 XML 并生成底层的类,可以满足我 90% 的需求并节省大量的输入。 这就是这个工具的作用。
要求
该代码利用 MycroXaml 声明式解析器来生成 GUI,如果你想编译该项目,你需要单独下载它。
示例
假设你在标记中有一个非常简单的声明式元素
<ComplexNumber Real="12.34" Imaginary="45.67"/>
生成器创建底层的类
public class ComplexNumber
{
protected double imaginary;
protected double real;
public double Imaginary
{
get {return imaginary;}
set {imaginary=value;}
}
public double Real
{
get {return real;}
set {real=value;}
}
}
如果标记包含更复杂的对象图,例如
<Database Name="MyDB">
<Tables>
<Table Name="Person">
<Fields>
<Field Name="ID" PrimaryKey="true"/>
<Field Name="FirstName" Unique="false"/>
<Field Name="LastName" Unique="false" Indexed="true"/>
</Fields>
</Table>
</Tables>
</Database>
生成器创建所有类及其属性
public class Field
{
protected bool unique;
protected string name;
protected bool primaryKey;
protected bool indexed;
public bool Unique
{
get {return unique;}
set {unique=value;}
}
public string Name
{
get {return name;}
set {name=value;}
}
public bool PrimaryKey
{
get {return primaryKey;}
set {primaryKey=value;}
}
public bool Indexed
{
get {return indexed;}
set {indexed=value;}
}
}
public class Database
{
protected string name;
protected ArrayList tables;
public string Name
{
get {return name;}
set {name=value;}
}
public ArrayList Tables
{
get {return tables;}
}
public Database()
{
tables=new ArrayList();
}
}
public class Table
{
protected string name;
protected ArrayList fields;
public string Name
{
get {return name;}
set {name=value;}
}
public ArrayList Fields
{
get {return fields;}
}
public Table()
{
fields=new ArrayList();
}
}
现在可以将这些类粘贴到你的应用程序中,并且可以使用声明式代码来实例化对象图并分配属性值。
需要注意的事项
上面的示例突出了一些生成器的特性
- 它遵循声明式代码的“类-属性-类”模式。
- 它假定“类-属性-类”模式中的“属性”是一个集合,并将属性类型实现为
ArrayList
。 它也可以是一个抽象类,例如,但此生成器不支持。 - 如果标记中的属性被赋值为“true”或“false”,则属性类型为“
bool
”。 - 如果属性值仅包含数字,则属性类型为“
int
”。 - 如果属性值包含数字和小数点,则属性类型为“
double
”。 - 所有其他属性类型都假定为“
string
”。 不支持枚举。 - 集合属性仅具有“
get
”方法。 - 集合属性在类构造函数中实例化。
代码
该代码非常简单。 由于有些人喜欢在文章中看到代码,所以这里提供:
using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using MycroXaml.Parser;
namespace MyXaml.ClassGenerator
{
public sealed class ClassInfo
{
public string name;
public Hashtable properties;
public ArrayList collections;
public ClassInfo(string name)
{
this.name=name;
properties=new Hashtable();
collections=new ArrayList();
}
}
public class ClassGenerator
{
protected Parser p;
protected Hashtable classInfoList;
public static void Main()
{
new ClassGenerator();
}
public ClassGenerator()
{
classInfoList=new Hashtable();
StreamReader sr=new StreamReader("ClassGen.mycroxaml");
string text=sr.ReadToEnd();
sr.Close();
XmlDocument doc=new XmlDocument();
doc.LoadXml(text);
p=new Parser();
Form form=(Form)p.Load(doc, "ClassGenerator", this);
Application.Run(form);
}
public void OnGenerate(object sender, EventArgs e)
{
classInfoList.Clear();
TextBox src=(TextBox)p.GetInstance("xml");
TextBox dest=(TextBox)p.GetInstance("code");
XmlDocument doc=new XmlDocument();
try
{
doc.LoadXml(src.Text);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
XmlNode root=doc.DocumentElement;
ProcessClass(root);
dest.Text=EmitCode();
}
protected void ProcessClass(XmlNode node)
{
string className=node.Name;
ClassInfo ci;
if (classInfoList.Contains(className))
{
ci=(ClassInfo)classInfoList[className];
}
else
{
ci=new ClassInfo(className);
classInfoList[className]=ci;
}
foreach(XmlAttribute attr in node.Attributes)
{
string name=attr.Name;
string val=attr.Value.ToLower();
// assume a string type
string ptype="string";
// if this is a new field, figure out the type
if (!ci.properties.Contains(name))
{
// may be boolean
if ( (val=="true") || (val=="false") )
{
ptype="bool";
}
// if it starts with a digit, may be int or double
if (Char.IsDigit(val, 0))
{
ptype="int";
if (val.IndexOf(".") != -1)
{
ptype="double";
}
}
ci.properties[name]=ptype;
}
foreach(XmlNode childNode in node.ChildNodes)
{
if (!ci.collections.Contains(childNode.Name))
{
ci.collections.Add(childNode.Name);
foreach(XmlNode grandchildNode in childNode.ChildNodes)
{
ProcessClass(grandchildNode);
}
}
}
}
}
protected string EmitCode()
{
StringBuilder sb=new StringBuilder();
foreach(DictionaryEntry entry in classInfoList)
{
ClassInfo ci=(ClassInfo)entry.Value;
string cname=ci.name.ToUpper()[0] + ci.name.Substring(1);
sb.Append("public class "+cname+"\r\n");
sb.Append("{\r\n");
// emit properties
foreach(DictionaryEntry prop in ci.properties)
{
string pname=(string)prop.Key;
string ptype=(string)prop.Value;
pname=pname.ToLower()[0]+pname.Substring(1);
sb.Append("\tprotected "+ptype+" "+pname+";\r\n");
}
// emit collections
foreach(string collName in ci.collections)
{
string name=collName.ToLower()[0]+collName.Substring(1);
sb.Append("\tprotected ArrayList "+name+";\r\n");
}
// emit get/set for properties
foreach(DictionaryEntry prop in ci.properties)
{
string pname=(string)prop.Key;
string ptype=(string)prop.Value;
pname=pname.ToLower()[0]+pname.Substring(1);
string sname=pname.ToUpper()[0]+pname.Substring(1);
sb.Append("\tpublic "+ptype+" "+sname+"\r\n");
sb.Append("\t{\r\n");
sb.Append("\t\tget {return "+pname+";}\r\n");
sb.Append("\t\tset {"+pname+"=value;}\r\n");
sb.Append("\t}\r\n");
}
// emit get for collections
foreach(string collName in ci.collections)
{
string name=collName.ToUpper()[0]+collName.Substring(1);
string collNameSmall=collName.ToLower()[0]+collName.Substring(1);
sb.Append("\tpublic ArrayList "+name+"\r\n");
sb.Append("\t{\r\n");
sb.Append("\t\tget {return "+collNameSmall+";}\r\n");
sb.Append("\t}\r\n");
}
// If class has collections, create a constructor that
// initializes the ArrayLists.
if (ci.collections.Count > 0)
{
// emit constructor
sb.Append("\tpublic "+cname+"()\r\n");
sb.Append("\t{\r\n");
// for each collection, initialize it in the constructor
foreach(string collName in ci.collections)
{
string name=collName.ToLower()[0]+collName.Substring(1);
sb.Append("\t\t"+name+"=new ArrayList();\r\n");
}
sb.Append("\t}\r\n");
}
sb.Append("}\r\n\r\n");
}
return sb.ToString();
}
}
}
结论
我希望这个小工具对所有声明式程序员都有帮助!