İlkay İlknur

just a developer...

Roslyn - Scripting API ve C# Interactive Window

Merhaba Arkadaşlar,

Bir önceki yazımızda sizlerle Compiler As a Service konseptini inceleyerek Roslyn projesinin temelini oluşturan kavramları ve servisleri kısaca ve tamemen teorik olarak incelemiştik. Bu yazımızdan itibaren Roslyn ile derinlere dalmaya başlayacağız ve uygulamalı olarak Roslyn’i kullanmaya başlayacağız.

Önceki yazımızda Compiler içerisindeki servislerin birer API ile dışarıya açılmasının getireceği yeniliklerden bahsederken ilk sırada programlama dillerinin scripting özelliği kazanmasını söylemiştik. Scripting özelliği ile aslında uygulamalar içerisinde C# veya VB kodlarının dinamik olarak çalıştırılmasından söz etmekteyiz. Özellikle IronPython gibi dinamik programlama dillerine baktığımızda bu şekilde scripting özellikleri programlama dilinin yapısından dolayı mümkün olabilmekte. Hatta C# 4.0 ile beraber gelen dinamik programlama yenilikleri ve DLR (Dynamic Language Runtime) ile uygulamalarımız içerisinde şu anda Dinamik Programlama dillerinin kodlarını çalıştırabilmekteyiz.

Ancak konu C# ve VB kodlarının dinamik olarak çalıştırılabilmesine geldiğinde Roslyn öncesine kadar böyle bir şeyi gerçekleştirmek bizler için çok kolay değildi. Bunun için Reflection ve CodeDom tarafında bir takım derleme işlemleri yapmak gerekmekteydi.

Roslyn içerisinde bulunan Scripting API ile artık C# ve VB kodlarını uygulamalar içerisinde hızlı bir şekilde işletip sonuçlarını alabilmekteyiz. Şimdi ilk olarak bir Console uygulaması yaratalım ve bu uygulama içerisinden kodlarımızı dinamik olarak çalıştıralım.(Makale boyunca görmüş olduğunuz kodları çalıştırabilmek için  Roslyn.Compilers ve Roslyn.Compiler.CSharp dll’lerini referans olarak eklemeniz gerekmekte.)

ScriptEngine engine = new ScriptEngine();
engine.Execute(@"System.Console.WriteLine(""Hello Roslyn !!!"");");
Yukarıdaki kodu doğrudan çalıştırdığımızda string olarak yazmış olduğumuz kodun C# compilerı tarafından işletildiğini ve Console uygulamamızın ekranına "Hello Roslyn !!!” yazdığını görüyoruz.

Şimdi biraz daha ileri gidelim ve değişken tanımlayıp bu tanımladığımız değişkeni bir alt satırda yazmış olduğumuz bir başka dinamik kod içerisinde kullanalım.
ScriptEngine engine = new ScriptEngine();
engine.Execute(@"int x = 5;");
engine.Execute(@"System.Console.WriteLine(x);");
Yazmış olduğumuz kodu çalıştırdığımızda bakalım ne ile karşılaşacağız :)

Compiler tarafından ilk olarak yaptığımız tanımlama işletildikten sonra 2. sırada yazmış olduğumuz tanımladığımız değişkenin değerini Console’a yazdıran kod işletildiği sırada yukarıdaki exception fırlatılmakta ve x değişkeninin context içerisinde bulunmadığı söylenmekte. ;)

Buradan da çıkaracağımız sonuç şu olmalı ki ScriptEngine tipi içerisinde bulunan Execute metoduna sadece string olarak verdiğimiz C# veya VB kodları işletildikten sonra bu kodlar içerisinde yapılmış olan tüm tanımlamalar compiler tarafından unutulmakta. Peki bu tanımlamaların unutulmaması için ne yapmamız gerekiyor ?

Execution Context

C# veya VB kodlarının dinamik olarak işletilmesi sırasında daha önceden işletilen kodlarının compiler tarafından unutulmaması için kodların içerisinde çalıştırılacağı bir context belirlememiz gerekmekte. Bunun için de Roslyn tarafında kodumuzun içerisinde işletileceği bir Session belirlememiz gerekmekte ve Execute metoduna parametre olarak vereceğimiz kodun yaratmış olduğumuz Session içerisinde işletilmesi gerektiğini bildirmemiz gerekmekte.

ScriptEngine engine = new ScriptEngine();
Session session = Session.Create();
engine.Execute(@"int x = 5;", session);
engine.Execute(@"System.Console.WriteLine(x);", session);
Şimdi biraz daha ileriye gidelim ve session içerisinde bir metot tanımlayalım ve daha sonra bu metodu çağıralım.
ScriptEngine engine = new ScriptEngine();
Session session = Session.Create();
engine.Execute(@"void Print (object x){ System.Console.WriteLine(x);}",session);
engine.Execute(@"int x = 5;", session);
engine.Execute(@"Print(x)", session);
Şu ana kadar Roslyn tarafında dinamik kod işletme ile ilgili olarak temel kısımları inceledik. Ancak incelememiş olduğumuz bir nokta kaldı. O da dinamik olarak işletmiş olduğumuz kodun aslında çalıştırılmış olduğu host uygulama ile nasıl iletişimde olacağı.

Host Object

Host uygulama ile iletişimde bulunmak için Roslyn tarafında Host Object yapısını kullanmamız gerekmekte. Host Object dediğimiz yapı aslında herhangi bir tip olabilmekte. Bu tip, tanımlamış olduğumuz session içerisine dahil edilmekte ve böylece biz de dinamik olarak işleteceğimiz kodlar tarafında bu host object içerisinde bulunan tüm public üyelere erişebilmekteyiz ve istediğimiz işlemleri gerçekleştirebilmekteyiz. Örnek olarak host uygulama içerisnde şu şekilde içerisinde sayıları tutan ve bir takım işlemler gerçekleştiren bir sınıf tanımlayalım.

public class NumberList
{
 List<int> numbers = new List<int>();

 
 void Add(int i)
 {
 numbers.Add(i);
 }
 void PrintNumbers()
 {
 for (int i = 0; i < numbers.Count; i++)
 {
 Console.WriteLine("{0}- {1}", i + 1, numbers[i]);
 }
 }
}
 

Yukarıda tanımlamış olduğumuz tip host uygulama olan Console uygulamamızda yer almakta. Şimdi sıra geldi bu tipi nasıl kullanacağımıza. Eğer bir tipi host object olarak kullanmak istiyorsak öncelikli olarak bu tipi ilgili Script Engine ve Session tiplerine bildirmemiz gerekmekte.

NumberList numberList = new NumberList();
ScriptEngine engine = new ScriptEngine(new[] 
 { typeof(NumberList).Assembly.Location });
Session session = Session.Create(numberList);
engine.Execute("Add(3);", session);
engine.Execute("Add(5)", session);
engine.Execute("PrintNumbers()", session);
Uygulamamızı çalıştırdığımızda aşağıdaki gibi bir çıktı ile karşılaşmaktayız.

C# Interactive Window

C# ve VB programlama dillerine Scripting özelliğinin gelmesiyle beraber yapılabilecekler düşünüldüğünde aklımıza ilk olarak REPL(Read-Eval-Print-Loop) uygulamaları gelmekte. Yukarıda yapmış olduğumuz örneklere baktığımızda aslında bir REPL uygulaması yapmak için hemen hemen tüm parçalar elimizde. Bu nedenle REPL uygulaması kısmını tamamen size bırakıyorum ;) Ancak yine de takıldığınız noktalar olursa zaten internette Roslyn ile yapılmış pek çok REPL uygulaması bulunmakta bunlardan yararlanabilirsiz ya da İletişim bölümünden bana ulaşabilirsiniz.

Makalemizin sonlarına gelirken makalenin bu bölümünde aslında yine Roslyn ile beraber gelen ve Visual Studio içerisine entegre edilen bir REPL uygulamasından bahsediyor olacağım. Roslyn paketini bilgisayarınıza yükledikten sonra Visual Studio içerisinde View => Other Windows yolunu izlediğinizde burada “C# Interactive Window” seçeneğini görüyor olacaksınız. Buraya tıkladığımızda karşımıza gelen aslında C# kodları ile çalışan bir REPL uygulaması. Bu uygulama ile hızlı bir şekilde istediğimiz kodu yazıp sonucunu görebilmekteyiz.

Şimdi isterseniz buraya bir takım kodlar yazalım ve sonuçlarını inceleyelim.

Gördüğünüz gibi aslında bir C# uygulaması içerisinde yapabileceğimiz pek çok şeyi bu Window içerisinde yapabilmekteyiz ve sonuçlarını hemen görebilmekteyiz. Peki diğer dil özellikleri bu Window içerisinde destekleniyor mu diye soracak olursanız aslında compiler tarafından sağlanan bilgiler ile çalışan outlining, colorizer, intellisense gibi özellikler de bu window içerisinde çalışmakta. Burdan da anlıyoruz ki aslında compiler tarafından sağlanan farklı farklı API’lar birbirlerinden bağımsız olarak beraberce çalışabilmekteler. ;)

C# Interactive Window içerisinde bilmemiz gereken en önemli komutlardan biri de library referansı ekleme komutudur. #r “LibraryName” komutu ile Interactive Window içerisinde çalıştıracağınız kodlar ile ilgili gerekli referansları ekleyebilirsiniz.

Bunun yanında Interactive Window içerisindeki en güzel özelliklerden biri de yazmış olduğunuz uygulama içerisindeki kodları hızlı bir şekilde işletebilme ve sonuçlarını inceleyebilme şansı vermesi. Bunun için çalıştırmak istediğiniz kodu seçip Ctrl+Enter yapmanız yeterli. Yazmış olduğunuz kod Interactive Window içerisine kopyalanmakta ve işletilmekte. ;)

Eğer bir takım referanslar Interactive Window içerisinde tanımlı değilse kopyalamış olduğunuz kod Interactive Window içerisinde çalıştırılamayabilir. Bu nedenle öncelikle uygulama içerisinde referansların Interactive Window içerisinde referans olarak eklendiğinden emin olmamız gerekmekte. Bunun için de ilgili Project’e sağ tıklayarak “Reset Interactive Window From Project”’i seçmemiz gerekmekte.

Bu noktadan sonra ise artık yapmamız gereken ilgili kodu seçip Ctrl+Enter ile C# Interactive Window içerisinde işletmek.

Not : Interactive Window şu anda sadece C# için bulunmaktadır. İlerleyen sürümlerde VB için de Interactive Window duyurulacaktır.

Gördüğünüz gibi Compiler As a Service (CaaS) konsepti oldukça geniş ve bir o kadar da eğlenceli bir konu :) Bu yazımızda her ne kadar sadece Scripting API’yi incelemiş olsakta sırf bu API’yı kullarak bile yapabileceğimiz pek çok şey bulunmakta. Bu yüzden bir C# veya VB developer olarak herkese Roslyn projesini takip etmesini öneriyorum.

Umarım sizler için faydalı bir makale olmuştur.

Not: Makale içerisindeki kodlar Roslyn CTP-1 ile yazılmış ve test edilmiştir.



Yorum Gönder