In several recent projects that I have worked on, I've wanted to create a pluggable system that allows me to add new functionality to my applicaiton without having to rebuild the application ore redistribute any of the original bits. As any OOP developer knows, there are 10 times as many ways to do this as there are developers in the world. I chose a simple method of using a base class and having all of plugin modules inherit from this base class. The key here, is that when my app starts up, it needs to load a list of all classes from any assembly in the bin folder that inherits from my base class. To do this, I've come up with a fairly big chunk of code that relies more on error handling than I would like. My code is basically as follows (proprietary info / objects have been changed to protect the innocent... errr... to cover my butt. :)
250 Private Function _GetApplicationList() As MyModuleCollection
251 Dim apps As New MyModuleCollection
252 Try
253 Dim fi As FileInfo
254 Dim Asm As System.Reflection.Assembly
255 Dim t As Type
256 Dim o As Object
257 Dim fsa As MyBaseObject
258 Dim app As MyModuleObject
259
260 'get the current working folder
261 Dim diFolder As DirectoryInfo = New FileInfo(Me.GetType.Assembly.Location).Directory
262
263 'loop through each of the assmeblies in this folder and load up any class that inherits MyBaseObject
264 For Each fi In diFolder.GetFiles("*.dll")
265 Try
266 'load the assmebly
267 Asm = [Assembly].LoadFile(fi.FullName)
268
269 'check the assembly to ensure i'm not going to cause a loop by examining the class i'm currently calling from
270 If Asm.FullName <> Me.GetType.Assembly.FullName Then
271
272 'loop through all the classes in this assembly
273 For Each t In Asm.GetTypes
274 Try
275 'load up the class assuming the base class accepts one parameter in the constructor
276 o = Asm.CreateInstance(t.FullName, False, BindingFlags.CreateInstance, Nothing, New Object() {Nothing}, Nothing, Nothing)
277 'check to see if it's inheriting MyBaseObject
278 If o.GetType.IsSubclassOf(GetType(MyBaseObject)) Then
279 fsa = CType(o, MyBaseObject)
280 'create the object to store the module information
281 app = New MyModuleObject
282
283 app.AssemblyName = New IO.FileInfo(t.Assembly.Location).Name
284 app.AssemblyType = fsa.GetType.FullName
285
286
287
288 'add it to the module collection
289 apps.Add(app)
290 End If
291 Catch ex As System.Exception
292 'an error occured. but we really don't care at this point. just continue on.
293 End Try
294 Next
295
296 End If
297 Catch
298 'ingore here. can't load the file.
299 End Try
300 Next
301
302 Catch ex As Exception
303 'error occured... what to do?
304 Return New MyModuleCollection
305 End Try
306
307 'send back the results
308 Return apps
309 End Function
As you can see, the idea is to loop through all classes in all assemblies in the current folder that my app is executing from, try to instantiate every class and if it works, check to see if that class inherits from my object. This code works - and it works great. It produces the exact results that I am expecting to find. However, I can't help but think that there is a better way to do this. I can see a huge problem in my code already... what happens when there are dozens of assemblies and hundreds, if not thousands, of classes to search through? That's a lot of looping and a lot of error handing... and catching exceptions is expensive.
So my question is: Does anyone know of a better method to find al classes in a given assembly, that inherit from a given base class or interface?