65.9K
CodeProject 正在变化。 阅读更多。
Home

从声明式代码生成类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.54/5 (10投票s)

2004年10月7日

CPOL

2分钟阅读

viewsIcon

84572

downloadIcon

901

从声明式的 XML 代码示例创建 C# 类。

Sample Image - DeclarativeClassGenerator.png

引言

我发现我经常直接在 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();
    }
  }
}

结论

我希望这个小工具对所有声明式程序员都有帮助!

© . All rights reserved.