GetCustomAttribute returns null or nothing in release builds
I have a couple of .NET classes that I use to call stored procedures in two lines of code using .NET reflection. One of my methods creates a SqlCommand for a stored procedure by examining attributes and parameters on the method that called it (IE the call to the stored proc basically needs to match the method signature). This technique was initially posted here back in 2002!
It was working beautifully for me in debug mode, but suddenly stopped working in release builds. Why? Because of a JIT optimization known as inlining. Basically, the .NET JIT compiler took my simple function’s code and placed it “inline” where the call to that function occurs. I discovered this after a good bit of pain, and finally, a manual in-code reflection-based stack dump to a log file… my method was no where to be found in the stack. Ahh… inlining. No wonder it couldn’t find the attribute!
Inlining is a great optimization, saving plenty of processor time pushing all that stuff on the stack just to call my simple function, except when the next function down the stack actually needs an attribute from an inlined function one level above on the stack… the attribute doesn’t exist because the function has been obliterated by the inlining optimization. I found some good information about how the JIT inlines here and here. But Microsoft needs to add an additional rule to their JIT inlining rules: a function should NEVER be inlined if it has an attribute on it, especially a custom attribute that sits outside of Microsoft signed assemblies. Otherwise, reflection will fail to find the attribute. (IE GetCustomAttribute returns null/Nothing).
There is a workaround though. Simply add this attribute to the method that you do not want inlined:
I posted this in hope that someone might find this information helpful… I also posted this to Microsoft Connect. I’d call it a bug for sure… it is unexpected behavior.
UPDATE: Microsoft responded quickly. They don’t consider this a bug. I have to agree with them that it is a bad idea to depend on stack traces, but the same problem could occur if you pass the “curent method.” IE if you get inlined, that means the method may or may not be what you think it is. In general, it seems like the techniques outlined in this article are probably a bit outdated… just like me. 🙂