One of the new features in 2008r2 is the ability to use introspection to create object instances. I had discussed the ideas and design problems in a previous post, but now that the release is out, I can discuss how things ended up.
We added a new operator to the language: GetTypeInfo. It takes a class reference (much the same way that IsA does), and it returns an Introspection.TypeInfo object. So we can use this to get type information about a class that has no instances, but in such a way that the compiler is aware of the reference (so the class cannot be dead stripped). Additionally, we added Introspection.ConstructorInfo, and TypeInfo.GetConstructors so that you can actually create an object instance. All classes, regardless of whether they have a Constructor method or not, will return at least one ConstructorInfo so that you can construct the object. However, that doesn't mean every class will have a ConstructorInfo that takes no parameters, so be careful of that situation in your own code.
One interesting caveat to the current implementation is that you can use introspection as a way to defeat scoping rules. Introspection does not impose (and cannot impose) scoping rules like the compiler can. (This isn't new to ConstructorInfo either, it's the same for all member information.) So, you can use this as a way to call a protected constructor for a class which should instead be using a factory function! What's more, there is no way to query for the scope information at runtime, so there's no way to know whether you're doing an end-run around scope.
That being said, let's see how you can create an instance of a class using introspection.
Class Class1
Sub Constructor()
MsgBox "Hello world"
End Sub
End Class
dim ti as Introspection.TypeInfo = GetTypeInfo( Class1 )
dim ctors() as Introspection.ConstructorInfo = ti.GetConstructors
// Since there's only one constructor, we can do this safely
dim c as Class1 = ctors( 0 ).Invoke
If you run this code, you'll see the "Hello world" message displayed, so the constructor is being called -- and we're returning a valid instance of Class1, but we've never used the new operator to do it! So, it takes very little code to use introspection to create object instances.
Now the question becomes: what do I do if I want to create arbitrary classes by string. And the answer is: with work. As I mentioned in my previous article, using strings simply isn't feasible for a GetTypeInfo-like operator because of the dead-stripping problem. What's more, it's a dangerous thing to do in a generic fashion. Imagine if you allowed that -- then I could create a FolderItem that points to your desktop, and deletes everything from it. Oops, major security problem.
So you'll have to use something like a class factory to do this, and it's not very hard to do. The basic idea is that you have one call that registers classes which are safe to instantiate, and you have another call which creates an instance by string. So you would use it like this:
mFactory = new ClassFactory
mFactory.AddClassToMap( GetTypeInfo( Date ) )
dim d as Variant = mFactory.CreateNewInstance( "Date" )
If you'd like the full source code for such a project, you can download it here.
Enjoy!
Good one! I've started referencing your articles in the RB Wiki (http://declaresub.com/wiki/index.php/)
Thanks Aaron! This is a good summary of your previous Introspection posts and the new functionality added with RB2008r2!
Thanks for the example!
Thank you for this very helpful example.
Paul