0

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

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

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

4

1 回答 1

1

好的,如果您坚持“自动激活”,我所知道的唯一方法是将代码生成为字符串,然后在运行时使用 System.CodeDom.Compiler 命名空间中的类对其进行编译。我只用它从头开始生成完整的类,所以我不知道你是否可以让它为已经存在的类添加属性的需要工作,但如果你编译扩展方法也许你可以运行。

.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 中执行此操作肯定很有用。这绝对不是您想要定期做的事情,但是,如果您需要支持自定义脚本来执行复杂的验证或数据转换,这样的事情可能非常有用。

于 2012-05-12T23:52:58.297 回答