Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
205 views
in Technique[技术] by (71.8m points)

vb.net - Autovivified properties?

suppose I declare a class like this:

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

and added properties something along these lines:

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

but now want to access it like this:

t.Source

how can I create a getter without knowing the name of the getter?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Ok, if you insist on "auto-vivifying", the only way I know of to do something like that is to generate the code as a string, and then compile it at runtime using the classes in the System.CodeDom.Compiler namespace. I've only ever used it to generate complete classes from scratch, so I don't know if you could even get it to work for what need to add properties to an already existing class, but perhaps you could if you compiled extension methods at runtime.

The .NET framework includes multiple implementations of the CodeDomeProvider class, one for each language. You will most likely be interested in the Microsoft.VisualBasic.VBCodeProvider class.

First, you'll need to create a CompilerParameters object. You'll want to fill its ReferencedAssemblies collection property with a list of all the libraries your generated code will need to reference. Set the GenerateExecutable property to False. Set GenerateInMemory to True.

Next, you'll need to create a string with the source code you want to compile. Then, call CompileAssemblyFromSource, passing it the CompilerParameters object and the string of source code.

The CompileAssemblyFromSource method will return a CompilerResults object. The Errors collection contains a list of compile errors, if there are any, and the CompiledAssembly property will be a reference to your compiled library (as an Assembly object). To create an instance of your dynamically compiled class, call the CompiledAssembly.CreateInstance method.

If you're just generating a small amount of code, it's pretty quick to compile it. But if it's a lot of code, you may notice an impact on performance.

Here's a simple example of how to generate a dynamic class containing a single dynamic property:

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

Note, I never use Option Strict Off, but for the sake of simplicity in this example, I turned it off so I could simply call myDynamicObject.WelcomeMessage without writing all the reflection code myself.

Calling methods on objects using reflection can be painful and dangerous. Therefore, it can be helpful to provide a base class or interface in a shared assembly which is referenced by both the generated assembly, and the fixed assembly which calls the generated assembly. That way, you can use the dynamically generated objects through a strongly typed interface.

I figured based on your question that you were just more used to dynamic languages like JavaScript, so you were just thinking of a solution using the wrong mindset, not that you really needed to or even should be doing it this way. But, it is definitely useful in some situations to know how to do this in .NET. It's definitely not something you want to be doing on a regular basis, but, if you need to support custom scripts to perform complex validation or data transformations, something like this can be very useful.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...