Los Techies : Blogs about software and anything tech!

Beware exceptions in attribute constructors


If you’d like to have some really wacky bugs, be sure to do something like this:

public class BlowupAttribute : Attribute
{
    public BlowupAttribute(int time)
    {
        if (time <= 0)
            throw new ArgumentOutOfRangeException("time", time, "Must be greater than zero.");
    }
}

Attributes are a little different than other classes, as you’re never really in control of when the constructor gets called.  Things get especially weird in unit tests, where test runners do quite a bit of reflection that triggers the constructors of attributes.  This test fails:

[Blowup(-1)]
public void Asplode()
{
}

[Test]
public void Fails_anyway()
{
    true.ShouldBeTrue();
}

Simply because another member in the class has a bad value in the attribute.  Unfortunately, the stack trace gives absolutely zero hint on where the exception occurred:

TestCase 'M:AttributeExceptions.Blarg.Fails_anyway'
failed: Must be greater than zero.
Parameter name: time
Actual value was -1.
    System.ArgumentOutOfRangeException: Must be greater than zero.
    Parameter name: time
    Actual value was -1.
    C:\dev\MSTestSpec\MSTestSpec.Tests\AttributeExceptions.cs(13,0): at AttributeExceptions.BlowupAttribute..ctor(Int32 time)
    at System.Reflection.CustomAttribute._CreateCaObject(Void* pModule, Void* pCtor, Byte** ppBlob, Byte* pEndBlob, Int32* pcNamedArgs)
    at System.Reflection.CustomAttribute.CreateCaObject(Module module, RuntimeMethodHandle ctor, IntPtr& blob, IntPtr blobEnd, Int32& namedArgs)
    at System.Reflection.CustomAttribute.GetCustomAttributes(Module decoratedModule, Int32 decoratedMetadataToken, Int32 pcaCount, RuntimeType attributeFilterType, Boolean mustBeInheritable, IList derivedAttributes)
    at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeMethodInfo method, RuntimeType caType, Boolean inherit)
    at NUnit.Core.Reflect.HasAttribute(MemberInfo member, String attrName, Boolean inherit)
    at NUnit.Core.Reflect.GetMethodWithAttribute(Type fixtureType, String attributeName, BindingFlags bindingFlags, Boolean inherit)
    at NUnit.Core.NUnitTestFixture..ctor(Type fixtureType)
    at NUnit.Core.Builders.NUnitTestFixtureBuilder.MakeSuite(Type type)
    at NUnit.Core.Builders.AbstractFixtureBuilder.BuildFrom(Type type)
    at NUnit.Core.Extensibility.SuiteBuilderCollection.BuildFrom(Type type)
    at NUnit.Core.Builders.TestAssemblyBuilder.GetFixtures(Assembly assembly, String ns)
    at NUnit.Core.Builders.TestAssemblyBuilder.Build(String assemblyName, Boolean autoSuites)
    at NUnit.Core.Builders.TestAssemblyBuilder.Build(String assemblyName, String testName, Boolean autoSuites)
    at NUnit.Core.TestSuiteBuilder.BuildSingleAssembly(TestPackage package)
    at NUnit.Core.TestSuiteBuilder.Build(TestPackage package)
    at NUnit.AddInRunner.NUnitTestRunner.run(ITestListener testListener, Assembly assembly, ITestFilter filter)
    at NUnit.AddInRunner.NUnitTestRunner.runMethod(ITestListener testListener, Assembly assembly, MethodInfo method)
    at NUnit.AddInRunner.NUnitTestRunner.RunMember(ITestListener testListener, Assembly assembly, MemberInfo member)
    at TestDriven.TestRunner.AdaptorTestRunner.Run(ITestListener testListener, ITraceListener traceListener, String assemblyPath, String testPath)
    at TestDriven.TestRunner.ThreadTestRunner.Runner.Run()

Nowhere do we see where the original attribute was declared, that information is lost.  Attributes are meant for metadata, and exceptions can really screw things up.  To be safe, avoid throwing exceptions in an attribute constructor.  Also, be very careful of any complex operations done in the constructor.  If you’re doing anything more than merely capturing parameters passed in, you’re doing too much.

Kick It on DotNetKicks.com
Posted Nov 22 2008, 08:05 PM by bogardj
Filed under:

Comments

Captain Obvious wrote re: Beware exceptions in attribute constructors
on 11-22-2008 10:46 PM

"Nowhere do we see where the original attribute was declared, that information is lost."

... AttributeExceptions.cs(13,0): at AttributeExceptions.BlowupAttribute..ctor(Int32 time)

bogardj wrote re: Beware exceptions in attribute constructors
on 11-23-2008 11:44 AM

@Captain Obvious

That's where the attribute class is declared, but not the usage.  I have no information on which attribute usage caused the exception.

More obvious now? :)

Reflective Perspective - Chris Alcock » The Morning Brew #229 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #229
on 11-24-2008 4:14 AM

Pingback from  Reflective Perspective - Chris Alcock  » The Morning Brew #229

Dew Drop - November 24, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Drop - November 24, 2008 | Alvin Ashcraft's Morning Dew
on 11-24-2008 7:09 AM

Pingback from  Dew Drop - November 24, 2008 | Alvin Ashcraft's Morning Dew

Dew Drop - November 24, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Drop - November 24, 2008 | Alvin Ashcraft's Morning Dew
on 11-24-2008 7:09 AM

Pingback from  Dew Drop - November 24, 2008 | Alvin Ashcraft's Morning Dew

Jimmy Bogard wrote Attributes are lousy decorators
on 12-08-2008 11:07 PM

Attributes allow developers to provide a mechanism to add metadata to types, assemblies, type members

Rick Minerich's Development Wonderland wrote Discoveries This Week 01/09/2008
on 01-09-2009 10:38 AM

It’s been a very exiting week.  I actually had more things to post than time would allow me to write

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems