header
header Register : : Login header
header
divider
menuleft
menuright
submenu
left
Jan 16

Written by: Karl Prosser
1/16/2010 8:27 PM

Have you ever just wanted to run a line or two of C# from within PowerShell and consume the resulting objects from PowerShell? Before V2 you had to do some codedom yourself Plus write a full dotnet class yourself. With V2 you can just do add-type but still you have to write a full class just to run an expression. Anyway how about a year ago after getting fed up with doing that, and also after seeing Mono's C# commandline interface i thought lets do something quick and dirty that could do this.. with the script that will follow you can do stuff like (c being an alias for the function that compiles and runs a C# expression

(c DateTime.Now).adddays(5)
(c "new{a=1,b=2,c=3}").b
c 'from x in Directory.GetFiles(@"f:\downloads") where x.Contains("win") select x'

an interesting thing i found out, was that with the C# compiler in memory i can't create more than one anonymous type (2nd example above) with the same signature (i.e int,int,int ) so the compiler will try to create the exact same type again. So it may seem pointless to call DateTime.Now when you can just do [DateTime]::Now but thats not the point, the point was it was running C#, returning an object that you could immediately use in powershell. I threw a linq example in there just to show you a glimpse of the potential. So how do you do this. You could even do this in V1 with codedom generation but here i'm using PowerShell V2

function run-csharpexpression([string] $expression )
{
$global:ccounter = [int]$ccounter + 1
$local:name  =  [system.guid]::NewGuid().tostring().replace('-','_').insert(0,"csharpexpr")
$local:ns = "ShellTools.DynamicCSharpExpression.N${ccounter}"

$local:template = @"
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;

namespace $ns
{
public static class Runner
{
public static object RunExpression()
{
return [[EXPRESSION]];
}
}
}
"@

$local:source = $local:template.replace("[[EXPRESSION]]",$expression)

add-Type $local:source -Language CsharpVersion3 | out-Null
invoke-Expression ('[' + $local:ns + '.Runner]::RunExpression()')
}

Thanks to Oising http://www.nivot.org for playing around with this when i was building it. So if i wanted to really turn this into something industrial quality what would i do?

  • try to work out a way that i could create the same anonymous type twice..
  • deal with exceptions well
  • make a version that you can pass in objects from powershell to the expression, and that they could even be put in as fields on the runner class so you could reference them by name from C#
  • Make a version that is designed to be run as part of the pipeline, where the function wrapper would manage the PROCESS block and call the csharp expression once every PROCESS block invocation.
  • I would possibly make a special version of Generics since generics interop is often one of the harder things in Dotnet
  • I'd have it keep a track of all the classes it has made, and have functions to look at them and interact with them and maybe reuse them. Particularly if they are parameterized.

Of course if you were so pathologically inclined you could very easily adapt this to make a VB.NET version too. Enjoy. And if anybody feels inspired to make a better more industrial quality version i'd love to have a look at it.

Tags:

Your name:
Title:
Comment:
Add Comment    Cancel  
 

We have a new sponsor!  Introducting Pragma Systems.  See the home page for details.

Blogs
  
Search Blogs
  
Archives
  
right
   
footer Sponsored by Quest Software • SAPIEN Technologies • Compellent • Microsoft Windows Server 2008 footer
footer