views:

142

answers:

4

Background

I have a few scripts that run as part of my build process that look at the various source code files and generate some more source code for me.

The scripts use CodeDom to generate them and they read the .cs files using a simple text reader.

Question

One of the scripts is looking for use of a specific class attribute called PageMenuItem and its purpose is to build a static list of page menu items.

It does this right now by reading all of the .cs files and looks for "PageMenuItem" attributes, then it counts the number of arguments and tries to figure out which constructor is being used so it can pull apart the various pieces of information.

There are 7 constructors for PageMenuItem with various parameters, so it is getting very difficult to determine from the .cs source code which constructor is being used and therefore how to parse out the information.

Instead of trying to parse the text myself, I would like to simply construct a PageMenuItem object in memory and then use its properties.

So, I need a way of taking the attribute declaration from the .cs file and construct a new instance of PageMenuItem from it.

Is that possible?


Another way of asking this question:

Given this string:

string myCodeStatement = "[MyAttribute(\"asdf\", \"foo\")]";

How can I create an object of the type MyAttribute so that I can work with that object? I have full access to the source code that defines MyAttribute.

+1  A: 

Seems like you could introspect the class files, or alternately add an annotation to the constructor that would make your parsing job simpler. Aspect oriented techniques might help -- capture every time that constructor is called, and as it is, add the item to your list.

hypermiler
+1  A: 

You can use the CSharpCodeProvider to do this. There is a Microsoft Support article describing the process.

Reed Copsey
+1  A: 

Is invoking the compiler an option? You could build the source files themselves and use reflection to walk the attributes, or you could create a dummy source file that you mark up with those attributes. Either way, once it's compiled, you can reflect in to access the attribute's properties.

Neil Whitaker
+1  A: 

This script is part of the build process, so it needs to be pretty quick. Also, it generates source code that is going to get compiled, so I want to avoid "double compiling" my project.

I also don't want to have to compile and load an assembly just to reflect against it, which seems "expensive" to do several hundred times during my build process.

I decided that I just needed to fix up how I am parsing the attributes now. ---

I'm already reading the source code and trying to count the number of attributes. I decided I could modify that code to just parse the arguments into their types and then use Activator.CreateInstance() using my parsed arguments. Activator will figure out which constructor to use and I will get an instance of my attribute class and can use its properties.

I already know which types are available in the attribute's constructor, so I wrote a little method that parses the stuff between the parans into their correct type (string, int, guid, etc) and then I pass that object array to the Activator.

The Activator does the hard work of finding the correct constructor and gives me back an instance of my attribute class.

quip