views:

60

answers:

2

I'd like to instantiate a class by name (a string) without specifying a namespace or assembly. Like this (Unity syntax):

var processor = container.Resolve<IProcessor>("SpecialProcessor");

would instantiate the first IProcessor it finds called SpecialProcessor. Maybe

MyNamespace.SpecialProcessor

I'd like to avoid having to create an entry in a config every time somebody adds a new processor. I'm fine with having an entry for all candidate assemblies though.

Can I use an IoC container for something like this or should I roll my own?

A: 

It sounds like you have a plugin architecture and you want to allow other components to provide implementations of IProcessor without having to update some master config file. If that's the case, then I think you are best suited by using MEF (managed extensibility framework) (Website).

This is a framework designed to allow such behavior. Once you establish a catalog of assemblies to load components from, importing the collection of IProcessor instances is as easy as the following

var processors = container.GetExportedValues<IProcessor>();

There are many tutorials online for MEF that should get you started.

JaredPar
Yeah, you're right. But this is the only thing I'd need MEF for and it's a pretty small part of the app. I think I'll just roll my own for now. I'd also like to limit it by namespace, like only "Namespace.*" are allowed to match. This is like the Policy Injection block, but that works the opposite way (applies the matching rule to the type passed in instead of the types it can match).
BT
+1  A: 

Here's a function that does something very similar to what you want. You can modify it to filter based on a specific class name pretty easily.

These functions have references to a few utilities we use for logging and exception handling. You'll need to replace them with whatever you normally do in these situations.

    public static T FindAndCreate<T>(bool localOnly, bool exportedOnly)
    {
        Type[] types = FindAssignableClasses(typeof(T), localOnly, exportedOnly, false);
        if (types.Length == 0)
        {
            return default(T);
        }
        if (types.Length != 1)
        {
            Log.Warn(typeof(ReflectionUtil),
                     "FindAndCreate found {0} instances of {1} whereas only 1 was expected.  Using {2}.  {3}",
                     types.Length,
                     typeof(T).FullName,
                     types[0].FullName,
                     String.Join("\r\n  ", Array.ConvertAll<Type, String>(types, GetFullName)));
        }
        try
        {
            return (T)Activator.CreateInstance(types[0]);
        }
        catch (Exception ex)
        {
            throw ExceptionUtil.Rethrow(ex,
                                        "Unable to create instance of {0} found for interface {1}.",
                                        types[0].FullName,
                                        typeof(T).FullName);
        }
    }

    public static Type[] FindAssignableClasses(Type assignable, bool localOnly, bool exportedOnly, bool loadDll)
    {
        var list = new List<Type>();
        string localDirectoryName = Path.GetDirectoryName(typeof(ReflectionUtil).Assembly.CodeBase);

        if (loadDll && !_loadedAllDlls)
        {
            foreach (string dllPath in Directory.GetFiles(localDirectoryName.Substring(6), "*.dll"))
            {
                try
                {
                    Assembly.LoadFrom(dllPath);
                }
                catch
                {
                    // ignore
                }
            }
            _loadedAllDlls = true;
        }

        foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
        {
            try
            {
                if (localOnly && Path.GetDirectoryName(asm.CodeBase) != localDirectoryName)
                {
                    continue;
                }

                Type[] typesInAssembly;
                try
                {
                    typesInAssembly = exportedOnly ? asm.GetExportedTypes() : asm.GetTypes();
                }
                catch
                {
                    continue;
                }

                foreach (Type t in typesInAssembly)
                {
                    try
                    {
                        if (assignable.IsAssignableFrom(t) && assignable != t)
                        {
                            list.Add(t);
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Debug(
                            typeof(ReflectionUtil),
                            String.Format(
                                "Error searching for types assignable to type {0} searching assembly {1} testing {2}{3}",
                                assignable.FullName,
                                asm.FullName,
                                t.FullName,
                                FlattenReflectionTypeLoadException(ex)),
                            ex);
                    }
                }
            }
            catch (Exception ex)
            {
                // ignore dynamic module error, no way to check for this condition first
                // http://groups.google.com/group/microsoft.public.dotnet.languages.csharp/browse_thread/thread/7b02223aefc6afba/c8f5bd05cc8b24b0
                if (!(ex is NotSupportedException && ex.Message.Contains("not supported in a dynamic")))
                {
                    Log.Debug(
                        typeof(ReflectionUtil),
                        String.Format(
                            "Error searching for types assignable to type {0} searching assembly {1} from {2}{3}",
                            assignable.FullName,
                            asm.FullName,
                            asm.CodeBase,
                            FlattenReflectionTypeLoadException(ex)),
                        ex);
                }
            }
        }

        return list.ToArray();
    }
Sam
Thanks. That's some handy code to have around. I'll take some ideas from here as I make mine.
BT
@BT: be sure and accept this as your answer if it works for you. ;)
dboarman
@Sam: that is nice looking stuff. I'll have to keep it in mind myself.
dboarman