Edward Tanguay
Edward Tanguay

Reputation: 193352

How to make C# recognize custom attributes in external .dll?

The code below successfully recognizes internal classes which are decorated with my custom "Module" attribute, I load the assembly like this:

Assembly assembly = Assembly.GetExecutingAssembly();

However, when I load in an external module and look through its classes, it finds the classes in the external assembly but does not recognize the custom attributes:

Assembly assembly = Assembly.LoadFrom(@"c:\tests\modules\CustomModules.dll");

What do I have to specify so that C# recognizes custom attributes in external .dlls the same as it does with the internal classes?

Here is the code that successfully runs through and recognizes internal classes decorated with my "Module" attribute:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace DynamicAssembly2
{
    class Program
    {
        static void Main(string[] args)
        {
            var modules = from t in GetModules()
                           select t;
            foreach (var module in modules)
            {
                ModuleAttribute[] moduleAttributes = GetModuleAttributes(module);
                Console.WriteLine(module.FullName);
                foreach (var moduleAttribute in moduleAttributes)
                {
                    Console.WriteLine(moduleAttribute.Description);
                }
            }
            Console.ReadLine();
        }

        public static IEnumerable<Type> GetModules()
        {
            //Assembly assembly = Assembly.LoadFrom(@"c:\tests\modules\CustomModules.dll");
            Assembly assembly = Assembly.GetExecutingAssembly();

            return GetAssemblyClasses(assembly)
                .Where((Type type) => {return IsAModule(type);});
        }

        public static IEnumerable<Type> GetAssemblyClasses(Assembly assembly)
        {
            foreach (Type type in assembly.GetTypes())
            {
                Console.WriteLine(type.FullName);
                yield return type;
            }
        }

        public static bool IsAModule(Type type)
        {
            return GetModuleAttribute(type) != null;
        }

        public static ModuleAttribute GetModuleAttribute(Type type)
        {
            ModuleAttribute[] moduleAttributes = GetModuleAttributes(type);
            Console.WriteLine(moduleAttributes.Length);
            if (moduleAttributes != null && moduleAttributes.Length != 0)
                return moduleAttributes[0];
            return null;
        }

        public static ModuleAttribute[] GetModuleAttributes(Type type)
        {
            return (ModuleAttribute[])type.GetCustomAttributes(typeof(ModuleAttribute), true);
        }

    }
}

Here is my custom Mdoule attribute:

using System;

namespace DynamicAssembly2
{
    [AttributeUsage(AttributeTargets.Class)]
    public class ModuleAttribute : Attribute
    {
        public string Description { get; set; }
    }
}

Here is a custom module:

namespace DynamicAssembly2
{
    [Module(Description="This is the main customer class.")]
    class Customers
    {
    }
}

Upvotes: 2

Views: 3144

Answers (4)

Mark Lakata
Mark Lakata

Reputation: 20878

I had the same problem. If the attribute was defined in an internal *cs, I could get the attribute, but if it was defined in an external dll, I would just get null when I queried for it (no error), but no linker error either.

Apparently a custom Attribute defined in an DLL and the same custom Attribute defined in local code (*cs) will happily compile into one application without any warnings about multiple definitions (the name spaces are identical). I got rid of the local code (*cs), and fixed the references and it worked.

Upvotes: 0

edosoft
edosoft

Reputation: 17281

Aloha

I've tested your code but couldn't get it to compile because the type ModuleAttribute must be known in both the Main program and the external dll. I assumed there were no references involved.

I did get it to work by introducing a reference.

Here's the class. This assembly holds a reference to DynamicAssembly

using System;    

namespace DynamicAssembly2
{
    [DynamicAssembly.Module(Description = "This is the main customer class.")]
    public class Customers
    {
    }
}

Here's the code in DynamicAssembly:

using System.Reflection;

namespace DynamicAssembly
{
    [AttributeUsage(AttributeTargets.Class)]
    public class ModuleAttribute : Attribute
    {
        public string Description { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var modules = from t in GetModules()
                          select t;
            foreach (var module in modules)
            {
                ModuleAttribute[] moduleAttributes = GetModuleAttributes(module);
                Console.WriteLine(module.FullName);
                foreach (var moduleAttribute in moduleAttributes)
                {
                    Console.WriteLine(moduleAttribute.Description);
                }
            }
            Console.ReadLine();
        }

        public static IEnumerable<Type> GetModules()
        {
            Assembly assembly = Assembly.LoadFrom(@"C:\Temp\ClassLibrary1\bin\Debug\ClassLibrary1.dll");

            return GetAssemblyClasses(assembly)
                .Where((Type type) => { return IsAModule(type); });
        }

        public static IEnumerable<Type> GetAssemblyClasses(Assembly assembly)
        {
            foreach (Type type in assembly.GetTypes())
            {
                yield return type;
            }
        }

        public static bool IsAModule(Type type)
        {
            return GetModuleAttribute(type) != null;
        }

        public static ModuleAttribute GetModuleAttribute(Type type)
        {
            ModuleAttribute[] moduleAttributes = GetModuleAttributes(type);
            Console.WriteLine(moduleAttributes.Length);
            if (moduleAttributes != null && moduleAttributes.Length != 0)
                return moduleAttributes[0];
            return null;
        }

        public static ModuleAttribute[] GetModuleAttributes(Type type)
        {
            return (ModuleAttribute[])type.GetCustomAttributes(typeof(ModuleAttribute), true);
        }

    }
}

Upvotes: 1

Marc Gravell
Marc Gravell

Reputation: 1063358

I'm pretty sure that it doesn't distinguish... are you sure you are asking for the right attribute? As long as both projects reference the same ModuleAttribute, then typeof(ModuleAttribute) should work. Otherwise you'll need to first find the Type of the attribute you want (from Assembly.GetType()), and use that when calling GetCustomAttributes.

Upvotes: 2

Rob Walker
Rob Walker

Reputation: 47482

How many instances of the ModuleAttribute class do you have defined across your assemblies?

It looks like you have one in DynamicAssembly2 and one in CustomModules ... in which case they are different types.

Your code in DynamicAssembly2 should use the attribute class defined in CustomModules (or both assemblies should load the attribute from a 3rd assembly).

Upvotes: 4

Related Questions