Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding all classes containing a method in C#

Tags:

c#

reflection

I want to search through all classes in a namespace for those containing a certain method. If a class contains a method then I want to create an instance of the class and run the method.

Obviously I have to start with reflection but I'm stuck on where to go.

Edit:

Interfaces are not the way I want to do this.

What I'm looking for is embedding testing functions into the code but a single calling interface. If there's a self-test function, call it. If there isn't, ignore it.

like image 254
Loren Pechtel Avatar asked May 05 '10 19:05

Loren Pechtel


3 Answers

Create an interface that declares the method and then have various classes implement that interface.

You can then use reflection to find all types within an assembly that implement that interface.

From there you'll need to create an instance of each type and then call the method. The implementation details will vary depending on what you are trying to do.

Update based on comments:

I still think an interface (or attribute) is the way to go. This is how it would work with an interface.

interface ISelfTester
{
    void SelfTest();
}

class SomeClass : ISelfTester
{
    /* ... */

    public void SelfTest() 
    {
        // test code
    }

    /* ... */
}

You could then invoke each type's SelfTest method like so (borrowing from Dathan and Darren Kopp):

var type = typeof(ISelfTester);
var types = AppDomain.CurrentDomain.GetAssemblies()
    .Select(x => x.GetTypes())
    .SelectMany(x => x)
    .Where(x => x.Namespace == "My.Name.Space" && type.IsAssignableFrom(x));

foreach (Type t in types)
{
    ISelfTester obj = Activator.CreateInstance(t) as ISelfTester;
    obj.SelfTest();
}
like image 192
jrummell Avatar answered Nov 07 '22 05:11

jrummell


Without more information about what distinguishes the method, I'm just going to assume it's distinguished by name, and it's public. The name assumption is dangerous, so I wouldn't recommend doing this, but the following should do what you want (assuming Activator is able to create an instance).

EDIT: Added Where(x => x.Namespace == "My.Name.Space") to limit the results to a single target namespace.

EDIT: Added if ... else to handle the case of static methods.

var methods = AppDomain.CurrentDomain.GetAssemblies()
    .Select(x => x.GetTypes())
    .SelectMany(x => x)
    .Where(x => x.Namespace == "My.Name.Space")
    .Where(c => c.GetMethod("MethodName") != null)
    .Select(c => c.GetMethod("MethodName"));

foreach (MethodInfo mi in methods)
{
    if (mi.IsStatic)
    {
        mi.Invoke(null, null); // replace null with the appropriate arguments
    }
    else if (!mi.DeclaringType.IsAbstract)
    {
        var obj = Activator.CreateInstance(mi.DeclaringType);
        mi.Invoke(obj, null); // replace null with the appropriate arguments
    }
}

If you have control over the types, though, jrummel's suggestion about interfaces is a much safer way to do this.

like image 11
Dathan Avatar answered Nov 07 '22 04:11

Dathan


One option would be to use Reflection, as described above, but rather than finding the method by name, look for a method tagged with an appropriate custom attribute. This is similar to what the MS DataContractSerializer does with attributes like [OnDeserializing]. This way the class implementer is specifically spelling out their intent for the method, rather than having it suddenly do something unexpected as a result of it having a particular name.

On a side note, since what you're after is a test method, you might check out something like NUnit. There are several excellent free unit testing frameworks out there. They also provide additional features that can help with your testing, as they provide the scaffolding for the different types of test assertions you might want to make.

like image 2
Dan Bryant Avatar answered Nov 07 '22 04:11

Dan Bryant