How to figure out dynamically all methods with cus

2019-05-22 12:11发布

问题:

I have a simple challenge. I dynamically need to figure out all methods with a specific attribute in C#. I'm going to load the assemblies dynamically from another application and need to find out the exact methods. The assemblies look like the followings:

Base.dll:

 Class Base
   {
   [testmethod]
   public void method1()
   ... 
   }

Derived.dll:

 Class Derived:Base
  {
   [testmethod]
   public void method2()
  }

Now in 3rd application I dynamically like to load the above mentioned dlls and find out testmethods.

If I load Base.dll, I need to get testmethod1. If I load Drived.dll, should I get testmethod1 and testmethod2.

I found some code online which helped me to load dlls dynamically:

 List<Assembly> a = new List<Assembly>();

    string bin = @"Bin-Folder";

    DirectoryInfo oDirectoryInfo = new DirectoryInfo(bin);

    //Check the directory exists
    if (oDirectoryInfo.Exists)
    {
     //Foreach Assembly with dll as the extension
     foreach (FileInfo oFileInfo in oDirectoryInfo.GetFiles("*.dll", SearchOption.AllDirectories))
     {

      Assembly tempAssembly = null;

     //Before loading the assembly, check all current loaded assemblies in case talready loaded
    //has already been loaded as a reference to another assembly
    //Loading the assembly twice can cause major issues
    foreach (Assembly loadedAssembly in AppDomain.CurrentDomain.GetAssemblies())
    {
     //Check the assembly is not dynamically generated as we are not interested in these
     if (loadedAssembly.ManifestModule.GetType().Namespace != "System.Reflection.Emit")
     {
       //Get the loaded assembly filename
        string sLoadedFilename =
                                loadedAssembly.CodeBase.Substring(loadedAssembly.CodeBase.LastIndexOf('/') + 1);

      //If the filenames match, set the assembly to the one that is already loaded
        if (sLoadedFilename.ToUpper() == oFileInfo.Name.ToUpper())
        {
            tempAssembly = loadedAssembly;
            break;
        }
      }
     }

     //If the assembly is not aleady loaded, load it manually
     if (tempAssembly == null)
     {
         tempAssembly = Assembly.LoadFrom(oFileInfo.FullName);
     }
     a.Add(tempAssembly); 
    } 

The above code is working fine and I can load the DLLs. However when I use the following code to find out the right method, it doesn't return any desired results. I'm wondering which part is not correct. The following code lists about 145 methods but non of them is one which I'm looking for.

public static List<string> GetTests(Type testClass)
{
 MethodInfo[] methodInfos = testClass.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance);
 Array.Sort(methodInfos,
       delegate(MethodInfo methodInfo1, MethodInfo methodInfo2)
 { return methodInfo1.Name.CompareTo(methodInfo2.Name); });

 foreach (MethodInfo mi in methodInfos)
 {
   foreach (var item in mi.GetCustomAttributes(false))
     {
      if
     (item.ToString().CompareTo("Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute") == 0)
                    result.Add(mi.Name);
            }
        }

        return result;
   }

Can any one help me with this issue?

I'm not sure why but I tried to instantiate objects from above mentioned classes (Base and Derived) and the above mentioned code returns the right results. However as mentioned above if I don't have an object from base and derived classes and try to figure out the methods based on the types, it doesn't return the desired results.

Thx

回答1:

The simplest approach is to use MethodInfo.IsDefined - quite possibly with LINQ as well:

var testMethods = from assembly in assemblies
                  from type in assembly.GetTypes()
                  from method in type.GetMethods()
                  where method.IsDefined(typeof(TestMethodAttribute))
                  select method;

foreach (var method in testMethods)
{
    Console.WriteLine(method);
}

(I'd do all the sorting with LINQ as well. Obviously you can tune the GetMethods call etc to only return instance methods, for example.)

It's not entirely clear to me why your current approach doesn't work or why it does work when you've created instances - but without a short but complete example demonstrating the problem, it would be hard to diagnose it further. I'd definitely start with the code above :)