自动化属性?

时间:2012-05-12 18:36:02

标签: vb.net

假设我声明了这样一个类:

Class tst
  Public Props As New Dictionary(Of String, MyProp)
End Class

并在这些方面添加了属性:

Dim t As New tst
t.Props.Add("Source", new MyProp(3))

但现在想要像这样访问它:

t.Source

如何在不知道吸气剂名称的情况下创建吸气剂?

1 个答案:

答案 0 :(得分:1)

好吧,如果你坚持“自动生存”,我知道做这样的事情的唯一方法是将代码生成为字符串,然后使用System.CodeDom中的类在运行时编译它。编译器命名空间。我只是用它来从头开始生成完整的类,所以我不知道你是否能够将它用于需要为已经存在的类添加属性的工作,但是如果你编译了扩展方法,也许你可以运行时。

.NET框架包括CodeDomeProvider类的多个实现,每种语言一个。您很可能对Microsoft.VisualBasic.VBCodeProvider类感兴趣。

首先,您需要创建CompilerParameters对象。您需要使用生成的代码需要引用的所有库的列表来填充其ReferencedAssemblies集合属性。将GenerateExecutable属性设置为False。将GenerateInMemory设置为True。

接下来,您需要创建一个包含要编译的源代码的字符串。然后,调用CompileAssemblyFromSource,向其传递CompilerParameters对象和源代码字符串。

CompileAssemblyFromSource方法将返回CompilerResults对象。 Errors集合包含编译错误列表(如果有),CompiledAssembly属性将是对已编译库的引用(作为Assembly对象)。要创建动态编译类的实例,请调用CompiledAssembly.CreateInstance方法。

如果您只是生成少量代码,那么编译它的速度非常快。但如果是很多代码,您可能会注意到对性能的影响。

以下是如何生成包含单个动态属性的动态类的简单示例:

Option Strict Off

Imports System.CodeDom.Compiler
Imports Microsoft.VisualBasic
Imports System.Text

Public Class Form3
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim code As StringBuilder = New StringBuilder()
        code.AppendLine("Namespace MyDynamicNamespace")
        code.AppendLine("   Public Class MyDynamicClass")
        code.AppendLine("       Public ReadOnly Property WelcomeMessage() As String")
        code.AppendLine("           Get")
        code.AppendLine("               Return ""Hello World""")
        code.AppendLine("           End Get")
        code.AppendLine("       End Property")
        code.AppendLine("   End Class")
        code.AppendLine("End Namespace")
        Dim myDynamicObject As Object = generateObject(code.ToString(), "MyDynamicNamespace.MyDynamicClass")
        MessageBox.Show(myDynamicObject.WelcomeMessage)
    End Sub


    Private Function generateObject(ByVal code As String, ByVal typeName As String) As Object
        Dim parameters As CompilerParameters = New CompilerParameters()
        parameters.ReferencedAssemblies.Add("System.dll")
        parameters.GenerateInMemory = True
        parameters.GenerateExecutable = False
        Dim provider As VBCodeProvider = New VBCodeProvider()
        Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, code)
        If results.Errors.HasErrors Then
            Throw New Exception("Failed to compile dynamic class")
        End If
        Return results.CompiledAssembly.CreateInstance(typeName)
    End Function
End Class

注意,我从不使用Option Strict Off,但为了简单起见,我将其关闭,因此我可以在不编写所有反射代码的情况下简单地调用myDynamicObject.WelcomeMessage

使用反射调用对象上的方法可能既痛苦又危险。因此,在共享程序集中提供基类或接口会很有帮助,该程序集由生成的程序集和调用生成的程序集的固定程序集引用。这样,您就可以通过强类型接口使用动态生成的对象。

我根据你的问题认为你刚刚习惯了像JavaScript这样的动态语言,所以你只是想用一种错误思维方式的解决方案,而不是你真的需要甚至应该这样做。但是,在某些情况下,知道如何在.NET中执行此操作绝对有用。这绝对不是你想要定期做的事情,但是,如果你需要支持自定义脚本来执行复杂的验证或数据转换,这样的事情可能非常有用。