İlkay İlknur

just a developer...

C# 7.0 - Out Variables

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

C# 7.0 ile beraber gelecek olan ufak ama oldukça kullanışlı özelliklerden biri de out variable'lar. Aslında yazının devamını okuyunca bu özellik daha önce gelmedi mi diye de düşünebilirsiniz. Çünkü bu özelliğin ilk olarak C# 6.0 ile beraber gelmesi planlanıyordu ancak Visual Studip 2015 RC versiyonuna geldiğinde C# ekibi bu feature'ı release'e kadar yetiştiremeyeceğini düşündüğü için C# 6.0'dan çıkardı. Şimdi gelelim out variable'ların kullanımına.

out keyword'ü bizim bir metottan birden fazla değer döndürmemiz gerektiği durumlarda başvurduğumuz bir keyword. Örneğin, .NET Framework içerisindeki int.TryParse metoduna bakarsak bu metodun boolean bir değer döndürdüğünü görüyoruz. Aynı zamanda bir de out parametre alıyor ve buradan da parse ettiği değeri metodu çağıran yere geri veriyor. Bir örnek yaparsak,

int result;
string str = "123123";
bool isSucceeded = int.TryParse(str, out result);

</ out keyword'ünün kullanımının en sıkıntılı yanı out parametresi olarak vereceğimiz değişkeni metodu çağırmadan önce tanımlamış olmamız gerekliliği. Çünkü genelde kod yazarken bu tanımlamaları önceden yapmayı unutuyoruz ve out parametresini gördüğümüz anda bir üst satıra geçip o değişkeni tanımlamak zorunda kalıyoruz. Bu da bizi kod yazarken yavaşlatıyor. Artık C# 7.0 ile beraber bu dikkat dağınıklığı ve kod yazarken yavaşlama durumu sona eriyor ve out parametresi olan bir metodu çağırırken inline olarak out parametreyi tanımlayabiliyoruz.

string str = "123123";
bool isSucceeded = int.TryParse(str, out int result);

Bu şekilde gördüğünüz gibi result değişkenini out parametre geçerken tanımlıyoruz ve metot çağırımından sonra da bu değişkeni kullanabiliyoruz. Hatta isterseniz out parametre tanımlarken var keywordünü de kullanbilirsiniz.

string str = "123123";
bool isSucceeded = int.TryParse(str, out var result);

out Parametreden Kurtulma

out parametre kabul eden metotları kullanırken belki de sadece metodun dönüş değerini kullanacağımız ve out parametreyi aslında sadece mecbur kaldığımız için tanımladığımız durumlar olabilir. Mesela, int.TryParse metoduna verdiğiniz string'in bir int value'nun string karşılığı olup olmadığını merak ediyor olabilirsiniz. Dolayısıyla out parametresi olarak verdiğimiz değere hiçbir yerde ihtiyacınız olmayacaktır. Bu gibi durumlar için de yine C# 7.0 out variables syntaxını kullanarak out variable tanımlamaktan kurtulabiliriz. Bunun için out variable'ın adını yazmak yerine _ koyarsak out parametre tanımlamaktan kurtulmuş oluyoruz. Arka plandaki gerekli işlemleri derleyici kendisi hallediyor.

string str = "123123";
if (int.TryParse(str, out var _))
{

}

C# 7.0 içerisindeki belki de en ufak özelliklerden biri out variable'lar. Ancak çözdüğü probleme baktığımızda bu versiyonda en çok kullanılacak olan özelliklerden biri olmaya aday.

Bir sonraki yazıda görüşmek üzere...



C# 7.0 - Tuples

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

C# 7.0 ile beraber gelecek olan önemli özelliklerden biri de Tuple'lar. Tuple tiplerine aslında çokta yabancı değiliz. Tuple tipiyle ilk olarak .NET Framework 4.0 ile tanışmıştık. Hani şu içerisinde Item1, Item2, Item3 diye propertyler olan tipler 😃 Çoğu zaman aslında kullanmak istediğimiz ama bu property isimlerinden dolayı kullanma konusunda içimizin rahat olmadığı tipler 😃

Tupleların en önemli kullanım alanları aslında bir metottan birden fazla değer döndürmek zorunda kaldığımız durumlar. C# içerisinde aslında bir metottan birden fazla değer döndürmek istediğimizde out parametrelerini kullanabiliyoruz.

private void Foo(int param1, out int intReturnParam, out string stringReturnParam)
{

}

Ancak out parametreler maalesef async metotlarda kullanılamıyor. Dolayısıyla bu durumda Tuple tipini kullanmak durumunda kalabiliyoruz.

private async Task<Tuple<intstring>> FooAsync(int param1)
{

}

private async void Barrier()
{
    var result = await FooAsync(1);
    Debug.WriteLine($"{result.Item1} : {result.Item2}");
}

Bu durumda da metodu çağıran veya kullanan yazılımcılar aslında bu async metottan ne döndüğü konusunda hiçbir şekilde bilgi sahibi olamıyorlar. Item1, Item2 fieldlarını ne işe yaradığını, hangisinde hangi bilgi olduğunu iyi bir şekilde dökümante etmeniz gerekiyor. Ayrıca mevcut Tuple tipi bir class olduğu için heap allocationa neden oluyor.

Tüm bu nedenlerden dolayı aslında gerçek anlamda Tuple desteği C# 7.0 ile beraber geliyor. Peki C# 7.0'da Tuple'lar nasıl olacak. Gelelim bu kısma.

Not: İlk olarak bu özelliği kullanmadan önce projemize System.ValueTuple nuget paketini eklememiz gerekiyor.

Metotlarda birden fazla değer döndürmek istediğimizde döndüreceğimiz alanların tiplerini ve isimlerini parantez içerisinde yazmamız gerekecek.

static (int count, string value) Foo()
{

}

static Task<(int count, string value)> FooAsync()
{

}

Yukarıda gördüğünüz gibi Foo metodunun dönüş değeri bir tuple ve bu tuple'ın içerisinde count ve value diye 2 tane alan bulunuyor. Ayrıca Task döndüren asenkron metotların da dönüş tipleri gördüğünüz gibi tuple olabiliyor. Bu şekilde tuple dönüş tiplerini tanımladıktan sonra peki metot içerisinde bir tuple nasıl tanımlıyoruz kısmına bakalım.

static(int count, string value) Foo()
{
    var retVal = (count: 1, value: "Foo");
    return retVal;
}

Parantez içerisinde sadece alanın adını ve değerini yazdığınızda da compiler arka planda aynı anonymous objectlerde olduğu gibi alanın tipini kendisi buluyor ve ona uygun tuple tipini yaratıyor. Ayrıca yine tipin compiler tarafından bilindiği durumlarda da tuple'lardaki field adlarının pekte önemi olmuyor. Compiler arka planda ilgili çevrimi kendisi yapıyor. Önemli olan tuple tipindeki alanların tiplerinin uyuşması.

static (int count, string value) Foo()
{
    var retVal = (c: 1, v: "Foo");
    return retVal;
}

Tuple Deconstruction

Bir metottan veya herhangi bir yerden bir tuple döndüğünde o tuple içerisindeki değerleri ayrıştırmak ve metodun devamında ayrıştırılmış halini kullanmak önemli. Bu yüzden tuple içerisindeki alanları ayrıştırıp içerisindeki değerleri değişkenlere atamak için de kolay bir syntax geliyor C# 7.0 ile.

static void Main(string[] args)
{
    var (count, value) = Foo();
}

static (int count, string value) Foo()
{
    var retVal = (c: 1, v: "Foo");
    return retVal;
}

Main metodunda göründüğü gibi Foo metodundan dönen tuple tipi içerisindeki alanları count ve value ismindeki local değişkenlere atamasını yapabiliyoruz. Böylece aslında tuple tipleri tamamen görünmez bir şekilde kalabiliyorlar. Yani siz bir metottan tuple döndüğünü biliyorsanız, bu tuple içerisindeki alanları hemen hızlıca lokal değişkenlere alıp kodunuzu temiz tutabilirsiniz.

Bazen de metotlardan dönen tuple içerisindeki sadece belirli alanlar işinize yarayabilir. Bu durumda tuple içerisindeki tüm alanları deconstruct etmek yerine istediğiniz değerleri deconstruct edip değişkenlere atayabilirsiniz. Bunun için yukarıda gösterdiğim syntax'ı kullanarak değişken ismi yerine _ koymanız yeterli.

static void Main(string[] args)
{
    var (count, _) = Foo();
}

static (int count, string value) Foo()
{
    var retVal = (c: 1, v: "Foo");
    return retVal;
}

C# 7.0 ile beraber gelen tuple'ların Framework içerisinde bulanan Tuple'lardan bazı farkları var. Bunlardan ilki C# 7.0 tuple'larının struct olması. Böylece bu tupleların yaratılmaları daha az maliyetli. Ayrıca C# 7.0 tuple'ları mutable. Yani bir tuple yarattıktan sonra fieldın değerini değiştirebilirsiniz. Ancak .NET Framework içerisindeki Tuple tipleri immutable. Yani yarattıktan sonra fieldın değerini sadece okuyabilirsiniz, değiştiremezsiniz.

Bir sonraki yazıda görüşmek üzere...



C# 7.0 - Pattern Matching

Şu ana kadar C# 7.0 ile beraber gelmesi planlanan pek çok özelliği inceledik. Bu blog postu da saymazsak incelemediğimiz sadece 3 özellik kalıyor. BU özellikler pattern matching, out var ve tuples.

Bu blog postta ise pattern matching özelliğine bakacağız. C# 7.0 ile beraber gelecek en büyük özelliklerden biri olan pattern matching ile ilgili çektiğim videoyu aşağıdan izleyebilirsiniz.

Video Linki : https://www.youtube.com/watch?v=HzSh0PFBfoQ

Pattern Matching Kod Örnekleri: https://github.com/ilkayilknur/csharp-7-new-features/tree/master/PatternMatching

Görüşmek Üzere



C# 7.0 Kod Örnekleri

C# 6.0 zamanlarında özellikleri anlatan kod örneklerini Github üzerinden paylaşmıştım. C# 7.0 versiyonu da artık yavaş yavaş belirginleşmeye ve release olmaya doğru giderken benzer tarzda örnekleri C# 7.0 içinde koymaya karar verdim.

İlgili örneklere https://github.com/ilkayilknur/csharp-7-new-features adresinden ulaşabilirsiniz. Hatta ekleyebileceğiniz özel kullanım durumları da varsa pull request gönderebilirsiniz.

Görüşmek Üzere



Visual Studio VNext - Visual Studio 15 Preview Yenilikleri

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

Bu seneki Build konferansının en önemli duyurularından biri de Visual Studio'nun bir sonraki major versiyonu olacak olan Visual Studio 15 Preview'dı. Daha ilk preview olmasına rağmen içerisinde güzel yenilikleri barındıran Visual Studio 15'e bu yazımızda kısaca bir gözatacağız.

Visual Studio 15 Preview adından da anlaşılacağı gibi henüz preview :). Dolayısıyla bilgisayarınıza yüklediğinizde eğer bir sorunla karşılaşırsanız bu sorunla ilgili Microsoft size destek vermeyecektir. Bu nedenle tavsiyem bilgisayarınızda yeni bir sanal makinada veya Azure üzerinde bir sanal makinada bu yenilikleri test etmeniz.

Yeni Installer

Bu yazıyı okuyorsanız tahmin ediyorum ki %90 ihtimalle hayatınızda en az bir kere makinanıza Visual Studio kurulumu yapmışsınızdır. Önce ufak bir setup dosyası download edilir sonra o setup dosyası arka planda 1-2 GB büyüklüğünde olan kurulum dosyalarını download eder ve sonrasında da 45dk-1 saate yakın Visual Studio'nun kurulmasını bekleriz. Kurulum zamanı setup sırasında nelerin kurulmasını istediğinize göre de değişir. Bazen developerlar yeniden kurulumla uğraşmamak için belki ileride lazım olur diye tüm opsiyonları seçerler. Bazen de default seçimle ilerlerler. Visual Studio ekibindeki kullanım datalarına bakarsak default seçenekle ilerleyen developerların %70'i default içerisindeki tüm functionalityleri ve tüm app tiplerini bile kullanmıyor.

Bu nedenlerden dolayı Visual Studio VNext'in en büyük temalarından biri bu kurulum aşamasını kolaylaştırmak ve hızlandırmak olacak. Hem setup dosyasının boyutunu ufaltma, hem kurulumu hızlandırma hem de Visual Studio'yu uygulama tipleri bazında modüler hale getirme şimdilik düşünülen yeniliklerden. Şimdi gelelim ilk preview'daki installer yeniliklerine.

http://aka.ms/vsnext adresine gidip sağdaki seçenekten yeni installerı download edip installer yeniliklerini test edebilirsiniz. Soldaki installer ise bildiğimiz klasik Visual Studio installerı.

Installerı download edip çalıştırdığımızda önceki versiyonların installerlarına nazaran farklı bir ekranla karşılaşıyoruz.

Ekrandan da görüleceği gibi artık Visual Studio çeşitli uygulama tiplerine uygun paketlerle geliyor. Yani eğer sadece .NET development yapacaksanız .NET Development paketini kurup hızlı bir şekilde kurulumu tamamlayabilirsiniz. Eğer sadece editör olarak kullanacağım diyorsanız core editörü seçip 300 MB'lık bir download yapıp Visual Studio'yu kurabiliyorsunuz. Şu anda sadece yukarıda resimde gördüğünüz kadar paket mevcut. Ancak zaman içerisinde Universal App paketi, Cloud App paketi gibi daha pek çok farklı paket installera eklenecektir.

Paket bazılı kurulumla beraber kurulum süreleri de gözle görülür bir şekilde düşüyor. Daha önce Visual Studio kurulumuyla 1-1.5 saatimizi harcarken artık çok daha kısa bir sürede kurulumu tamamlayabiliyoruz. Örneğin sadece core editör paketini yüklemek isterseniz max 10 dk içerisinde kurulum tamamlanıyor.(Paket downloadu yapıldığı için internet hızına göre değişiklik gösterebilir.)

Hızlandırılmış İlk Açılım

1-1.5 saat bekleyip Visual Studio kurulumunu tamamladıktan sonra ilk açtığımızda kullanacağımız dile göre seçim yaparız ve bir süre Visual Studio'nun ilk kullanıma hazırlanmasını bekleriz. Her ne kadar uyarıda bir kaç dakika sürebilir dese de bu süreç oldukça uzun sürebilmekte. Benim de şahsen Visual Studio kurulumunda en sinir olduğum şeylerden biridir bu :)

Visual Studio 15 ile beraber artık bu süre de oldukça kısalıyor ve 1 dakikayı bile bulmadan Visual Studio ilk kez bile olsa hızlıca açılıyor. Şahsen en sevdiğim özelliklerden biride bu Visual Studio 15 içerisindeki :D

Klasör Bazlı Çalışabilme

Yeni Visual Studio'nun en önemli özelliklerinden biri de klasör bazlı çalışabilme. Bundan önce Visual Studio'da bir projeyi açmak istediğimizde o projenin illa ki bir solution dosyası olması gerekiyordu. Ancak yeni Visual Studio ile beraber bu zorunluluk ortadan kalktı. Artık solution dosyası olmadan da doğrudan klasör bazlı olarak projeyi açıp çalışabiliyoruz. Böylece python, javascript gibi solution dosyası gerektirmeyen proje tipleriyle Visual Studio içerisinde çalışmak daha da kolay.

Solution Explorer içerisinde klasör görünümü

Code Style Rules

Roslyn projesiyle beraber custom analyzer yazabilmeye başlamamızla en çok sorulan sorulardan biri de proje içerisindeki code standartlarını korumak için bir analyzer yapabilmenin mümkün olup olmadıydı. Bununla ilgili 1-2 proje olsa da tam anlamıyla çözülemeyen bir sorundu bu. Artık Visual Studio 15'le beraber kodlama standartlarına uygun kurallar tanımlayabiliyoruz ve bu kuralların kontrolü projenin derleme aşamasında yapılıyor. Eğer kurala uygun olmayan kullanımlar varsa bunların fixleri için de Visual Studio size ilgili code fixi otomatik olarak sunuyor.

Code style kuralları şu anda Tools => Options ekranı içerisinde Text Editor => C# altında yer alıyor. Bu ekran içerisinde pek çok kural ayarlamak mümkün. Örneğin, projeler içerisinde this ve var keywordlerinin kullanımlarını belirleyebiliyorsunuz. Developerlar var keywordünü hiç kullanmamalı yada tipin belirgin olduğu durumlarda var keywordu kullanılabilir gibi kuralları ayarlayabiliyorsunuz.

Eğer isimlendirmelerle ilgili kurallar belirlemek isterseniz bu kuralları da yine aynı ekran üzerinden ayarlayabiliyorsunuz. Örneğin projede metot isimlendirmelerinde pascal casing kullanılsın diye ayarlarsanız Visual Studio derleme esnasında sizi isimlendirme kurallarına uymadığınız konusunda uyarıyor ve bunu düzeltmeniz için size code fix sunuyor.

Kuralları belirledikten sonra Visual Studio belirlediğimiz kurallara göre build esnasında ilgili uyarıları yapıyor.

"Show Potential Fixes"'a basarsak

Code style rule ekranı içerisinde yapılabilecek pek çok farklı ayar bulunmakta. Bununla ilgili daha detaylı bir şekilde başka bir blog yazısı yazmayı planlıyorum.

Diğer Özellikler

Build'deki sessionları izlerseniz Visual Studio 15 içerisinde yukarıda bahsettiklerime ek olarak gösterilen pek çok farklı özellik olduğunu göreceksiniz. Intellisense substring matching, intellisense filtering, "how do I" (en heyecanlanmadığım özellik :( ), code fix ile proje veya nuget paket referansı ekleme (en efsane özellik :) ) gibi özelliklerin hepsi private buildler üzerinden gösterildi. Bunun anlamı bu özellikle şu an için kullanıcılar tarafından test edilmek için hazır değiller. Tahmin ediyorum ki ilerleyen preview sürümlerinde bu özellikler de yavaş yavaş Visual Studio 15 içerisine eklenecektir.

Build'de Visual Studio 15 ile ilgili oturumlara aşağıda ulaşabilirsiniz.

Visual Studio 15 Preview yenilikleri şimdilik bu kadar. Yeni preview sürümleri çıktıkça içerisinde bulunan yeni özellikleri yine blogdan paylaşıyor olacağım.

Görüşmek üzere...



Roslyn - CSharpSyntaxWalker İle Syntax Tree Üzerinde Gezinme

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

Bir önceki roslyn makalemizde syntax tree yapısından ve syntax tree içerisinde bulunan yapılardan bahsetmiştik. Ayrıca syntax tree içerisinde istediğimiz yapıdaki elementleri bulmak için LINQ kullanmıştık. Bu yazıda ise syntax tree içerisinde gezinmenin farklı bir yolunu inceleyeceğiz. İlk olarak projemize yine Microsoft.CodeAnalysis nuget paketini ekliyoruz.

Install-Package Microsoft.CodeAnalysis 

Genelde syntax treeler ile çalışırken amacımız belirli bir tipte olan elementleri bulup bunlar üzerinde işlemler yapmak oluyor. Örneğin, tüm class tanımlamalarını bulup kontrol etmek veya değişken tanımlamalarını bulup kontrol etmek gibi... Bu nedenle aslında çoğunlukla ilk yaptığımız şey syntax tree içerisinde bulmayı amaçladığımız tanımlamalara gitmek. SyntaxWalker yapısı da otomatik olarak syntax tree üzerinde kolayca gezinmemizi ve istediğimiz tanımlamalara hızlıca ulaşmamızı sağlayan bir yapı. C# syntax tree'leri için bunun özelleşmiş hali de CSharpSyntaxWalker tipi. CSharpSyntaxWalker tipinin yapısına bakarsak bu tip abstract bir tip. Bu nedenle kendi SyntaxWalker tipimizi yazıp bu tipten türeteceğiz.

public class MethodWalker : CSharpSyntaxWalker
{

}

Diyelim ki çalıştığınız şirkette kullandığınız bir kodlama standardı var ve bu standarda göre yazdığınız metotların isimleri büyük harfle başlamalı. Bu nedenle syntax walker ile syntax tree içerisinde gezip metot tanımlamalarını bulup bu metotların isimlerini kontrol etmeliyiz.

Yukarıda gördüğünüz üzere CSharpSyntaxWalker içerisinde override edilebilecek çok sayıda metot var. Bu metotları override ettiğinizde syntax tree içerisinde metodun isminde yazan elementlere denk gelindiğinde override ettiğiniz metot çağırılıyor. Böylece sizde istediğiniz işlemleri burada yapabiliyorsunuz. En basitinden eğer tüm nodeları veya tokenları gezmek isterseniz Visit ve VisitToken metotlarını override edebilirsiniz. Böylece hızlı bir şekilde tree üzerinde istediğiniz elemanlara erişebilirsiniz.

public class MethodWalker : CSharpSyntaxWalker
{
    public override void VisitToken(SyntaxToken token)
    {
        base.VisitToken(token);
    }

    public override void Visit(SyntaxNode node)
    {
        base.Visit(node);
    }
}

Ancak daha spesifik tanımlamalara erişmekse amacınız daha özelleşmiş metotlar işinizi daha da kolaylaştırabilir. Bizim örneğinizde aslında biz sadece metot tanımlamalarına odaklanmak istiyoruz. Bu nedenle metot tanımlamalarına erişmek bizim için yeterli. Bu nedenle doğrudan VisitMethodDeclaration metodunu override ederek ilerliyoruz.

public class MethodWalker : CSharpSyntaxWalker
{
    public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
        base.VisitMethodDeclaration(node);
    }
}

Şimdi sıra geldi ilgili kontrolü yapma aşamasına. VisitMethodDeclaration metodunu override ettiğimizde bize artık MethodDeclarationSyntax node'u parametre olarak geliyor. Artık yapmamız gereken metot adının ilk harfini kontrol edip bir eğer illegal bir kullanım varsa ismini collection içerisine eklemek.

public class MethodWalker : CSharpSyntaxWalker
{
    public List<string> illegalMethodNames = new List<string>();
    public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
        if (char.IsLower(node.Identifier.ValueText, 0))
        {
            illegalMethodNames.Add(node.Identifier.ValueText);
        }
    }
}

Gördüğünüz gibi bu kadar basit. Şimdi sıra geldi bu yazdığımız syntax walker aracılığıyla kod üzerinde gezinmeye.

class Program
{
    static void Main(string[] args)
    {
        var tree = CSharpSyntaxTree.ParseText(@"class Foo
                {
                    void Bar()
                    {
                    }

                    void bar1()
                    {
                        //Illegal
                    }

                    void Bar2()
                    {
                    }

                    void m()
                    {
                        //Illegal
                    }

                    class InnerFoo
                    {
                        void innerM()
                        {
                            //Illegal
                        }
                    }
                }"
);

        MethodWalker methodWalker = new MethodWalker();
        methodWalker.Visit(tree.GetRoot());

        methodWalker.illegalMethodNames.ForEach(t => Console.WriteLine(t));
    }
}

Gördüğünüz gibi syntax tree yarattıktan sonra syntax walkerın visit metodunu syntax tree'nin rootunu göndererek çağırıyoruz ve ağaç üzerinde gezinmeye başlıyoruz. Uygulamayı çalıştırırsak çıktı olarak küçük harfle başlayan metotların isimlerinin ekrana yazıldığını görebiliriz.

Gördüğünüz üzere syntax tree üzerinde gezinmek roslyn API'ları sayesinde oldukça kolay. Siz de kod içerisinde belirli analizleri yapmak için hızlı bir şekilde ve oldukça az kod yazıp ilgili kontrolleri yapabilirsiniz. Peki analizleri yaptık ve hataları kısımları bulduk. Bunları nasıl temizleyeceğiz ? Örneğin küçük harfle başlayan metotların isimlerini otomatik olarak değiştirsek nasıl olur ? Bu da bir sonraki roslyn makalesinin konusu olacak :)

Bir sonraki makalede görüşmek üzere...



Roslyn Syntax Tree API'larına Giriş

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

Daha önce .NET Compiler Platform (Roslyn) ile ilgili yazdığımız tüm yazılarda ve videolarda Roslyn projesinin derleyicileri bir kara kutu olmaktan çıkardığından ve artık bizim de derleyicilerin kullandığı yapıları kullanıp elimizdeki kodu analiz edebileceğimizden çokca bahsetmiştik.

Bugün bu konuya syntax tree API'ları ile giriş yapacağız. Yazdığımız kodlar derleme işlemi sırasında ilk olarak derleyici tarafından parse edilirler ve bu işlemin sonucunda bir syntax tree oluşturulur. Bu syntax tree içerisinde yazmış olduğumuz kod içerisindeki herşey hiyerarşik bir şekilde tutulur.

Örneğin, aşağıdaki gibi son derece basit bir kodumuz olduğunu düşünelim.

class C
{
    public void M()
    { }
}

Bu kod derleyici tarafından derlendiğinde aşağıdaki gibi bir syntax tree oluşturulur.

Gördüğümüz gibi yazmış olduğumuz kod içerisinde bulunan her bir tanımlama, boşluk, keyword vb... tüm bileşenler syntax tree içerisinde eksiksiz tutuluyor. Syntax tree'ler derleme aşamasının en önemli yapılarından biri. Çünkü kod içerisindeki tüm bileşenler syntax tree içerisinde düzenleniyor ve kategorilere ayrılıyor. Derleme işlemi sırasında parserdan sonraki adımlarda da bu aşamada üretilen syntax tree kullanılıyor.

Derleyici tarafından yaratılan syntax tree içerisinde 3 farklı tipte eleman bulunuyor.

  • Syntax Node
  • Syntax Token
  • Syntax Trivia

Syntax Node'lar kod içerisindeki tanımlamaları,ifadeleri, koşul ifadeleri vb... dil içerisinde temel yapıları temsil ederler. Örneğin class tanımlaması en tepede bir syntax node ile ifade edilir. Roslyn içerisinde her yapıya özel SyntaxNode tipinden türetilmiş özel SyntaxNode sınıfları vardır. Yukarıdaki syntax tree resminde mavi olan nodelar birer syntax node'dur.

Syntax Token'lar yine dil içerisinde bulunan en ufak bileşenleri temsil eden yapılardır. Syntax tokenların tree içerisinde altında hiçbir zaman başka tokenlar veya syntax node'lar bulunmaz. Roslyn içerisinde SyntaxToken'lar için tek bir tip bulunurken bu tip içerisindeki Kind propertysi ile token tipleri birbirinden ayrıştırılabilir. Yukarıdaki syntax tree resmindeki yeşil renkli nodelar syntax tokendır.

Syntax Trivia'lar da kod içerisinde bulunan ancak derleme işlemi için çokta büyük anlam ifade etmeyen bileşenler için kullanılır. Bu bileşenler kod içerisindeki yorumlar, boşluklar gibi yapılardır. Syntax trivia'lar syntax tokenlara bağlıdırlar. Her bir syntax token'ın LeadingTrivia ve TrailingTrivia collectionları vardır. Bu collectionlar ile token'a bağlı olan trivia'lara erişilebilir.

Bu kadar teori şimdilik yeterli :) Şimdi hemen bir console uygulaması açalım ve bu API'ları nasıl kullanacağımıza bakalım.

Projemizi yarattıktan sonra yapmamız gereken ilk şey Microsoft.CodeAnalysis nuget paketini yüklemek.

Install-Package Microsoft.CodeAnalysis 

Bu işlemi de gerçekleştirdikten sonra artık hazırız. Varolan bir kodun syntax tree'sini çıkarabilmek için Microsoft.CodeAnalysis.CSharp namespace'i içerisinde CSharpSyntaxTree tipinin ParseText metodunu kullanacağız. Bu metot string olarak kendisine verilen bir kodun syntax treesini verir.

class Program
{
    static void Main()
    {
        var tree = CSharpSyntaxTree.ParseText(@"class C
        {
            public void M()
            { }
        }"
);
    }
}

ParseText metodunu çağırdıktan sonra artık elimizde bir syntax tree var. Bu syntax tree üzerinde istediğimiz elemanlara ulaşabiliriz ve ağaç üzerinde gezebiliriz. İlk olarak yaratılan veri yapısı bir ağaç olduğu için ağacın root elemanına ulaşalım.

class Program
{
    static void Main(string[] args)
    {
        var tree = CSharpSyntaxTree.ParseText(@"class C
        {
            public void M()
            { }
        }"
);

        var root = tree.GetRoot();
    }
}   

Ağacın rootuna ulaştıktan sonra artık ağaç içerisindeki her bir elamana da rahatlıkla ulaşabiliriz. Örneğin basit bir LINQ sorgusuyla tüm metot tanımlamalarına ulaşmamız mümkün.

class Program
{
    static void Main(string[] args)
    {
        var tree = CSharpSyntaxTree.ParseText(@"class C
        {
            public void M()
            { }
        }"
);

        var root = tree.GetRoot();
        var methods = root.DescendantNodes().OfType<MethodDeclarationSyntax>();
        foreach (var method in methods)
        {
            Console.WriteLine(method.ToString());
        }
    }
}

Bu kod çalıştığında parse ettiğimiz kod içerisinde tek metot olduğu için ve o metot da M metodu olduğu için M metodu doğrudan console'a yazılacak.

Olayı biraz daha karmaşıklaştıralım ve parametre kabul eden metotların listesini çıkaralım.

class Program
{
    static void Main(string[] args)
    {
        var tree = CSharpSyntaxTree.ParseText(@"class C
        {
            public void M()
            { }
            public void M2(int a)
            { }
            public void M3(int a,string b)
            { }
        }"
);

        var root = tree.GetRoot();
        var methods = root.DescendantNodes().OfType<MethodDeclarationSyntax>().Where(t => t.ParameterList.Parameters.Any());
        foreach (var method in methods)
        {
            Console.WriteLine(method.ToString());
        }
    }
}

Parse ettiğimiz koda baktığımızda yazdığımız kodun çıktısının M2 ve M3 metotları olması lazım. Hemen kodu çalıştırıp sonucunu görelim.

Şimdi eminim aklınıza takılan şöyle bir sorun var. Bu sorguları bu şekilde iyi güzel yazıyoruzda metot tanımlamalarının MethodDeclarationSyntax tipinde olduğunu veya parametrelerin bu tip içerisindeki ParameterList.Parameters collectionında tutulduğunu nasıl bileceğiz ? Bu konu tabi sadece bizim değil aynı zaman da Roslyn'i ve Roslyn-Visual Studio entagrasyonunu yazan ekibin de bir sorunuydu zamanında. Bu nedenle geliştirdikleri bir araçla bu sorunu çözdüler. Şimdi bu araç artık Visual Studio içerisinde extension olarak sunuluyor.

Syntax Visualizer dediğimiz pencere yardımıyla istediğimiz kodun syntax treesini herhangi bir kod çalıştırmadan görebiliriz ve her bir elemanın tüm propertylerine de ulaşabiliriz. Bu extensionı yüklemek için .NET Compiler Platform SDK'ini yüklememiz gerekiyor. Bu extension aynı zamanda Roslyn API'larıyla yaptığımız kod analizlerini Visual Studio extensionı olarak yayınlamamız sağlayan templateları da sağlıyor. Extensionı kurmak için buradan VS Gallery'e gidebilir veya Visual Studio içerisiden Tools => Extensions and Updates menüsü üzerinde .NET Compiler Platform SDK'ini yükleyebilirsiniz.

Extension'ı yükledikten sonra View => Other Windows => Syntax Visualizer adımlarıyla Syntax Visualizer'ı açabilirsiniz.

Parse ettiğimiz kodu da Visual Studio içerisine kopyalarsak aşağıda gördüğünüz gibi hızlı bir şekilde elemanlara ulaşabilir ve propertylerini hızlı bir şekilde kontrol edebiliriz.

Bu yazımızda syntax tree API'larını kısa bir giriş yaptık ve LINQ ile syntax tree üzerinde elemanlara nasıl erişilebiliriz konusunu inceledik. Bir sonraki yazıda syntax tree üzerinde dolaşmanın farklı bir yolunu inceleyeceğiz.

Görüşmek üzere...



Roslyn Scripting APIs

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

Bir önceki yazımızda Visual Studio 2015 Update 1 ile gelen Interactive Window'u incelemiştik. Interactive Window ile C# kodlarını Visual Studio içerisinde hızlı bir şekilde çalıştırıp anında sonuçlarını görebiliyoruz. Nasıl browserda console içerisinde script çalıştırıp sonucunu anında görebiliyorsak aynısını artık C# için de yapabiliyoruz. Peki bu nasıl mümkün oluyor ?

Aslında tüm sihir Roslyn'in scripting API'larında. Yani Interactive Window içerisinde bir kod yazdığınızda bu kod scripting API'ları aracılığıyla Roslyn'e gönderiliyor ve arkada derlenip, çalıştırılıp sonucu bize geri veriliyor. Şimdi isterseniz gelin bu scripting API'larına biraz gözatalım.

Scripting API'larına ulaşabilmemiz için projemize ilk olarak Microsoft.CodeAnalysis.Scripting nuget paketini yüklememiz gerekiyor.

PM> Install-Package Microsoft.CodeAnalysis.Scripting 

Nuget paketini yüklediğimiz proje eğer .NET Framework 4.6'yı target etmiyorsa aşağıdaki hatayı alıyoruz. Bu hatayı almamak için projemizi .NET Framework 4.6'ya yükseltmemiz gerekiyor.

Could not install package 'System.Runtime 4.0.20'. You are trying to install this package into a project that targets '.NETFramework,Version=v4.5.2', but the package does not contain any assembly references or content files that are compatible with that framework

Nuget paketini başarılı bir şekilde yükledikten sonra artık scripting API'larını test etmeye hazırız. Genel olarak C# kodlarını çalıştırmak için kullanacağımız tipler Microsoft.CodeAnalysis.CSharp.Scripting namespace'i içerisinde bulunuyor.

İlk olarak en basit olandan başlıyoruz. CSharpScript.EvaluateScript metodu bizim basit C# ifadelerini çalıştırıp sonuçlarını almamızı sağlayan metot.

class Program
{
    static void Main(string[] args)
    {
        RunAsync().Wait();
    }

    static async Task RunAsync()
    {
        var result = await CSharpScript.EvaluateAsync("1+1");
        var result2 = await CSharpScript.EvaluateAsync<int>("1+1");
        Console.WriteLine($"result:{result}, result2:{result2}");
    }
}

Eğer EvaluateAsync'in generic olmayan metodunu kullanırsak bize sonuç object tipinden dönüyor. Ancak generic metodu kullanırsak strongly-typed olarak script'in sonucunu alabiliyoruz.

class Program
{
    static void Main(string[] args)
    {
        RunAsync().Wait();
    }

    static async Task RunAsync()
    {
        var result = await CSharpScript.EvaluateAsync<int>("int x=10;int y=12; int z=x+y; z");
        Console.WriteLine($"result:{result}");
    }
}

Yukarıda yazdığımız koda tekrar bakmamızda fayda var. "int x=10;int y=12; int z=x+y; z" aslında derlenebilir bir C# kodu değil. Ancak scripting API'larında bir değişkenin o anki değerini alabilmek için doğrudan adını yazdığımızda değerini alabiliyoruz. Aynı diğer scripting ortamlarında olduğu gibi.

EvaluateAsync metodu opsiyonel ikinci parametre olarak bizden ScriptOptions tipinde bir object bekliyor. Bu parametre ile istediğimiz namespace'i veya kütüphaneyi referans olarak ekleyebiliyoruz. Böylece EvaluateAsync içerisinde çalıştıracağımız kodlarda eklediğimiz kütüphanelerden ve namespacelerden tipleri kullanabiliyoruz. Eğer bu namespaceleri veya kütüphaneleri eklemezsek runtimeda scriptin derlenmesi esnasında compilation error alırız.

Örneğin C# 6.0 ile beraber static tiplerin isimlerini using ile eklediğimizde kod içerisinde artık doğrudan tipin ismini kullanmadan metodun adıyla çağrım yapabiliyorduk. Math sınıfı içerisindeki Tan metodunu çağırdığımız aşağıdaki kodu düşünelim.

static void Main(string[] args)
{
    RunAsync().Wait();
}

static async Task RunAsync()
{
    try
    {
        var result = await CSharpScript.EvaluateAsync<int>("Tan(20);");
        Console.WriteLine($"result:{result}");
    }
    catch (CompilationErrorException ex)
    {
        Console.WriteLine(ex.ToString());
    }
}

Bu kodu çalıştırdığımızda compilation error alırız. Çünkü using ifadesi ile Math tipini script içerisinde referans almadık.

Eğer aşağıdaki gibi System.Math namespace'ini script içerisine referans olarak eklersek Tan(20) kodu başarılı olarak çalışacak ve sonucunu alabileceğiz.

static void Main(string[] args)
{
    RunAsync().Wait();
}

static async Task RunAsync()
{
    try
    {
        var result = await CSharpScript.EvaluateAsync<double>("Tan(90);"ScriptOptions.Default.WithImports("System.Math"));
        Console.WriteLine($"result:{result}");
    }
    catch (CompilationErrorException ex)
    {
        Console.WriteLine(ex.ToString());
    }
}

Şimdiye kadar yaptığımız örneklerden de gördüğünüz üzere EvaluateAsync metodu context bağımsız olarak çalışıyor. Yani bir kod veriyorsunuz, derleyip, kodu çalıştırıyor ancak sonrasında içerisindeki tüm değişkenler ve değerleri kaybolup gidiyor. Peki ya aynı interactive window senaryosunda olduğu gibi çalıştıracağımız kodu dışarıdan alıyorsak ve bu kodu da çalıştırdığımız contexti korumak istiyorsak ne yapacağız ? İşte tam burada devreye EvaluateAsync metodunun biraz daha gelişmiş versiyonu olan RunAsync metodu devreye giriyor.

RunAsync metodu EvaluateAsync metodunun aksine bize ScriptState tipinde bir object döndürüyor. Bu object de kodu çalıştırdığımız contexti içerisinde barındırıyor.

static void Main(string[] args)
{
    RunAsync().Wait();
}

static async Task RunAsync()
{
    var state = await CSharpScript.RunAsync<int>("1+2");
}

ScriptState içerisindeki property ve metotlara kısaca bakarsak.

  • ContinueWithAsync metotları tahmin edeceğiniz üzere mevcut context üzerinden yeni kodlar çalıştırmamızı sağlar.
  • GetVariable metodu script içerisinde o anda tanımlı olan belirli bir değişkenle ilgili bilgileri alabilmemizi sağlar.
  • ReturnValue ise çalıştırdığımız kodun geri dönüş değerini içerir. EvaluteAsync metodundan doğrudan dönen değer burada ReturnValue propertysinde bulunuyor.
  • Script propertysi son çalıştırdığımız script ile ilgili bilgileri saklar.
  • Variables propertysi ise script içerisinde o anda tanımlı olan tüm değişkenlerler ile ilgili bilgileri içerisinde barındırır.

Şimdi ContinueWithAsync ile ufak bir örnek yapalım.

static void Main(string[] args)
{
    RunAsync().Wait();
}

static async Task RunAsync()
{
    var state = await CSharpScript.RunAsync<int>("1+2");
    Console.WriteLine(state.ReturnValue);
    var state2 = await state.ContinueWithAsync("int i=1;");
    var state3 = await state2.ContinueWithAsync<int>("i+5");
    Console.WriteLine(state3.ReturnValue);
}

Yukarıda görüldüğü gibi öncelikle basit bir toplama işlemi çalıştırdık. Sonrasında aynı context içerisinden devam edip bir değişken tanımladık. Bir sonraki adımda ise bu değişkeni kullanarak bir toplama işlemi daha yaptık. Burada en çok dikkat çekmek istediğim nokta buradaki işlemlerden dönen tiplerin immutable olması. Yani her yeni script çalıştırışımızda bize yeni bir ScriptState nesnesi geliyor. Aslında baktığımızda bu state nesnelerini uygun bir şekilde saklarsak ihtiyacımıza göre ilgili kodları hiç çalıştırmamış gibi bir önceki state üzerinden devam etme imkanına da sahip olabiliriz.

Yukarıdaki kodun çıktısını aşağıda görebilirsiniz.

Yukarıda ScriptState tipi içerisinde script içerisinde tanımladığımız değişkenlerle ilgili bilgileri içerisinde saklar demiştik. Şimdi bununla ilgili de ufak bir örnek yapalım.

static void Main(string[] args)
{
    RunAsync().Wait();
}

static async Task RunAsync()
{
    var state = await CSharpScript.RunAsync("int x=1;");
    PrintVariables(state);
    var state2 = await state.ContinueWithAsync("x=4;");
    PrintVariables(state2);
    var state3 = await state2.ContinueWithAsync("x=10;");
    PrintVariables(state3);
}

private static void PrintVariables(ScriptState state)
{
    foreach (var variable in state.Variables)
    {
        Console.WriteLine($"Variable Name: {variable.Name},Type: {variable.Type.Name}, Value: {variable.Value}");
    }
}

ScriptState nesnesi o anda script içerisinde tanımlı olan değişkenlerle ilgili isim,tip ve o anki değeri gibi bilgileri içerisinde barındırıyor. Dolayısıyla bizde bu nesne üzerinden değişkenlerle ilgili bilgilere ulaşabiliyoruz.

Yukarıdaki kodun çıktısına bakarsak.

Yukarıdaki çıktıyla beraber kodu beraber incelersek her bir state nesnesi içerisinde değişkenin değerinin değiştiğini görüyoruz.

CSharpScript tipi içerisinde son olarak Create statik metodunu kullanarak da script çalıştırabiliyoruz. Ancak bu metot diğerlerinden biraz farklı. Öncelikle bu metodu çağırdığımızda parametre olarak verdiğimiz kod anında çalıştırılmıyor. Çalıştırma operasyonunu scripti yarattıktan sonra bizim tetiklememiz gerekiyor.

static void Main(string[] args)
{
    RunAsync().Wait();
}

static async Task RunAsync()
{
    var script = CSharpScript.Create("1+2");
    var state = await script.RunAsync();
}

Ayrıca burada diğer metotlardan farklı olarak script olarak verdiğimiz kodun derlenmesi RunAsync metodunu ilk çağırışımızda yapılıyor. Eğer istersek biz de Compile metodunu kullanarak derleme işlemini önceden tetikleyebiliyoruz.

static void Main(string[] args)
{
    RunAsync().Wait();
}

static async Task RunAsync()
{
    var script = CSharpScript.Create("1+2");
    script.Compile();
    var state = await script.RunAsync();
}

Script tipi üzerinden script çalıştırdığımızda bu tip içerisinde kodu derlemek için gerekli olan tüm yapılar(syntax tree vs...) saklanıyor. Bu nedenle eğer hep aynı kodu çalıştıracaksak bu yapılar bizim için ekstra yük demek. Bunun için CreateDelegate metodunu kullanarak içerisinde bu ekstra yapıları içermeyen daha basit bir tip elde edebiliriz ve bu tip üzerinde kodu daha hızlı ve efektif şekilde çalıştırabiliriz.

static void Main(string[] args)
{
    RunAsync().Wait();
}

static async Task RunAsync()
{
    var script = CSharpScript.Create("1+2");
    var runner = script.CreateDelegate();
    var result = await runner();
    Console.WriteLine(result);
}

Yukarıdaki kodu bilgisayarınızda çalıştırırsanız delegate yaratma aşamasının biraz vakit aldığını ancak sonrasında delegate'i çalıştırma işleminin çok hızlı olduğunu göreceksiniz.

Roslyn içerisinde scripting API'larının kullanımları bu şekilde. Gördüğünüz gibi API'ların hepsi oldukça kuvvetli ve çok farklı senaryolara da cevap verebilecek şekilde tasarlanmış. Bu API'lar kullanılarak Interactive Window gibi daha pek çok farklı uygulama da yapılabilir. Örneğin web sayfası üzerinden aldığınız kodları serverda çalıştırıp sonuçlarını yine web sayfası üzerinde gösterebilirsiniz. Böylece kullanıcılar için ufak bir playground yapmış olabilirsiniz.

Roslyn'in Scripting API'ları da aynı compilerlar gibi open source. Eğer bakmak isterseniz buradan ulaşabilirsiniz. Aynı şekilde Interactive Window Scripting API'ları kullanılarak nasıl yazılmış merak ediyorsanız Github üzerinden kodlarına ulaşabilirsiniz.

Bir sonraki yazıda görüşmek üzere



C# Interactive Window

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

C# Interactive Window ile ilgili ilk yazımı bundan yaklaşık 3 sene önce yazmışım. Roslyn projesi ilk duyurulduğunda projenin gösterilen en önemli kısımlarından biri Interactive Window'du. Ancak zaman geçtikçe öncelikler değişti ve interactive window'un arka planda kullandığı scripting API'larının çıkışı RTM sonrasına ertelendi ve dolayısıyla interactive window'a da ancak Visual Studio 2015 Update 1 ile kavuşabildik.

Interactive Window Nedir ?

Interactive window, C# dili ile çalışan ve zengin editör desteğine sahip bir read-eval-print-loop(REPL). Read-eval-print-loop terimini biraz daha açarsak, yazmış olduğunuz C# kodunu okuyan ve bunu arka planda çalıştırdıktan sonra da sonucunu ekrana yazan bir uygulama. Yani artık bir kodu denemek için en basitinden bir console uygulaması yaratmaya gerek kalmadan doğrudan kodu REPL uygulaması içerisinden çalıştırıp hızlı bir şekilde sonucunu görebiliriz. Bunun yanında yeni bir dil yeniliği geldiğinde de yine bu yeniliği hızlı bir şekilde REPL uygulaması içerisinde test etmemiz de mümkün.

Visual Studio 2015 Update 1'i bilgisayarımıza kurduktan sonra C# Interactive Window'u 2 şekilde açabiliriz. Bunların ilki ve en kolayı Visual Studio'nun sağ üst köşesinde bulunan Quick Launch.

Diğer seçenek ise View => Other Windows => C# Interactive üst menüsünü kullanarak açmak.

Interactive window ilk açıldığında ise aşağıdaki gibi görünüyor.

Interactive Window'un bir REPL(read-eval-print-loop) olduğundan bahsetmiştik. Dolayısıyla ilk olarak bir Hello World yazıp sonucu görmekte fayda var :)

> Console.WriteLine("Hello Interactive Window"); 
Hello Interactive Window

Gördüğümüz gibi Hello Interactive Window yazısı çalıştırdığımız kodun çıktısı olarak ekrana yazıldı. O zaman işleri biraz daha karıştıralım ve önce bir değişken tanımlayalım ve sonrasında da bu değişken üzerinden bir işlem yapalım.

> List<int> numbers = new List<int> { 4, 1, 6, 3, 9 }; 
> numbers.OrderBy(t => t).ToList().ForEach(t => Console.WriteLine(t));
1
3
4
6
9

Interactive Window içerisinde yazacağımız kodların illa tek satırdan oluşması gibi kısıt yok. Aynı kod yazar gibi Enter tuşuna basıp kod yazmaya devam edebiliriz.

> foreach(var item in numbers.OrderBy(t=>t)) 
.{
. Console.WriteLine(item);
.}
1
3
4
6
9

Interactive window içerisinde tanımladığımız değişkenler bir context içerisinde saklanıyor. Dolayısıyla önceki kod parçalarında tanımladığımız değişkenlere istediğiniz zaman window içerisinden tekrar ulaşıp kullanabiliyoruz.

Interactive Window içerisinde metotlar yazıp sonrasında bu metotları çağırmamız da mümkün.

> void PrintOrderedNumbers(List<int> unorderedList) 
.{
. unorderedList.OrderBy(t => t).ToList().ForEach(t => Console.WriteLine(t));
.}
> PrintOrderedNumbers(numbers);
1
3
4
6
9

Interactive window içerisinde kullanmak istediğimiz bir tipin bulunduğu namespace'i using ifadesi kullanarak ekleyebilmekteyiz.

> using System.Threading; 

Interactive Window içerisindeki context default olarak async tanımlı. Dolayısıyla await kullanarak istediğimiz bir async bir metodu çağırabiliriz.

> using System.Web; 
> using System.Net;
> WebClient client = new WebClient();
> var response = await client.DownloadStringTaskAsync("http://ilkayilknur.com");
> Console.WriteLine(response.Substring(0, 50));
<!DOCTYPE html> <html>
<head prefix="fb: http://

Directives

Interactive Window içerisinde bazı özel komutları kullanarak özel bazı işlemler yapmamız da mümkün. Bunlardan ilki Interactive Window içerisine dll referansı ekleme komutu'i olan #r komutu.

Referansı ekledikten sonra yukarıda görüldüğü gibi dll içerisindeki tipleri intellisense'te görebiliyoruz ve yazacağımız kodlarda bu tipleri kullanabiliyoruz.

Eğer interactive window ekranındaki kodları temizlemek istersek #cls veya #clear komutlarını kullanabiliriz. Bu komutlar sadece ekranda bulunan kodları temizliyor. Yani daha önceden tanımlamış olduğumuz değişken veya metotların hepsini koruyor. Eğer interactive window'u tamamen sıfırlamak yani tüm tanımladığınız değişkenler,metotlar vb.. tümünden kurtulmak istersek kullanacağımız komut #reset komutu olacak.

Interactive window içerisinde dışarıdan bir dosya içerisinde bulunan kodları çalıştırmamız da mümkün. Örneğin aşağıdaki kodu bir txt dosyasına kopyalayıp bilgisayarda bu dosyayı kaydedelim.

void PrintOrderedNumbers(List<int> unorderedList) 
{
unorderedList.OrderBy(t => t).ToList().ForEach(t => Console.WriteLine(t));
}
PrintOrderedNumbers(numbers);

Sonrasında interactive window içerisinde #load komutunu kullanarak bu dosya içerisindeki kodları çalıştırıp sonucunu interactive window içerisinde görebiliriz.

> #load "C:\Users\ilkay\Documents\GitHub\xomnicloud\xomni-sdk-dotnet\src\XOMNI.SDK.Public\bin\Debug\code.txt"
1
3
4
6
9

Son olarak ise interactive window içerisinde komutlarla ilgili yardım almak istersek #help komutunu kullanabiliyoruz.

Görüldüğü gibi interactive window Visual Studio içerisindeki en vazgeçilmez özelliklerden biri olmaya aday. Özellikle ufak bir kod parçasının nasıl çalıştığını görmek için veya hızlıca demolar yapmak için oldukça güzel bir ortam. Hala daha bazı eksikleri olsa da (örneğin nugetten referans ekleme) ileride bu eksik noktalar da giderilecektir diye düşünüyorum.

Bir sonraki yazıda görüşmek üzere.



.NET Compiler Platform API'larını Kullanmak İçin Visual Studio Ortamını Hazırlama

Daha önceki C# 6.0 yazılarımızda Visual Studio 2015 ile beraber sıfırdan yeniden yazılan C# ve VB compilerlarının hayatımıza gireceğinden bahsetmiştik. Hatta yeni yazılan Roslyn compilerı sayesinde C# 6.0 ve VB 12 içerisindeki bazı özelliklerin getirilmesinin de ne kadar kolaylaştığına değinmiştik.

Roslyn compilerlarının yeniden yazılma amaçlarından biri de compilerları bir kara kutu olmaktan çıkartmak.

compiler

Compilerların kara kutu olmasından kastımız input olarak aldığı source code'dan output olarak assembly çıkarması ve bu üretim esnasında yaptığı tüm işlemlerin bir kapalı kutu olarak içeride yapılması. Yani compiler sizin kodunuzu derlerken aslında bu kara kutu içerisinde pek çok işlem yapıyor ve pek çok data ve veri yapısı oluşturuyor ancak siz hiçbir şekilde bu oluşturduğu yapılara ve dataya ulaşamıyorsunuz. Ayrıca kodunuz derlenip assembly oluştuktan sonra da compiler kodunuzu derlerken oluşturduğu her türlü  veri yapısını ve datayı siliyor. Bu nedenle de şu anda Visual Studio içerisinde refactoring  ve kod analizi yapan araçları & extensionları geliştirmek de oldukça zor. Çünkü compilerla iletişime geçebileceğiniz API'lar mevcut değil.

openingcompilerRoslyn compilerları ile beraber artık compilerlar kara kutu olmaktan çıkıyorlar ve içerilerinde oluşturdukları yapıları dışarıya açabiliyorlar. Hal böyle olunca da artık kodu analiz eden ve yaptığı analize göre uygun refactoring seçeneklerini sunan extensionlar yazmak da oldukça kolay.

Şimdilik roslyn compilerları ile ilgili bu kadar giriş sanırım yeterli. (Roslyn ile ilgili daha detaylı bilgi için daha önce yazdığım bir yazıyı da okuyabilirsiniz.) Bu yazımızda amacımız Roslyn compiler API'ları ile uygulamalar geliştirmek için Visual Studio 2015 ortamını nasıl hazırlarız sorusuna cevap vermek.

Öncelikle tabi ki Visual Studio 2015 Preview'e ihtiyacımız var. Visual Studio 2015 Preview'ı ISO olarak bilgisayarınıza kuracağınız gibi Azure üzerinde Visual Studio 2015 imajlı bir VM yaratarak da ilerleyebilirsiniz.

Ortam Kurulumu

Visual Studio 2015 Preview'ı kurduktan sonra kurmamız gerek ilk bileşen Visual Studio 2015 Preview SDK. Visual Studio SDK bizim Visual Studio için extension geliştirmemiz için bize gerekli kütüphaneleri ve araçları sağlayacak olan bileşen. Güncel indirme linkini http://roslyn.codeplex.com/ adresinde bulabilirsiniz.

Sonrasında ise kuruluma .NET Compiler Platform SDK Templates 'i kurarak devam ediyoruz. Bu extension'ı kurduktan sonra artık Visual Studio 2015 Preview'ı açıp File => New Project yaptığımızda artık bize yardımcı olacak olan yeni .NET Compiler Platform proje tipleri gelecek.

Son olarak yapacağımız kurulum ise opsiyonel. Zorunlu değil ancak işimizi son derece kolaylaştıracak bir araç kendileri. Bu neden kurmanızı ben şiddetle tavsiye ediyorum :) Zaten ilerleyen yazılarda da .NET Compiler Platform Syntax Visualizer extensionına sıkça değineceğiz. Ancak şimdilik çok detaya girmeden bu noktada bırakalım.

Tüm bu kurulumları yaptıktan sonra Visual Studio 2015 Preview'ı açarsanız .NET Compiler Platform projelerinin Visual Studio 2015 içerisine eklendiğini görebilirsiniz.

 roslynprojects

Bu son görüntü de demek oluyor ki artık .NET Compiler platform API'ları ile boğuşmaya hazırız.

Bir sonraki yazıda görüşmek üzere.