假设我声明一个这样的类:
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
如何在不知道吸气剂名称的情况下创建吸气剂?
假设我声明一个这样的类:
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
如何在不知道吸气剂名称的情况下创建吸气剂?
好的,如果您坚持“自动激活”,我所知道的唯一方法是将代码生成为字符串,然后在运行时使用 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 中执行此操作肯定很有用。这绝对不是您想要定期做的事情,但是,如果您需要支持自定义脚本来执行复杂的验证或数据转换,这样的事情可能非常有用。