tags:

views:

158

answers:

3

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.

+10  A: 

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();
}
jrummell
+5, if it were possible to give you +5. I'll have to settle for +1
John Buchanan
+8  A: 

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.

Dathan
+1 for linq with reflection - nice!
James Westgate
Very close--I'd only like to search my namespace, though.
Loren Pechtel
@Loren My apologies - easy fix.
Dathan
That does it except for a mystery. One class containing a test function is a singleton that's declared entirely statically. (It's really a mathematical transformation and has no internal state beyond a table built during initialization.) This causes an error that it can't make an abstract class. It's not abstract!
Loren Pechtel
@Loren `static` classes are `abstract sealed`, at least in terms of runtime information. I've updated the code sample to handle that case.
Dathan
That does it. Now there's no forgetting to update the list of tests to run. I hate manually maintained lists!
Loren Pechtel
+2  A: 

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.

Dan Bryant
What I was looking for is to simply include a method SelfTest() in any class that had test capability. This puts the test code right with the code it's testing. However, I don't want to have to maintain a list of such methods to call, but rather find them. The compiler certainly can do it and doing it manually risks the bug of failing to add one to the list.
Loren Pechtel
@Loren using a custom attribute to do this is probably much safer. By using the custom attribute you allow the implementing class to name the method whatever they want and just mark it with the appropriate attribute. Then you can use reflection to search for all methods that have that attribute and call those methods. This way you are not reliant on the calling class naming the method correctly and you have no problems if someone happens to name their method wrong.
Stephan