prosseek
prosseek

Reputation: 190889

Using .NET Reflection and Attribute to control the action

I have this code copied from C# 4.0 in a nutshell that uses Attribute/Reflection API to control the action: the number of tests to run and show error message.

When I run the code, I got this result.

Method Method1 will be tested; reps=1; msg=; memo=
Test1
Method Method2 will be tested; reps=3; msg=; memo=abc
Test2
Test2
Test2
Method Method3 will be tested; reps=3; msg=Debugging Time!; memo=
Test3
Test3
Test3

Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object
  at Hello.Main () [0x00000] in <filename unknown>:0 

Why is this Unhandled Exception?

The source code is as follows :

using System;
using System.Reflection;

[AttributeUsage (AttributeTargets.Method)]
public sealed class TestAttribute : Attribute
{
    public int Repititions;
    public string FailureMessage;
    public string Memo;

    public TestAttribute() : this(1) {}
    public TestAttribute(int repititions) {Repititions = repititions;}
}

class Foo
{
    [Test]
    public void Method1()
    {
        Console.WriteLine("Test1");
    }
    [Test(3, Memo="abc")]
    public void Method2()
    {
        Console.WriteLine("Test2");
    }
    [Test(3, FailureMessage="Debugging Time!")]
    public void Method3()
    {
        Console.WriteLine("Test3");
    }
}

class Hello
{
    static void Main()
    {
        foreach (MethodInfo mi in typeof(Foo).GetMethods())
        {
            TestAttribute att = (TestAttribute) Attribute.GetCustomAttribute(mi, typeof(TestAttribute)); 
            if (att != null)
                Console.WriteLine("Method {0} will be tested; reps={1}; msg={2}; memo={3}", mi.Name, att.Repititions, att.FailureMessage, att.Memo);
                for (int i = 0; i < att.Repititions; i++)
                    try
                    {
                        mi.Invoke(new Foo(), null);
                    }
                    catch (Exception ex)
                    {
                        throw new Exception ("Error: " + att.FailureMessage, ex);
                    }
        }
    }
}

Upvotes: 0

Views: 759

Answers (4)

48klocs
48klocs

Reputation: 6103

I added a stub line to your code - you're trying to get custom attributes off of System.String's ToString() method.

foreach (MethodInfo mi in typeof(Foo).GetMethods())
    {
        Console.WriteLine(mi.ToString());
        TestAttribute att = (TestAttribute) Attribute.GetCustomAttribute(mi, typeof(TestAttribute)); 
        if (att != null)
            Console.WriteLine("Method {0} will be tested; reps={1}; msg={2}; memo={3}", mi.Name, att.Repititions, att.FailureMessage, att.Memo);
            for (int i = 0; i < att.Repititions; i++)
                try
                {
                    mi.Invoke(new Foo(), null);
                }
                catch (Exception ex)
                {
                    throw new Exception ("Error: " + att.FailureMessage, ex);
                }
    }

Upvotes: 0

agent-j
agent-j

Reputation: 27933

You were missing some curly braces {} in your if (att != null) and foreach. Indentation alone is not enough.

class Hello
{
    static void Main()
    {
        foreach (MethodInfo mi in typeof(Foo).GetMethods())
        {
            TestAttribute att = (TestAttribute) Attribute.GetCustomAttribute(mi, typeof(TestAttribute)); 
            if (att != null)
            {
                Console.WriteLine("Method {0} will be tested; reps={1}; msg={2}; memo={3}", mi.Name, att.Repititions, att.FailureMessage, att.Memo);
                for (int i = 0; i < att.Repititions; i++)
                {
                    try
                    {
                        mi.Invoke(new Foo(), null);
                    }
                    catch (Exception ex)
                    {
                        throw new Exception ("Error: " + att.FailureMessage, ex);
                    }
                }
            }
        }
    }
}

Upvotes: 3

Yuriy Faktorovich
Yuriy Faktorovich

Reputation: 68697

Because the MethodInfo mi is the ToString inherited from object which doesn't have a TestAttribute. So now you have the value of att is null and you try to call att.Repititions on it. You can change it to

if (att != null)
{
    Console.WriteLine("Method {0} will be tested; reps={1}; msg={2}; memo={3}", mi.Name, att.Repititions,
                        att.FailureMessage, att.Memo);
    for (int i = 0; i < att.Repititions; i++)
        try
        {
            mi.Invoke(new Foo(), null);
        }
        catch (Exception ex)
        {
            throw new Exception("Error: " + att.FailureMessage, ex);
        }
}

to fix it.

Upvotes: 1

Daniel A. White
Daniel A. White

Reputation: 190952

You are likely hitting an internal method that does not have the attribute, hence this line fails:

TestAttribute att = (TestAttribute) Attribute.GetCustomAttribute(mi, typeof(TestAttribute)); 

you want to use the safe-cast:

TestAttribute att =  Attribute.GetCustomAttribute(mi, typeof(TestAttribute)) as TestAttribute; 

Upvotes: 0

Related Questions