Tuesday, February 12, 2008

How to Test a Refactoring

For this post I am going to assume that you are familiar with the basic process of unit testing using a tool such as NUnit.

I will now lead those who are interested through how I have created a simple test around Create Handler Stub.

Project Setup
The basic procedure for creating a suitable Refactor test project is:

  • Create a new Plugin Project
  • Remove the default plugin class
  • Add Reference to DevExpress.DXCore.Testing.dll
  • Add Reference to DevExpress.Refactor.Testing.dll
  • Add a class which inherits from DevExpress.Refactor.Testing.RefactoringTestFixture
  • Implement readonly string properties : 'FixtureName' , 'RefactoringName' and 'Language'

Tests
Next you will need to add suitable tests to your class.
Typically you are looking to test 2 things.

1.> The refactoring is/isnot available in a given situation
2.> The refactoring has a given effect on a given set of code.

Test by Example
DevExpress' Refactoring testing uses a very intuitive 'test by Example' style for it's testing

I call it 'test by example' because that's exactly what you do. You add example code files as resources to the project showing when you expect a refactoring to be available and what you expect to happen when it is activated.

Resource Files
The Resource files are simple code examples with small quantities of marked up used to provide the test framework with additional information such as the locate of the caret or an indication of what is selected.

[Note: These Resource files cannot be compiled in the normal fashion. The VB and C# compilers have no idea how to deal with the markup. Instead these files need have their 'Build Action' set to 'Embedded Resource'. They will be read later by the testing environment]

CheckAvailability Test (Source code)
So for our first test we are going to check availability for a given set of source. The Test Source is as follows:
-------------------------------------------------------------
Namespace Test
    Public Class TestClass
        Public Sub TestMethod()
            Dim X As New System.Windows.Forms.Button
            AddHandler X.Click, AddressOf OnClick<<:caret:>>
        End Sub
    End Class
End Namespace
-------------------------------------------------------------

Markup
The <<:caret:>> markup represents (strangely enough) the position of the caret in the test scenario. The << is not the « from CodeRush fields but instead just 2 sets of <.

So we save the above source code as 'Availability_1.vb' in a folder called  "Examples".

Next we have to create the Test itself. 
-------------------------------------------------------------
<Test("Availability for incomplete AddHandler Statement", "Refactor_CreateStubForHandler_Tests", "Availability_1.vb")> _
Public Sub TestAvailability_1(ByVal sender As Object, ByVal ea As TestEventArgs)
    Call AssertAvailable()
End Sub
-------------------------------------------------------------

.. I expect the previous code to be mangled by BlogSpot but the overall gist is simple.

In the constructor for the Test attribute, I have passed the name of the test, the path to the embedded resource needed and the name of the resource file used in this test.

[Note: When more than one test exists per refactoring, it is more than reasonable to extract the resource path out into a constant]

The call to AssertAvailable uses the Test attribute defined on the test to determine the location of the resource file which represents the scenario to test.

When it runs, it locates the resource and constructs the scenario in question, places the cursor at the given location and then tests the availability of the refactoring.

Running your test
In order to run the test you will require 'DX_TestRunner.dll' (Available by request through support@DevExpress.com according to Mark Miller). This should be placed in your plugin directory and when you run a copy of VS, you'll have an additional diagnostic ToolWindow available called 'Test Runner'

This looks like this:

image

You can now highlight your test or fixture and click the tick mark to check if your refactoring is available when it should be. If all goes to plan you should see something that looks like the following:

image

...together with a main editor window which confirms the results.

image

So that's how to make sure that your refactoring is available when you said it should be, without having to manually check each and every situation manually.

Next time I'll expand on how to check the your refactoring affects your code in the way that you said it should.

[Important Note: None of this information would be here if it were not for the very generous cooperation of Mr Alex Zaharov and Mr Mark Miller who lets face it, have a lot more to do than answer my questions. Thanks very much for your time guys]


Thursday, January 17, 2008

QuickTemplates

I was recently asked how to install QuickTemplates.

This is a perfectly reasonable request, however until now there has not been an online assembly.

So now I present QuickTemplates.DLL (Zipped).

This has been compiled against DXCore 3. (since I don't work with anything less than the latest bleeding edge stuff :))

So just drop it in "C:\Program Files\Developer Express Inc\DXCore for Visual Studio .NET\2.0\Bin\Plugins" ( by default) and Away you go.

You'll need to bind a key to the "Create Quick Template" action on the Shortcuts page in Options.

-------------------------------------------------------------
Follow these steps to get to the Shortcuts options page:

1. From the DevExpress menu, select "Options...".
2. In the tree view on the left, navigate to this folder:

    IDE

3. Select the "Shortcuts" options page.
-------------------------------------------------------------

This is still rough and ready, but it does the job.

Feel free to post comments or email if you need more help :)


Wednesday, January 02, 2008

A Simple Lazy-Load CodeRush Template

I can't tell you how often I've written this code manually.image

Or alternatively...

image

So I have finally created a couple of CodeRush Templates to facilitate this.

LLS (Lazy load Static) - Triggers the first expansion.
LLM (Lazy Load Module) - Triggers the 2nd expansion.
LL - Is aliased to LLS.

Templates are lower case in reality, but lowercase 'l' is easily confused with upper case 'I' in some fonts.

Feel free to download them from here and import them for your own use.


How to Rebuild your CodeRush Templates

Warning: Please be sure that you have Exported any Custom Templates that you care about before you attempt this.

The templates that you use day to day are stored in your user profile. If you need to rebuild them for some reason, then CodeRush will do so from it's own internal store which does not contain any customizations that you may have made.

Delete your existing Templates.

Deleting your existing Templates is a simple matter of finding the appropriate file and deleting it. These are organised by language under your profile directory.

Mine is located at "C:\Users\Rory.Becker.MyDomain\AppData\Roaming\CodeRush for VS .NET\1.1\Settings\Editor\Templates"

In this folder I have a number of files. I am only interested in rebuilding my Visual Basic ones but you should be able to find suitable files for each language you have been using.

If you are also working with VB.Net, then you should at this point delete (or Move if you're paranoid) 'templates.Basic.bin' and 'templates.Basic.xml'.

Rebuild your Templates.

Next you'll need to perform the actual rebuild.

-------------------------------------------------------------Follow these steps to get to the Templates options page:

1. From the DevExpress menu, select "Options...".
2. In the tree view on the left, navigate to this folder:

    Editor

3. Select the "Templates" options page.
-------------------------------------------------------------

Now if you make sure of viewing the templates for the language that you wanted to rebuild, you will find that all the standard templates are back.

Next you'll want to Import your Custom Templates


How to Import CodeRush Templates

-------------------------------------------------------------Follow these steps to get to the Templates options page:

1. From the DevExpress menu, select "Options...".
2. In the tree view on the left, navigate to this folder:

    Editor

3. Select the "Templates" options page.
-------------------------------------------------------------

Now...

  1. Select the folder containing the templates that you wish to export.
  2. Right Click the Folder
  3. Select Import Templatesimage 
  4. Locate an XML file containing previously exported Templates.
  5. Click Ok and you're done.

How to Export CodeRush Templates

So why would you want to export templates anyway?

Well Imagine that you've just downloaded the latest CodeRush build. version 3.0.5 at the time of writing.

Clearly you want to be able to take advantage of all the new templates that the geniuses (is that a word - Seems like it should be Genii) at DevExpress may have added.

But CodeRush is polite and understands that you may have added your own customizations to the existing template set and so the behaviour for CodeRush is to not alter your existing Templates (Stored in your Profile) but to do nothing ... just in case.

But you still want your new templates right? So how do you do it?

Well first you organise your own templates in a folder called (by convention) "custom". Then you export that folder of Templates and then ask CodeRush to rebuild you templates from scratch and finally Import your own templates back into CodeRush.

Simple right?

Well yes, but just in case, I have provided steps for each.

Exporting you custom Templates.

-------------------------------------------------------------Follow these steps to get to the Templates options page:

1. From the DevExpress menu, select "Options...".
2. In the tree view on the left, navigate to this folder:

    Editor

3. Select the "Templates" options page.
-------------------------------------------------------------

Now...

  1. Select the folder containing the templates that you wish to export.
  2. Right Click the Folder
  3. Select Export Folder image
  4. Select a location on disk to export to.
  5. Click Ok and you're done.

Wednesday, December 12, 2007

CreateStubForHandler - How does it work?

Our good friend Koen Hoefkens suggests that there might be some interest around the inner workings of the newly refactored CreateStubForHandler.

Versions
There are 2 versions of the plugin. One for DXCore 2.x (for VS2003) and another for DXCore 3.x (for VS2005 and VS2008).

This was done for 2 reasons:
Koen (the original author) has a few projects kicking around in VS2003, and so we needed to maintain a version of the code capable of producing a version of the plugin that could likewise run under the DXCore 2.x and hence under VS2003. I have a virtual machine setup to run VS2003 under XP and so I am in a position to test this project at my end if need be.

DevExpress have recently released a beta of DXCore 3.0 along with companion copies of RefactorPro and CodeRush. Now these new betas (like the full versions which will follow) only run inside of VS2005 or better but provide some interesting features which are not available in previous versions. Therefore I thought it would be good to create a version of the code which would take advantage of these new features where possible.

So to meet both of these requirements, we have divided the project into 2 parts and therefore 2 directories. The main trunk of the project contains directories called 'VS2003' and 'VS2008'. These house the Solutions and further directories and Projects suited to those environments.

Triggering the plugin
Each Project contains a plugin class created from a template drawn from the appropriate DXCore. These Plugin classes each use techniques and technology again suited to the version of the DXCore that they support. The DXCore 2.x uses an Action and a RefactoringProvider to trigger the plugins main functions, while the VS2008 version uses the same action logic together with a CodeProvider to achieve a similar effect.

The inner workings are the same for each due to their sharing of a common "PluginLogic.vb" file which lives in the VS2003 folder structure and is linked to from the VS2008 solution.

How does it work?
Essentially what happens is that whether activated by Action, RefactoringProvider or CodeProvider, much the same thing happens.

We have a main function "CreateMethodStub" which is passed 3 parameters:

  1. CaretElement: A LanguageElement which represents the element of code next to the caret at the time of activation. We can use a LanguageElement here because we're talking about an object on the sourcecode.
  2. InsertionPoint: A SourcePoint which tells the plugin where the newly generated method should be placed.
  3. CompleteStatement: A boolean indicating whether or not the AddHandler/RemoveHandler statement requires completion.

This function is responsible for organising the various stages of the plugin's operation. You can think of it as the core of this plugin. It delegates to other functions to do almost everything and then triggers the expansion of the code into the Active Document.

Build the New Method
The first step in generating code using the DXCore is to choose the correct root LanguageElement. That would be "Method" in this case since we are trying to create a method.

Next it's all about adding other suitable LanguageElements to that Method in the correct places and waving the "DXCore Magic Code Generation Wand".

Creating a method is easy:
-------------------------------------------------------------
Dim Method As New Method(MethodName)
Method.MethodType = MethodTypeEnum.Void
-------------------------------------------------------------
..Next we need to add some parameters which is normally easy also:
-------------------------------------------------------------
Dim SomeParam as New Param("System.String",ParamNameHere)
-------------------------------------------------------------

Constructing the correct signature
The difficulty here is that we can't just create any old parameters. We have to create parameters suited to the Event for which we are creating our handler method.

So how do work out what those are then?

If you need a quick refresher on why we're using IElement derived interfaces then take a quick peek here

Well we ask the DXCore for the declaration of the Event which we are attempting to attach to like this....
-------------------------------------------------------------
Dim TheDelegate As IDelegateElement = CType(EventDec.Type.GetDeclaration, IDelegateElement)
-------------------------------------------------------------
... the iterate the parameters of said delegate duplicating the params on the method...
-------------------------------------------------------------
For Each Param As IParameterElement In TheDelegate.Parameters
    Dim NewParam As New Param(Param.Type.FullSignature, Param.Name)
    NewParam.Direction = Param.Direction
    Method.Parameters.Add(NewParam)
Next
-------------------------------------------------------------

Add Some Content
Next we need to add some default content to that Method. We have decided to continue the original plan for now, and generate a statement to Throw a NotImplementedException.

So we offload the creation of the ThrowStatement on another simple utility function just so that our code can remain tidy.

Now we could just add this ThrowStatement to our Method.
this would be done by calling...
-------------------------------------------------------------
Dim TheThrow as ThrowStatement = GetThrowStatement("System.NotImplementedException")
Method.AddNode(TheThrow)
-------------------------------------------------------------
...but We'd like to be a bit more clever with our plugin.

Although the Throw statement is a good default, in 90% of cases the first thing you'd like to do once your plugin has finished doing it's thing, is to erase the throw and replace it with something more useful.

So how about we select the ThrowStatement ready to be overtyped, just like in a template.

In order to do this you need to Wrap the code in special Template Elements. If you've every created your own templates, you'll be familiar with «Caret» and «BlockAnchor». They are placed one at either end of your code, and once "Expanded", will cause CodeRush to select the text in the document

There are 2 problems with this.

  • We don't have any code to wrap
  • We don't have any LanguageElements to represent Template Elements.

Hmmmm.....what to do?

Ok it turns out that there is a special LanguageElement for inserting arbitrary text into your code. It's called SnippetCodeElement

So the first implementation of this looked a bit like....
-------------------------------------------------------------
Method.AddNode(New SnippetCodeElement("«Caret»")
Method.AddNode(GetThrowStatement("System.NotImplementedException"))
Method.AddNode(New SnippetCodeElement("«BlockAnchor»"))
-------------------------------------------------------------
But this had the annoying side effect of inserting a CRLF after the Throw and before the «BlockAnchor». In essence the CRLF got selected as well and this just wasn't right.

So a new approach had to be found. So I asked (as I did for much of this) AlexZ (of Developer Express), if he could explain how they did this and I received and embarrassingly simple solution.

Alex suggested pre-generating the code for the ThrowStatement, deliberately removing the trailing CRLF, wrapping the resultant code in the raw Template Element text and finally adding the whole lot to the method as a single SnippetCodeElement thus...

-------------------------------------------------------------
Dim ThrowStatement As [Throw] = GetThrowStatement("System.NotImplementedException")
Dim ThrowWithoutCRLF As String = GenerateWithoutCRLF(ThrowStatement)
Dim NewSnippetCodeElement As New SnippetCodeElement("«Caret»" & ThrowWithoutCRLF & "«BlockAnchor»" & ControlChars.CrLf)
Method.AddNode(NewSnippetCodeElement)
-------------------------------------------------------------

Producing the Code
Finally we wave that "DXCore Magic Code Generation Wand" I mentioned earlier by calling...
-------------------------------------------------------------
CodeRush.Language.GenerateElement(TheMethod)
-------------------------------------------------------------
...and the inserting that code in the correct location in our original document...
-------------------------------------------------------------
CodeRush.Documents.ActiveTextDocument.ExpandText(InsertionPoint, ControlChars.CrLf & MethodCode)
-------------------------------------------------------------

And now we're done. :)

Full source is available via SVN


Updated: the previously existing repository is still intact however the code has been copied to a new repository where future work will be continued from. this new repo can be found here here

Tuesday, December 11, 2007

LanguageElements, LiteElements and IElements

It's important to understand at least 2 out of these 3 terms when creating DXCore plugins.

  • A LanguageElement is a structure that represents a code structure defined in source code.
  • A LiteElement is a Friend (Or internal) class used internally by the DXCore. It represents a code structure defined in IL (like the framework itself). This is the one you don't really need to understand because you can't cast the object itself to this type , because this type in unavailable to you, the humble plugin programmer.
  • An IElement is an interface implemented by the previous 2 structures. It provides common access to properties provided by the previous structures allowing you to code against this interface rather than have to code against the previous 2.

These 3 structures are of course the ultimate parent structures of many derivatives.

So you should write your plugin analysis code in terms of IElement derived interfaces.

Of course you'll need to create LanguageElement derived objects when you need to generate code, but try to use the interfaces for any discovery work you need to do.

If you don't do things this way, then you'll find that you need to use LanguageElements to analyse the source code and Reflection to go looking through the framework classes.

Believe me... You Don't Want To Do This!

Apart from which, using the IElement derived structures in the DXCore is way easier and results in tighter plugin code all around

A small example
So imagine that you have the code...
-------------------------------------------------------------Public Structure SomeStruct
    Private DummyMember As Integer
End Structure
Public Class SomeClass
    Private X As SomeStruct
    Private Y As Integer
End Class
-------------------------------------------------------------
The return value from GetDeclaration for X will be an "IElement" which you can cast back to "Struct" which is a descendant of LanguageElement.

The return value from GetDeclaration for Y will be an "IElement" which you can would be able to cast back to "LiteStructElement", only it's not available from outside of the StructuralParser Assembly.

Both of these can be addressed in a uniform way however, by casting them to IStructElement

Note: The theory goes that since most LanguageElement derivatives will have a matching IElement derivative. you should attempt to work in terms of IElement when trying to analyse code. Simply because you should not assume that that which you are trying to analyse is modifiable.

So hopefully that sorts out any confusion as to why IElement derivatives are used so prolifically in plugins.


Friday, December 07, 2007

Plugin Development under Vista 64-bit

There are a minor issue affecting the development of plugins under 64-bit vista.

The basic issue is that your installation of Visual studio (assuming that you go for the defaults) will be under "C:\Program Files (x86)\" and defaults in a plugin project (and indeed other types of project) will point to a directory under "C:\Program Files\".

Now CodeRush /Refactor/DXCore will all install (by default) also under "C:\Program Files (x86)\" and it seems very hard to convince the OS to allow anything else.

All this means that you will compile and build plugins to a directory ( C:\Program Files\..etc..") which the DXCore is not looking in.

The simplest way I have found to solve this is to use a program like Junction Link Magic to create a "Junction Point" between a new Empty folder created at "C:\Program Files\Developer Express Inc\" and the real folder at "C:\Program Files (x86)\Developer Express Inc\".

A "Junction Point" is a "Magic Portal" between the empty folder and the specified destination. The effect of which, is to fool the FileSystem, and hence every program that makes use of it, into seeing all the files in both locations.

It should be noted that there is only one set of files, they are just visible in 2 places. If you delete anything it will vanish from both locations

So once this is done you can happily compile Dlls to one location and run them through DXCore from the other.


CreateStubForHandler Plugin

Those who have been following this thread in the DevExpress Forums will already be aware of my involvement in a new plugin for VB developers called CreateStubForHandler

This plugin was created by Koen Hoefkens to aid VB.Net developers in their daily tasks.

The CreateStubForHandler plugin does 1 wonderful thing in 2 specific ways.

Refactoring/CodeProvider

Imagine you have this line of code...

-------------------------------------------------------------
AddHandler MyButton.Click, AddressOf MyClickHandler
-------------------------------------------------------------
...but you do not yet have and actual 'MyClickHandler'.

Typically you now have to either have a good memory for the parameters needed for the handler or you have to go trawling through the intellisense/tooltips/Object browser to determine what such a handler should look like.

Well now, with a simple refactoring (CodeProvider in Refactor3.0), you will be presented with a 'Target Picker' which you can use to determine the location that 'CreateStubForHandler' will create just such a handler.

The CodeProvider does all the heavy lifting for you. It creates the procedure and populates the signature with all the correct parameters.

It also fills the procedure with a simple throw exception statement.....
-------------------------------------------------------------
Throw New NotImplementedException()
-------------------------------------------------------------

Action

As an additional bonus, there is also an additional "Action" ("AddHandler completion") which you can map to a key of your choosing. This action should be activated when you have the following code...
-------------------------------------------------------------
AddHandler MyButton.Click
-------------------------------------------------------------
... with your caret on the right of said code. (note the lack of a comma)

Once activated, "AddHandler completion" will not only create the procedure for you but will calculate an appropriate default name for the method, but will fill out the "AddressOf" clause for you

Koen was generous enough to allow his plugin to be added to the DXCorePlugins Open Source project on GoogleCode.
So if you're interested you can get the full source to this plugin as well as a few others.

Great stuff!

Thanks Koen

Note: In truth, my only involvement in the project was a little refactoring of the code itself and the creating of a parallel VS2008 project to house the Refactor 3.0 version of the code

I have not added any functionality to the project (unless you count changing the VS2008 version to use a CodeProvider rather than a RefactoringProvider.)


Wednesday, December 05, 2007

Something, Beta, this way comes.

The 1st beta of CodeRush 3.0, Refactor 3.0 and therefore of course DXCore 3.0 is now available to subscribers of DevExpress' IDE Tools.

NOTE: These will replace version 2.x if installed and support has now been dropped entirely for VS2003 as all libraries are not build using .Net 2.0

Refactor! Vs Code!

imageRefactor has had a makeover and split it's offerings into "Refactor" items and "Code" items. At the moment, the pre-existing Refactor items are available in the Refactor! section. These are the items that take your code and reshape it, improving the readability and reusability without altering the effect it produces. image

Next up is the all new Code! section. The Code! section is for items which, unlike their Refactor siblings, do alter the code they act upon. Items like "Rotate item 90 degrees" act on some selected code and modify it in some way.  To quote Mark Miller:

'Rotate 90 Degrees' is most useful, taking logic that applies in one direction, and rotating it to apply in the other direction (converting Width to Height, X to Y, Left to Top, Right to Bottom, Column to Row, etc.), while 'Mirror Code' is less useful (converting Left to Right, "+" to "-", Top to Bottom, etc).

Other items available in Code! include the "Declare" range. These allow you to generate Classes, Structs, Interfaces, Constructors, Locals, Fields, Properties, Setters and Getters from references.

Code Issues

Refactor has always suffered from a small problem which was that you had to know that a Refactoring could be used before you could use it. So you would place your caret somewhere in code and if Refactorings were possible, then you would see the Refactoring SmartTag which lead to the SmartMenu which would then show you the refactorings that could be applied in the current location.

But if you never placed your caret in the correct location then you would have to rely on past knowledge to tell you where you might be able to refactor.

Code Issues is a promising little piece of architecture which will allow CodeRush to highlight code smells and bring to your attention, those items which could do with a little refactoring.

In the beta there are 2 Code Issues that are  highlighted. 'Undeclared Elements' and 'Unused Declarations'. This means that if you declare a variable or method but do not use it, the declaration with be highlighted and your attention drawn to it.

When I first started to use the new version of CodeRush, imageI thought I was suffering some strange programming glitch. I appeared to have some kind of imagegraphical corruption on the right hand side of my editor. It turns out that this is in fact a graphical map (see right) of the code Issues in the current document (Left).

The purpose of these would seem to be to help further draw your attention to your code issues. I have seen no sign of a Code Issues ToolWindow so far, but then this is the first beta and there are some issue preventing me at this time from accessing some of the more traditional ToolWindow. (CodeRush, Messages, Expression Lab).

As I have said there are only 2 Code Issues Providers so far but another quote from Mark Miller reveals a little about what is to come:

With regard to error checking, the goal is to ultimately implement all of the errors, warning, and hints produced by each of the language compilers we support. With regard to code smells, that's a much broader area, and for the most part we won't be talking about what we'll be addressing there until its released, other than to say that if we have a refactoring or code provider designed to solve a problem, it is highly likely that you'll see us ship a corresponding code issue provider to highlight opportunities to improve the code.

So stay tuned and I'll see what else I can find out for you.


Tuesday, December 04, 2007

My last Vista problem - Solved!

For those not keeping up... The last issue I have with Vista was detailed here.

The basic problem was that administrator programs were not recognising clicks events when they were not the foreground application.

The today I noticed, completely by accident, that My VS (a "Run as Administrator" application) DID recognise a "right click".

How strange....

That set me thinking about click messages and interception and that's when it hit me..... I run a third party App (Nifty Windows) for it's ability to toggle the StayOnTop bit of any window I choose. It does this by intercepting the Win+LClick key/Mouse event.

The problem seems to be that "Nifty Windows" intercepts the left click but is apparently (I'm guessing due to Vista security practices) is unable to send those clicks to any program which is elevated.

The solution: run Nifty Windows Elevated

Oh the relief :D


Wednesday, November 28, 2007

Extension methods and .Net 2 Revisited

It appears that, in VB, you will be unable to create the requisite Attribute unless you have "no root namespace".

Now that's annoying. All my projects have root namespaces.

If I understand things correctly the difference between C# and VB in this respect is....

...In C# this value is copied into each source file as each is created.
...In VB.Net this value is used at compile-time (and background compile-time) and every class in the system is wrapped in said namespace.

This means that a large VB.Net program is very sensitive to any changes to this particular value.

If I keep my default namespaces, then I cannot create this attribute in this namespace. I had thought I could declare the Attribute using "Namespace Global.System.Runtime.CompilerServices" but the keyword cannot be used in this way.

It looks like I will have to create a brand new project, to create a brand new dll, just so that I can put a single attribute in it.

It seems that this is another Compiler/Language deficiency for which the only answer is to create another assembly.

Perhaps I will call my new dll "System.Core.Dll" :D


A simple correction

I previously reported here, a method of using "Extension methods" in a .Net 2.0 targeted project. 

Originally I stated that the extension attribute needed to be created within the "System.ComponentModel.CompilerServices" namespace. This was incorrect and has now been corrected. the namespace in which you actually need to create said attribute is in fact "System.Runtime.CompilerServices".

Assuming that there's anybody out there actually reading any of this.......Sorry about that. I'll try to be more careful.


Wednesday, November 21, 2007

SOLVED - Multi-Target .Net 2.0 or Extension Methods

Update:Corrected "System.ComponentModel.CompilerServices" to "System.Runtime.CompilerServices"

Ok so it looks like "I was wrong". (well it's not like that hasn't happened before :))

NOTE: This information was found in an article called Basic Instincts in the November Edition of MSDN Magazine in a subsection entitled "Extension Methods in .NET Framework 2.0 Apps"

It's true that a .Net 2.0 targeted application cannot reference System.Core (The DLL that contains the Extension Attribute) but it turns out that you don't need to do this anyway.

Instead, you can create your own.

Yup with the following code.....
-------------------------------------------------------------
Namespace System.Runtime.CompilerServices
    Public Class ExtensionAttribute
        Inherits Attribute
    End Class
End Namespace
-------------------------------------------------------------
...in addition to the required import where necessary...
-------------------------------------------------------------
Imports System.Runtime.CompilerServices
-------------------------------------------------------------
...you too can fake out the IDE and have it compile your own String.ShowAsMessage Sub thus:
-------------------------------------------------------------
Public Module Ext
    <Extension()> _
    Public Sub ShowAsMessage(ByVal SomeString As String)
        MessageBox.Show(SomeString)
    End Sub
End Module
-------------------------------------------------------------
... and now you can do strange stuff like....
-------------------------------------------------------------
Call "Fred".ShowAsMessage()
-------------------------------------------------------------

The article further notes...

...this technique will not work for ASP.NET applications targeting the .NET Framework 2.0 because they have a runtime dependency on the 2.0 command-line compiler. When those apps are deployed to Web servers that only have the .NET Framework 2.0 installed, they will not work because the 2.0 VBC.EXE command-line compiler does not understand extension methods.

So I guess we just have to be careful.


Multi-Target .Net 2.0 or Extension Methods

It seems you have to choose to use VS2008 to target .Net 2.0 OR choose to use extension methods.

But you can't pick both.

ScottGu said in a comment on his blog that

Extension Methods are actually implemented entirely by the compiler - no new IL instructions within the CLR are required to support them.

This means that you can use the VS "Orcas" C# compiler and write code that uses Extension Methods, and use the multi-targetting features to run it on a vanilla .NET 2.0 box.

However the ExtensionAttribute (required to mask a method in a module as being an extension method) is not available to add to a project which targets .Net 2.0


SOLVED - COMException in VS2008 Conversion Wizard

The COMException is caused by a few projects in my solution being bound to IIS rather than to the built in Web Development Server. I Asked on the ASP.Net Forums and you can follow the conversation here.

Studio apparently needs some rights (that it doesn't get by default) to read some info from IIS.

The work around is to launch Studio with "Run as Administrator" permission and all should work as expected.

I still consider it a bug that studio doesn't explain itself properly, but this workaround will do for now.


Tuesday, November 20, 2007

COMException in VS2008 Conversion Wizard

The first serious project I am undertaking in VS2008 has stalled at the first hurdle.

I am attempting to upgrade a VS2005 Solution to VS2008.

This particular solution has approximately 15 projects, with 5 being WebApps.

When I open this solution in VS2008 and allow the Wizard to do it's thing, It fails on 3 of the 5 WebProjects with COMException. and nothing else.

Not the best error information.

Any thoughts?


[Update: I just realized that I had no pointer to my eventual solution.]

Monday, November 19, 2007

Whatever you do DON'T Download VS2008 PRO!..

...for at least the next 4 hours. I needs to get me a copy of this and I don't need the rest of you hogging all the bandwidth :P


VS2008 - Available to MSDN now!?

Well Daniel Moth thinks it is in his post here.

I can't see it though :(

Must mean it won't be long though :D