lundi 25 janvier 2016

Visual-Studio style code generation: Generating basic class implementation at runtime

I have a project that allows the users to write plugins. I include a small IDE that allows writing code in C# and VB.NET and compiling it using the CodeDom compilers. Each plugin inherits a specific base class.

To make life easier for the user, I included basic templates for the various plugin types. Basically when the user creates a new plugin of type XY he starts from a basic implementation similar to what you get when you write

Public Class Foo
    Inherits BaseFoo

and press enter in Visual Studio. The uncool thing is, that I just added some text files containing the premade code to the resources and display it from there. This works well enough until changes are made in the definitions of the base classes because the premade code needs to be adjusted accordingly.

In the framework there exist classes derived from CodeDomProvider like Microsoft.VisualBasic.VBCodeGenerator that seem to contain the necessary methods to create code based on type definitions. However the CodeDom methods seem to be designed to create new types from scratch for example for scripting purposes and don't interact very well with Reflection in the sense, that I can't feed MethodInfos and the like directly to CodeDom.

I could probably hack something together where I manually build the type to be generated, like

Dim v As New Microsoft.VisualBasic.VBCodeProvider
Dim t As New CodeDom.CodeTypeDeclaration("Foo")
t.Attributes = CodeDom.MemberAttributes.Public
Dim r As New CodeDom.CodeTypeReference(GetType(Foo))
t.BaseTypes.Add(r)
t.IsClass = True
t.Name = "TestClass"
Dim base = GetType(Foo)

For Each method In base.GetMethods
    If (method.Attributes And Reflection.MethodAttributes.Abstract) > 0 Then
        Dim m As New CodeDom.CodeMemberMethod()
        For Each p In method.GetParameters
            Dim newp As New CodeDom.CodeParameterDeclarationExpression(p.ParameterType, p.Name)
            newp.Direction = If(p.IsIn, CodeDom.FieldDirection.In, CodeDom.FieldDirection.Out)
            m.Parameters.Add(newp)
        Next
        m.Name = method.Name
        m.Attributes = CodeDom.MemberAttributes.Public Or CodeDom.MemberAttributes.Override
        t.Members.Add(m)
    End If
Next

Dim w As New IO.StringWriter
Dim opts As New CodeDom.Compiler.CodeGeneratorOptions
v.GenerateCodeFromType(t, w, opts)

For an example class

Public MustInherit Class Foo
    Public Property Foo1 As Double
    Public Function What() As Double
        Return Double.NaN
    End Function
    Public MustOverride Sub Test(ByRef Was As Double)
    Public MustOverride ReadOnly Property Something As Double
End Class

this creates the output

Public Class TestClass
    Inherits TypeGenerator.Foo

    Public Overrides Sub Test(ByRef Was As System.Double&)
    End Sub

    Public Overrides Sub get_Something()
    End Sub
End Class

This goes into the right direction, but it seems very prone to bugs and very fiddly. (On a side note, why does it implement the parameter as System.Double& which does not even compile. As a ByVal parameter it works).

Is there an easier way to achieve what I need?

  • Have a base class
  • Create a code template from the definitions therein at runtime




Aucun commentaire:

Enregistrer un commentaire