Welcome to blogs.conchango.com Sign in | Join | Help

Welcome to blogs.conchango.com

Howard van Rooijen's Blog

AssemblyReflector v1.0.0.0 : An event-based Assembly Parsing Engine

AssemblyReflector, along with Microsoft SDC TaskBuilder represent the fruits of my Xmas holiday hobby coding; I'm one of these unfortunate people who just can't turn off their brain and stop new ideas from coming even when they are suppose to be relaxing and taking a break from the work they do all year round.

AssemblyReflector (Conchango.Code.Reflection) is an event-based assembly parser - it allows assemblies to be searched for: Attributes, Events, Fields, Interfaces, Methods, Nested Types, Properties and Types, by subscribing to the relevent OnDiscover event and then performing a search for the member based on several available search methods;

  • Contains(string)
  • EndsWith(string)
  • Named(string)
  • OfType(Type) - Attributes Only
  • StartsWith(string)
  • WithBindingFlags(BindingFlags)

When an event is raised you can access the discovered member via the EventArgs.

I've uploaded AssemblyReflector v1.0.0.0 to my Projects Page on Darren Neimke's awesome ProjectDistributor site - where you can download the Source or the Binaries for the class library. The Source contains the AssemblyReflector project, a demo console application project, a test assembly project and a partially completed unit test suite.

In order for the demo application to work you will need to ensure that you have NUnit installed - as the TestAssembly uses the NUnit.Framework.TestAttribute and NUnit.Framework.TestFixtureAttribute.

Demo:

Once you have downloaded the Source or the Binaries; In a command prompt execute Conchango.Code.Reflection.Demo.exe - you will be presented with some information and 6 different searches you can perform on the Conchango.Code.Reflection.TestAssembly.dll:

Conchango.Code.Reflection.Demo

 

Select a number (1-6) and then hit enter. The specified search will run and the results will be outputted to the console window. To verify that the correct members have been found - look at Conchango.Code.Reflection.TestAssembly.cs (in the Binaries zip or Conchango.Code.Reflection.TestAssembly\TestTheOneRing.cs in the Source zip)- this is the source of Conchango.Code.Reflection.TestAssembly.dll.

Example of how to use AssemblyReflector:

Create a new class called simple.cs and paste the following in:

//---------------------------------------------------------------------------------------------------

//

//  Copyright © 2005 Conchango Plc. All rights reserved. NO WARRANTY

//  BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE

//  EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT

//  HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER

//  EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY

//  AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE

//  PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY

//  SERVICING, REPAIR OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN

//  WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE

//  PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,

//  INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM

//  (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES

//  SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER

//  PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH

//  DAMAGES.

//

//  Permission is granted to anyone to use this software for any purpose, including commercial

//  applications, and to alter it and redistribute it freely, subject to the following restrictions:

//

//  1. The origin of this software must not be misrepresented; you must not claim that you wrote the

//    original software. If you use this software in a product, an acknowledgment in the product

//    documentation would be appreciated but is not required.

//  2. Altered source versions must be plainly marked as such, and must not be misrepresented as

//    being the original software.

//  3. This notice may not be removed or altered from any source distribution.

//

//

//

//   File Created

//

//---------------------------------------------------------------------------------------------------

namespace Conchango.Code.Reflection.Demo

{

    #region Directives

    using System;

    using Conchango.Code.Reflection;

    using Conchango.Code.Reflection.EventArgs;

    #endregion

 

    ///

    /// A simple example of how to use AssemblyReflector

    ///

    public class Simple

    {

        private AssemblyReflector reflector;

 

        ///

        /// Main entry point the the application.

        ///

        ///

string arguments passed into the application - not used

        [STAThread]

        public static void Main(string[] args)

        {

            Simple simpleDemo = new Simple();

            Console.ReadLine();

        }

        ///

        /// Creates a new instance of the Simple object - wires up AssemblyReflector events and performs

        /// a search for all methods in Conchango.Code.Reflection.TestAssembly.dll that start with the

        /// word "IsAlso" and writes out the results to the console window.

        ///

        public Simple()

        {

            this.reflector = new AssemblyReflector();

            //Wire up events you want to display

            this.reflector.OnAssemblyLoadFailure += new AssemblyReflector.AssemblyLoadFailureEventHandler(OnAssemblyLoadFailure);

            this.reflector.OnMethodDiscovered += new AssemblyReflector.MethodDiscoveredEventHandler(OnMethodDiscovered);

            //Load the test assembly

            this.reflector.Assemblies.Load("Conchango.Code.Reflection.TestAssembly.dll");

            //Find all methods in the test assembly that start with the word "IsAlso"

            this.reflector.Methods.StartsWith("IsAlso");

        }

        ///

        /// Writes a message out to the console when an assembly fails to load successfully

        ///

        ///

The source of the event.

        ///

A AssemblyEventArgs that contains the event data.

        private void OnAssemblyLoadFailure(object sender, AssemblyEventArgs e)

        {

            Console.WriteLine(string.Format("Assembly: {0}, failed to load", (e.Message)));

        }

        ///

        /// Writes a message out to the console when a method matching the search parameters is found

        ///

        ///

The source of the event.

        ///

A MethodInfoEventArgs that contains the event data.

        private void OnMethodDiscovered(object sender, MethodInfoEventArgs e)

        {

            Console.WriteLine("Method " + e.Method.Name + " discovered.");

        }

    }

}

 

You will need to add a reference to both Conchango.Code.Reflection.dll and Conchango.Code.Reflection.TestAssembly.dll.

In the example above the TestAssembly is loaded and a search is performed for all methods that start with the word "IsAlso". The console window output should show the following:

Example simple.cs output

 

 

Published 09 January 2005 22:28 by howard.vanrooijen
Filed under: ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

howard.vanrooijen said:

Why not using Microsoft.Cci that is shipped with FxCop ?

The library exposes a visitor (StandardVisitor) that explores the entire graph. You can use it pass through *everyting* in the assemlby (even the IL)

// the visitor
public class StartsWithVisitor : StandardVisitor
{
public override TypeNode VisitMethod(Method method)
{
base.VisitMethod(method);
if (typeNode.Name.Name.StartsWith("..."))
Console.WriteLine("Method " + method.FullName + " discovered.");
}
}

// the parsing
Module module = Module.GetModule(fileName);
StartsWithVisitor vis = new StartsWithVisitor();
vis.VisitModule(module);

Cheers,
Peli
January 10, 2005 01:07
 

howard.vanrooijen said:

Hi Jonathan,

I knew I'd get a comment out of you!

The main reason I didn't go down the CCI route was because I wanted a lightweight parsing engine - the Microsoft.Cci.dll is 1.15 Mb in size! I didn't want to create a full blown static analysis engine - rather something akin to a lightweight Sax Parser.

I have used this class library in two main applications, firstly in AgileSpec (see blog post: http://blogs.conchango.com/howardvanrooijen/archive/2004/12/07/397.aspx for more info) - an application that generates light-weight specification documents from unit tests and secondly in various CodeSmith templates for generating derived types from interfaces (see blog post: http://blogs.conchango.com/howardvanrooijen/archive/2005/01/06/703.aspx for more info).

January 10, 2005 07:56
 

howard.vanrooijen said:

Great stuff. However, I downloaded it and here's the test results. Any ideas?


TestCase 'Conchango.Code.Reflection.UnitTests.TestAssemblyReflector+EventParser.Contains1' failed:
expected:<1>
but was:<0>
c:\documents and settings\shanselm\desktop\assemblyreflector 1.0.0\conchango.code.reflection-src-v1.0.0.0\conchango.code.reflection.unittests\testassemblyreflector.cs(559,0): at Conchango.Code.Reflection.UnitTests.EventParser.Contains1()

TestCase 'Conchango.Code.Reflection.UnitTests.TestAssemblyReflector+EventParser.Contains1Overload1' failed:
expected:<1>
but was:<0>
c:\documents and settings\shanselm\desktop\assemblyreflector 1.0.0\conchango.code.reflection-src-v1.0.0.0\conchango.code.reflection.unittests\testassemblyreflector.cs(611,0): at Conchango.Code.Reflection.UnitTests.EventParser.Contains1Overload1()

TestCase 'Conchango.Code.Reflection.UnitTests.TestAssemblyReflector+EventParser.Contains1Overload2' failed:
expected:<1>
but was:<0>
c:\documents and settings\shanselm\desktop\assemblyreflector 1.0.0\conchango.code.reflection-src-v1.0.0.0\conchango.code.reflection.unittests\testassemblyreflector.cs(663,0): at Conchango.Code.Reflection.UnitTests.EventParser.Contains1Overload2()

TestCase 'Conchango.Code.Reflection.UnitTests.TestAssemblyReflector+EventParser.EndsWith' failed:
expected:<1>
but was:<0>
c:\documents and settings\shanselm\desktop\assemblyreflector 1.0.0\conchango.code.reflection-src-v1.0.0.0\conchango.code.reflection.unittests\testassemblyreflector.cs(702,0): at Conchango.Code.Reflection.UnitTests.EventParser.EndsWith()

TestCase 'Conchango.Code.Reflection.UnitTests.TestAssemblyReflector+EventParser.EndsWithOverload1' failed:
expected:<1>
but was:<0>
c:\documents and settings\shanselm\desktop\assemblyreflector 1.0.0\conchango.code.reflection-src-v1.0.0.0\conchango.code.reflection.unittests\testassemblyreflector.cs(715,0): at Conchango.Code.Reflection.UnitTests.EventParser.EndsWithOverload1()

TestCase 'Conchango.Code.Reflection.UnitTests.TestAssemblyReflector+EventParser.EndsWithOverload2' failed:
expected:<1>
but was:<0>
c:\documents and settings\shanselm\desktop\assemblyreflector 1.0.0\conchango.code.reflection-src-v1.0.0.0\conchango.code.reflection.unittests\testassemblyreflector.cs(728,0): at Conchango.Code.Reflection.UnitTests.EventParser.EndsWithOverload2()

TestCase 'Conchango.Code.Reflection.UnitTests.TestAssemblyReflector+EventParser.Named' failed:
expected:<1>
but was:<0>
c:\documents and settings\shanselm\desktop\assemblyreflector 1.0.0\conchango.code.reflection-src-v1.0.0.0\conchango.code.reflection.unittests\testassemblyreflector.cs(741,0): at Conchango.Code.Reflection.UnitTests.EventParser.Named()

TestCase 'Conchango.Code.Reflection.UnitTests.TestAssemblyReflector+EventParser.NamedOverload1' failed:
expected:<1>
but was:<0>
c:\documents and settings\shanselm\desktop\assemblyreflector 1.0.0\conchango.code.reflection-src-v1.0.0.0\conchango.code.reflection.unittests\testassemblyreflector.cs(754,0): at Conchango.Code.Reflection.UnitTests.EventParser.NamedOverload1()

TestCase 'Conchango.Code.Reflection.UnitTests.TestAssemblyReflector+EventParser.NamedOverload2' failed:
expected:<1>
but was:<0>
c:\documents and settings\shanselm\desktop\assemblyreflector 1.0.0\conchango.code.reflection-src-v1.0.0.0\conchango.code.reflection.unittests\testassemblyreflector.cs(767,0): at Conchango.Code.Reflection.UnitTests.EventParser.NamedOverload2()

TestCase 'Conchango.Code.Reflection.UnitTests.TestAssemblyReflector+MethodParser.EndsWithOverload1' failed:
expected:<5>
but was:<6>
c:\documents and settings\shanselm\desktop\assemblyreflector 1.0.0\conchango.code.reflection-src-v1.0.0.0\conchango.code.reflection.unittests\testassemblyreflector.cs(1003,0): at Conchango.Code.Reflection.UnitTests.MethodParser.EndsWithOverload1()

65 succeeded, 10 failed, 0 skipped, took 0.18 seconds.


January 12, 2005 01:09
 

howard.vanrooijen said:

:)

Indeed if you want things lightweight, Microsoft.Cci is not the best solution.

I was wondering about your event-listener approach. Wouldn't it be more effective to create a visitor that explores the entire tree and this visitor would be inheritable. That's usually how you implement this kind of stuff (Reflector and Cci use this pattern)

public abstract class Visitor
{
public virtual void VisitAssembly(Assembly asembly)
{
foreach(Type type in Assembly.GetExportedTypes())
VisitType(type);
}

public virtual void VisitType(Type type)
{
...
}
}
Cheers,
Peli
January 12, 2005 05:36
 

howard.vanrooijen said:

Scott,

Sorry, that was me being a total jack of clubs and merging the wrong two branches of code (that's what I get for doing a deployment at 1am!) - the Unit Test Suite was expecting a newer version of the TestAssembly.dll. The Actual AssemblyReflector assembly is fine. I've uploaded a new release to project distributor:

http://projectdistributor.net/Releases/Release.aspx?releaseId=75

Hopefully NUnit should be totally green now.

Many Thanks for spotting this,

/Howard
January 12, 2005 08:36
 

howard.vanrooijen said:

Hey Jonathan,

Define "effective" :)

1) It's implemented in 1,581 lines of code (excluding comments)

2) It's pretty damn quick

3) Implementing a different member type means you inherit from MemberParserBase and override 1 method (if you want to differentiate the event args) or 2 methods if you want to do custom decisions / or raise new events.
the

foreach(Type type in Assembly.GetExportedTypes())

part of your example above has just been replaced with

this.assemblyReflector.Methods.Examine(type.GetMethods(this.assemblyReflector.BindingFlags));

So in essence, I thought it was a slightly bastardised Visitor Pattern (if you look at the definition here http://www.dofactory.com/Patterns/PatternVisitor.aspx ) with events thrown in for good measure :)

4) It's really easy to implement a new Validation Rule (I was going to change the interface so it would accept MemberInfo so you can do some more complex searches, say by return type etc).

5) An event-based approach makes it really simple to do introspection in Unit Tests (see the comment I posted http://www.hanselman.com/blog/CommentView,guid,2c115b60-1750-4754-8a01-ee6660dfef2b.aspx ) or Code Generation - it's much cleaner than having lots of verbose foreach loops.

Thanks for giving so much thought to my little hobby project!

/Howard
January 12, 2005 09:02
 

TrackBack said:

January 12, 2005 10:03
 

howard.vanrooijen said:

Indeed the event based visitor is nice. I used it a lot in my QuickGraph algorithms. I guess both approach have their good and bad sides.
January 12, 2005 19:59
 

TrackBack said:

The Daily Grind 538 - Wednesday, January 12, 2005
January 13, 2005 15:39
 

TrackBack said:

.NET Tools 2005
January 18, 2005 08:48
 

TrackBack said:

Karen Watterson's Weekly Destinations and Diversions (D & D) - 15 January 2005
January 18, 2005 08:49
 

TrackBack said:

February 10, 2005 12:06
 

unruledboy said:

i noticed that I have to use StartWith or something like that to trigger the discovery to perform. And I do not know how to get all the members(types, events, properties, functions etc).

btw, since objects are returned through event, is there a sample to show how to populate a treeview?
August 24, 2005 07:31
 

Howard van Rooijen's Blog said:

Updated 06/11/2006: If you are trying to locate the Netron Project see my post &quot; What happened to

November 6, 2006 16:31

Leave a Comment

(required) 
(optional)
(required) 
Submit

This Blog

Syndication

Powered by Community Server (Personal Edition), by Telligent Systems