Blogroll

Search

Quick and Dirty C# Expressions from within Powershell.

January 16th, 2010 by Karl

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.

Posted in Powershell | 3 Comments »

3 Responses

  1. Joel "Jaykul" Bennett Says:

    Very cool. Have you played with using .Net 4 (or Mono) to do it?

    Since they have real read-eval-print it seems like it would be easier, and *possibly* get you out of your class problems…

  2. Doug Says:

    Nice concept. I like.

  3. Karl Says:

    i should build a .net 4 host to play with. that would be cool

Leave a Comment

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.