Bygg ut dina applikationer med dynamic i .NET 4.0
I C# 4.0 så kommer ett nytt keyword kallat ”dynamic”. Precis som ”var” i C# 3.5 så säger det inget om vilken typ det är när man skriver. Skillnaden mot var är att var blir hårt typad vid kompilering, medan dynamic blir det vid runtime.
Så, varför skulle vi vilja typa variablerna vid runtime? När man arbetar med COM som i t.ex. Office-biblioteken så är inte allt hårt typat, när man anropar DOM:en via Silverlight så är det inte heller starkt typat mot det enskilta objektet och om vi vill köra kod i ett språk baserat på DLR så är det förstås inte heller starkt typat.
Det vi kan göra med dynamic är att vi kan anropa en metod i något av dessa bibliotek trots att .NET inte känner till att metoden finns under kompileringen. När vi däremot kör koden så ser .NET att objektet finns, och kör därefter den metoden vi just bad om.
Istället för att ta ett objekt och peka på en metod som vi vill anropa så tar vi ett objekt och säger att vi vill anropa en metod med ett visst namn.
Fördelen med det här är att vi enkelt kan anropa metoder som nödvändigtvis inte behöver när vi kompilerar, men som kommer att finnas när vi kör applikationen. Om nu inte metoden finns så får vi ett felmeddelande när vi anropar metoden.
Men varför vill vi blanda t.ex. C# och IronRuby i våra applikationer? Tänk dig att vi har en applikation som vi vill att användarna skall kunna bygga ut enkelt. Om vi skriver applikationen i C# och vill utöka funktionaliteten med t.ex. C# eller Visual Basic så kan vi använda MEF eller liknande för att få den funktionaliteten. Det här kräver dock att användaren skriver sin kod, kompilerar den och sedan lägger den i en mapp som vi sedan läser in. Fördelen med ett dynamiskt språk som IronRuby är att den inte behöver kompileras, utan kan köras direkt. Vi gör det därmed möjligt för personer att enkelt skriva ihop en fil som gör det de vill och sedan låter dem injicera koden i vår applikation.
I exemplet jag kommer att gå igenom så använder jag Visual Studio 2010 Beta 1 med .NET 4.0 Beta 1 och den versionen av IronRuby som finns tillgänglig för just dessa. IronRuby för .NET 4.0 Beta 1 finns att ladda ned här:
http://ironruby.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=27606
Vi börjar med att skapa ett console-projekt och lägger till referenser till Microsoft.Scripting, IronRuby och IronRuby.Libraries. Det finns även assemblies för IronPython för de som föredrar det.
Applikationen vi ska skriva nu kommer att hämta alla rb-filer (IronRuby) och köra en metod vid namn ”sayhello” i klassen ”RubyTest”.
Jag kommer att ha två olika rb-filer i exemplet, den ena skriver ut en rad direkt i programmet, och den andra kommer att visa en MessageBox med det inmatade värdet.
Console.rb:
class RubyTest
def sayhello
input = System::Console.ReadLine
System::Console::WriteLine "Ruby says: " + input
end
end
msgbox.rb
require 'System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
class RubyTest
def sayhello
input = System::Console.ReadLine
System::Windows::Forms::MessageBox.Show input, "Ruby says"
end
end
I båda fallen så läser vi in en sträng i programmet som vi sedan använder oss utav. Vi kommer alltså åt programmet utan att filerna på något sätt har med det att göra, utöver det faktum att de körs från det.
Det vi behöver göra nu är att läsa in filerna från console-programmet och köra sayhello-metoderna.
Grundstommen för vårt program ser ut så här:
using System;
using System.IO;
using IronRuby;
using Microsoft.Scripting.Hosting;
namespace IronRubyConsole
{
class Program
{
static void Main(string[] args)
{
DirectoryInfo di = new DirectoryInfo(Environment.CurrentDirectory + "\\scripts");
CallRuby(di.GetFiles("*.rb"));
Console.ReadKey();
}
private static void CallRuby(FileInfo[] files)
{
foreach (FileInfo file in files)
{
Console.WriteLine("{0}------------{0}{1}{0}------------", Environment.NewLine, file.Name);
ExecuteRuby(file.FullName);
}
}
private static void ExecuteRuby(string path)
{
}
}
}
Vi loopar igenom alla rb-filer i mappen “scripts” som ligger i samma mapp som exe-filen. Sedan anropar vi metoden ExecuteRuby() där vi kommer att köra filerna.
Koden som vi använder i ExecuteRuby är:
ScriptEngine engine = Ruby.CreateEngine();
engine.ExecuteFile(path);
object rubyclass = engine.Runtime.Globals.GetVariable("RubyTest");
dynamic rubytest = engine.Operations.CreateInstance(rubyclass);
rubytest.sayhello();
För att köra våra IronRuby-filer så behöver vi skapa ett ScriptEnginge-objekt som finns under Microsoft.Scripting.Hosting. Den ScriptEnginge vi använder är den som finns för IronRuby. Det finns även en för IronPython.
När vi har en ScriptEngine så kan vi exekvera filen som vi fick in via parametern.
Sedan anropar vi klassen RubyTest och skapar en instans av den.
Då variabeln ”rubytest” är satt som dynamic så kan vi anropa vilken metod som helst och kompilera, även om metoden inte är skapad än. Det kan vi se på den nedersta raden, ”rubytest.sayhello();”.
Om sayhello tar emot en parameter i IronRuby-filen så kan vi skicka med den som vanligt i anropet.
När vi nu kör applikationen och fyller den med text så får vi upp det här:
Vi kan alltså på ett väldigt enkelt sätt göra det möjligt för användarna av våra program att skriva script i IronRuby och andra dynamiska språk.
Jag har lagt upp projektet här (kräver .NET 4.0 Beta 1 för att kunna köras):