在开始编写软件代码之前,建立一个软件系统的模型是非常关键的一步。模型工具为设计者、架构师和开发者提供了一个图形界面,以操作类、接口、结构等数据结构的方法、属性、字段、事件。本文将介绍一个名为ModelStudio的设计工具,它可以帮助在直接编写代码之前创建模型结构,同时在与该设计工具交互的过程中,代码将在后台更新,并使用System.CodeDom命名空间自动生成。
认为首先对代码进行建模是面向对象编程(OOP)和设计优秀软件应用程序架构的良好起点。如果正在寻找一个反射解决方案,那么可能想要查看Sacha Barber的100%反射类图创建工具。对于本文不讨论的图表工具,修改并使用了Barber工具中的ClassDrawerContainerPanel及其依赖类。同时,也要感谢Chagosoft的cDOMx类构建器提供的灵感。
ModelStudio应用程序使用所称的ModelStudio框架来创建Visual Basic和C#代码文档。使用ModelStudio相当简单。当启动程序时,将看到一个空白画布。要开始创建第一个类,只需点击“新建命名空间”按钮,给它一个名字,一个新的命名空间对象就会被创建。然后,只需从左侧选择命名空间来添加“新导入”或“新类”。要向类添加属性和方法,只需选择所需的类并使用“新属性”或“新方法”按钮。向方法添加参数也非常简单;只需从左侧选择方法并点击“新参数”。
当对项目满意时,有以下选项来获取代码:使用“生成代码”按钮检索项目的代码;使用“生成文件”按钮在选定的文件夹中生成各个类文档;还可以使用“保存”按钮将项目(存储库)保存为文件,以便将来编辑或传输。要打开项目文件,请使用“打开”按钮,浏览到文件,项目将被加载。
需要补充一点关于创建这个资源工具的目的。从Visual Studio 2005开始,微软包含了他们所谓的类设计器。类设计器工具可能适合组织,特别是如果有超级Visual Studio Team System。然而,不喜欢类设计器工具的“小事情”。例如,喜欢所有生成的类在类构造期间初始化任何自定义类型。
在类设计器中,默认情况下,它只为提供了一个原型化的类属性。
Public Overridable Property Name() As String
Get
End Get
Set(ByVal value As String)
End Set
End Property
理想的代码应该是这样的:
Private m_Name As String
Public Overridable Property Name() As String
Get
Return Me.m_Name
End Get
Set(ByVal value As String)
Me.m_Name = value
End Set
End Property
ModelStudio框架实现了一个简单的设计,首先将类信息封装到一个简单的类的“外观”中。这里用最宽泛的意义来使用“外观”这个词。
外观类和每个数据中的信息然后被“翻译”,稍后将讨论,以创建实际的CodeDom类进行代码生成。
这是一个示例外观类,它将用于为生成的类创建一个新的Imports语句(即,using)。
Public Class CodeDomImport Implements ICodeMember
Private m_Import As String = "System.Text"
Private m_Parent As Object
Public Sub New(ByVal ImportName As String)
m_Import = ImportName
End Sub
Public Property Name() As String Implements ICodeMember.Name
Get
Return m_Import
End Get
Set(ByVal value As String)
m_Import = value
End Set
End Property
Public Property Parent() As Object
Get
Return m_Parent
End Get
Set(ByVal value As Object)
m_Parent = value
End Set
End Property
End Class
这是整个过程的核心。在这里,实际的代码被翻译、准备并生成到代码文件中。基本上,这个类的构造并不理想。然而,对于本文的目的,一切都在这个类中完成。翻译发生在返回一个常规的CodeDOM CodeNamespace时。
现在需要按照想要的方式准备代码输出。在所说的准备阶段,将使用以下CodeDom命名空间:
Imports System.CodeDom
Imports System.CodeDom.Compiler
为了准备类在类构造期间初始化所有自定义类型,使用以下代码:
Dim mcdClass As CodeTypeDeclaration = _Class.DOMClass
' Constructors
For Each meth As CodeDomMethod In _Class.Constructors
If meth.Parameters.Count > 0 Or _Class.CustomTypes.Count > 0 Then
Dim cnstr As New CodeConstructor
For Each param As CodeDomParameter In meth.Parameters
cnstr.Parameters.Add(New CodeParameterDeclarationExpression(param.Type, param.Name))
Next
' Here is where custom initialization begins
For Each p As CodeDomProperty In _Class.CustomTypes
Dim this As New CodeThisReferenceExpression()
Dim leftExpression As New CodePropertyReferenceExpression(this, p.Name)
Dim rightExpression As New CodeObjectCreateExpression(New CodeTypeReference(p.Type))
Dim assign As New CodeAssignStatement(leftExpression, rightExpression)
cnstr.Statements.Add(assign)
Next
p
mcdClass.Members.Add(cnstr)
End If
Next meth
现在,可以通过简单地调整上述命令来满足自己的个人喜好,来更改构造函数以初始化任何类型。
在这里,实际上输出翻译和准备的内容。显然,将在这里生成VB代码,但还有一个类似的C#生成方法。
Private Sub GenerateVBFile(ByVal n As CodeNamespace, ByVal OutputDirectory As String)
m_Unit = New CodeCompileUnit
m_Unit.Namespaces.Add(n)
' Generate the code with the VB code provider.
Dim provider As Microsoft.VisualBasic.VBCodeProvider = New Microsoft.VisualBasic.VBCodeProvider
' Build the output file name.
For Each c As CodeDom.CodeTypeDeclaration In n.Types
Dim tempUnit As New CodeCompileUnit
tempUnit.Namespaces.Add(New CodeDom.CodeNamespace(n.Name))
tempUnit.Namespaces(0).Types.Add(c)
' Build the output file name.
Dim sourceFile As String = OutputDirectory & Path.DirectorySeparatorChar & c.Name & "." + provider.FileExtension
' Create a TextWriter to a StreamWriter to an output file.
Using tw As New IndentedTextWriter(New StreamWriter(sourceFile, False), vbCrLf)
' Generate source code using the code provider.
provider.GenerateCodeFromCompileUnit(tempUnit, tw, New CodeGeneratorOptions())
' Close the output file.
tw.Close()
End Using
Next c
End Sub
Namespace Classroom
Public Class Student
Private m_Name As String
Private m_StudentID As Integer
Private m_MyTeacher As Teacher
Private m_MyChair As Chair
Private m_IsSpecial As Boolean
Private m_Books As List(Of Book)
Private Sub New()
MyBase.New
Me.MyTeacher = New Teacher
Me.MyChair = New Chair
Me.Books = New List(Of Book)
End Sub
Public Overridable Property Name() As [String]
Get
Return Me.m_Name
End Get
Set
Me.m_Name = value
End Set
End Property
...
End Class
End Namespace