Caner Tosuner

Leave your code better than you found it

C# Kodlarının Derlenip Çalıştırılması - CLR,IL,CLS,CTS Kavramları

C# yazıyoruz çalışıyor, VB yazıyoruz çalışıyor, C++ yazıyoruz çalışıyor, ne hikmettir pek bilmesek bile bir F# dilimiz var onu yazıyoruz .Net sağolsun onu da çalıştırıyor... 

Peki ama bütün bunlar nasıl oluyor ?..

.Net framework içerisinde geliştirme yapabilmek için bir çok dil mevcuttur ancak C# bunlardan en torpillisidir diyebiliriz heralde. Tamamen OOP dayalı bir dil olmasıyla beraber günümüz .Net ailesinde en çok tercih edilen dil haline gelmiştir.

Aşağıdaki resimde de görüldüğü üzre v1.0 dan başlayıp v4.5' e kadar gelen süreçte ne gibi yenilikler geldi, ilk başlarda neler yapabiliyorken son sürümle birlikte ne gibi yeni özellikler geldi bunları görebiliriz. 

Bütün bunlar güzel hoşta arkadaş bunları üstte bahsettiğimiz .Net ailesi içerisinde bulunan çeşitli programlama dilleriyle yapıyoruz ama nasıl oluyor da C# ile yazılan projenin çıktısı ayrı, F# ile yazılanın ki ayrı, VB ile yazılanın ki ayrı iken framework bunları yorumlayıp çalıştırıabiliyor ?.. 

Yazılmış olan kodların direkt olarak derlenip çalıştırılması gibi bir cevap düşünecek olsak o zamanda her bir programlama dili için ayrı ayrı bu işlemleri yapan logic'ler gerekecektir bu da çok fazla iş yükü,maliyet vs demektir ve aslında mümkünde değildir.

 

Aşağıda bulunan görsel aslında yazmış olduğumuz projelerin çalışmasına kadar olan süreci özetliyor gibi.

 

CLR - Common Language Runtime

.Net framework altında bulunan dillerden herhangi biriyle program yazdığımızda kodlar ilk olarak CIL (common intermadiate language) diğer bir deyişle IL dediğimiz byte kodlara dönüşür. Bu IL kodları aslında çalıştırılabilir kodlar değildir ve çalıştırılabilmesi için bir ara programa ihtiyaç vardır. Bu ara program da CLR'dır. CLR dönüştürülmüş olan byte IL kodlarını alıp bir JIT (Just-In-Time) derleyicisini etkinleştirerek makine koduna dönüştürür ve çalıştırır.

 

CLS- Common Language Specification

CLS için .Net framework çatısı altında bulunan programlama dillerinin ortak noktalarını barındırır ve programımızın diğer dillerle olan etkileşimini sağlar. Diller arasında ortak kullanılan yapılar ve onların birtakım kuralları bulunmaktadır. Yazdığınız kodların diğer programlama dilleriyle geliştirilen projelerde de kullanabilir olması istiyorsanız, Common Language Specification kurallarına uyumlu kodlar yazmamız gerekmektedir. Bu kurallara uygun geliştirdiğiniz kodlar, CLS desteği olan diller tarafından yorumlanabilir ve böylece diller arasında ki iletişim sağlanmış olur.

 

CTS- Common Type System

CTS CLR'ın bir alt kümesidir diyebiliriz. Şöyle ki; Common Type System sayesinde programlama dillerinde kullanılan veri türlerinin(int,double,byte,string etc.) arasında da uyum sağlanmış olur.

Örnek olarak; .Net framework altında bulunan bütün dillerde int,double,float vs. gibi tiplerin ram'da kapladıkları yer aynıdır ve bu dillerden biri için derlenen kodlar referans olarak diğerinde de kullanılabilmektedir. Bu nedenle Framework içerisinde bulunan tüm dillerde CTS den dolayı ayni kurallara sahip veri tiplerini kullanılır farklı olan şey syntax'dır.

 

.Net Assembly nedir ?

Assembly mi .Net Assembly mi ?

Genelde assembly nedir diye sorulduğunda belkide %80'imiz eski bir programlama dili olan assembly ile karıştırırız ancak aslında soruyu .Net Assembly nedir diye sorsak daha doğru bir soru olacaktır.

.Net Framework ailesindeki dillerden biriyle bir uygulama geliştirdiğimizde yazmış olduğumuz kodlar programlama dilinin derleyicisi kullanılarak bir .dll ya da .exe ye dönüştüğünde kodlar bir assembly içerisine koyulur. Nedir bunlar diye soracak olursak ;

.Net Assembly 4 bölümden oluşur

  1. IL
  2. Metadata
  3. Manifest
  4. Resources

 

 

1) IL

.Net ailesinde bulunan dillerden herhangi biri ile yazmış olduğumuz kodlar derlendikten sonra IL kodlarına dönüşür ve .Net Assembly'da ki IL katmanı bu kodları barındırır. Ancak bu kodlar makine kodu değildir, CLR tarafından makine koduna dönüştürülür. 

 

2) Metadata

Metadata içerisinde uygulamayı yazarken kullandığımız değişkenler, metodlar, external .dll' ler ve onların hangi metodlarının yada değişkenlerinin kullanıldığı bilgisi gibi tanımlamalar bulunur. Örneğin Main fonksiyonunda  Console.WriteLine("Caner Tosuner");  diye bir tanımlamamız olsun.

using System;

class MainApp
{
    public static void Main()
    {
        Console.WriteLine("Caner Tosuner");
    }
}

Metadata içerisinde Console sınıfının WriteLine metodunun kullanıldığı bilgisi yer alır. Yada bizim uygulamada kendi tanımladığımız class ların (Person class'ı gibi) bilgileri ve onların hangi property veya metodlarının kullanıldığı bilgisi de bulunur. Bu arada Visual Studio'da Metadata bilgisinden faydalanmayı ihmal etmemiş, intellisense de VS'da geliştirme sırasında bize öneriler sunarken aslında metadata bilgisinden faydalanmaktadır. 

 

3) Manifest 

Manifest assembly metadalarını diğer bir değişle kimlik bilgilerini tutar. Nedir bu bilgiler diye soracak olursak; assembly versiyon numarası, ürün adı, assembly adı, şirket bilgileri, yasal uyarılar, başka assembly'lerle referans yoluyla olan ilişkileri vs gibi bilgiler tutular. 

 

4) Resource

Uygulamada kullanılan kaynak dosyaların bulunduğu yer. Nedir bu dosyalar ; resim dosyaları, gif'ler, html-xml yada resource files vs olabilir.

 

C# 6.0 ile Gelen Yenilikler

Microsoft C# 6.0 'ı daha önce release ettiği sürümlere istinaden çok büyük major feature'lar ile kullanıma sunmadı desek çokta yanlış olmaz herhalde. Ancak 6.0 ile major feature'ların dışında geliştirici için development yapmasını çok daha kolaylaştıracak diyebileceğimiz bir takım önemli değişiklikler sundu. Daha öncesinde aynı işlemleri yapabilmek için satırlarca kod yazdığımız işlemler artık birkaç satırda yapılabilecek duruma gelmiş. 

6.o ile gelen feature listesi aşağıda ki gibidir.

 

1) Auto-Property Initializer

Daha önceki C# sürümlerinde field'lar için kullanılabilir olan initializer artık auto-property'ler için de kullanılabilecek. 

Öncesinde

using System;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
            Customer cust = new Customer();
            Console.WriteLine(cust.customerID);
            Console.ReadLine();
        }
    }

    public class Customer
    {
        public Customer()
        {
            customerID = Guid.NewGuid();
        }
        public Guid customerID { get; set; }
    }
}

 C# 6.0 ile

using System;
using static System.Console;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
            Customer cust = new Customer();
            WriteLine(cust.customerID);
            ReadLine();
        }
    }

    public class Customer
    {
        public Guid customerID { get; set; } = Guid.NewGuid();
    }
}

  

2) Using Static

Daha önceleri projede ilgili class'ta herhangi bir yerde static sınıfları kullanmak istediğimizde sınıfın adıyla birlikte o sınıfın fonksiyonlarını çağıra bilmekteydik. 

Öncesinde

class Program
{
    static void Main()
    {
        Console.WriteLine(Math.Sqrt(5*2 + 4*3)); 
        Console.WriteLine(System.DayOfWeek.Friday - System.DayOfWeek.Monday); 
    }
}

Artık C# 6.0 ile birlikte bu statik sınıfları using ile tanımlayıp sonrasında o sınıfa ait metotları kullanabileceğiz. 

C# 6.0 ile

using static System.Console;
using static System.Math;
using static System.DayOfWeek;
class Program
{
    static void Main()
    {
        WriteLine(Sqrt(5*2 + 4*3)); 
        WriteLine(Friday - Monday); 
    }
}

 

3) Expression-bodied methods

Lambda expresiion C# 3.5 ile gelen en büyük değişiklikti ve lambda sayesinde uzuuunnn mu uzun delegate kodları yazmaktansa o satırlara karşılık gelen lambda tanımlamalarını yazabilmekteyiz. Eğer yazılan metod geriye tek satırlık bir işlem yapıp değeri return ediyorsa bu işlem lambda expression ile de yazılabilecektir. 

Öncesinde

namespace CSharpSix
{
    class Program
    {
        private static double MultiplyNumbers(double num1, double num2) 
        { 
           return num1 * num2;
        }

        static void Main(string[] args)
        {
            double num1 = 3;
            double num2 = 6;

            Console.WriteLine(MultiplyNumbers(num1, num2));
            Console.ReadLine();
        }
    }
}

C# 6.0 ile

using static System.Console;

namespace CSharpSix
{
    class Program
    {
        private static double MultiplyNumbers(double num1, double num2) => num1 * num2;

        static void Main(string[] args)
        {
            double num1 = 3;
            double num2 = 6;

            WriteLine(MultiplyNumbers(num1, num2));
            ReadLine();
        }
    }
}

 

4) Null Conditional Operator

Developerlar için kaçınılmazdır ki bir objenin veya tanımlanmış olan bir değerin "null" mı değil mi diye sürekli kontrol etme durumunda kalmışızdır ve bu kontrol için genelde min 2 satır kod yazmak zorundayızdır. if(obj!=null) ... şeklinde. C# 6.0 ile birlikte null check yapma işlemleri biraz daha kolay hale getirilmiş.

Öncesinde

using System;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
            Person person = new Person();
            if (person.Name == String.Empty)
            {
                person = null;
            }

            Console.WriteLine(person != null ? person.Name : "Field is null.");
            Console.ReadLine();
        }
    }

    public class Person
    {
        public string Name { get; set; }
    }
}

C# 6.0 ile

using System;
using static System.Console;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
            Person person = new Person();
            if (person.Name == String.Empty)
            {
                person = null;
            }

            WriteLine(person?.Name ?? "Field is null.");

            ReadLine();
        }
    }

    public class Person
    {
        public string Name { get; set; } = "";
    }
}

 

5) String Interpolation

String bir ifadeyi formatlayıp farklı şekilde bir string birleştirme işlemi vs yaparak göstermek itediğimizde string.format("{0} {1}","Caner", "Tosuner"); vs şeklinde tanımlıyorduk. 6.0 il birlikte bu formatları ve ifadeleri yapmak biraz daha kısa hale getirilmiş diyebiliriz. 

Öncesinde

using System;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
            string firstName = "Caner";
            string lastName = "Tosuner";

            Console.WriteLine("Name : " + firstName + " " + lastName);
            Console.WriteLine("Name : {0} {1}", firstName, lastName);

            Console.ReadLine();
        }
    }
}

C# 6.0 ile

using static System.Console;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
            string firstName = "Caner";
            string lastName = "Tosuner";

            WriteLine($"{firstName} {lastName} is my name!");

            ReadLine();
        }
    }
}

Bu yazım şekliyle birlikte string format yazımının karmaşasından da kurtulmuş oluyoruz.

 

6) Exception Filters

try içerisinde bir exception alınıp carch'e düştüğünde exceptiona neden olan şeyle ilgili bir spesification yapmak istediğimizde genelde catch bloğu içerisinde if(ex.Message.Equals("bişey bişey")) şeklidne yazarız. 6.0 ile catch bloğu yanına bu if condition'ları yazabilir hale geliyoruz ve her condition'nın kendi catch'i oluyor.

Öncesinde 

using System;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                throw new Exception(404);
            }
            catch (Exception ex)
            {
                if (ex.Message.Equals("500"))
                    Console.Write("Bad Request");
                else if (ex.Message.Equals("401"))
                    Console.Write("Unauthorized");
                else if (ex.Message.Equals("402"))
                    Console.Write("Payment Required");
                else if (ex.Message.Equals("403"))
                    Console.Write("Forbidden");
                else if (ex.Message.Equals("404"))
                    Console.Write("Not Found");
            }
           Console.ReadLine();
        }
    }
}

C# 6.0 ile

using System;
using static System.Console;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
               throw new Exception(404);
            }
            catch (Exception ex) if (ex.Message.Equals("400"))
            {
                Write("Bad Request");
            }
            catch (Exception ex) if (ex.Message.Equals("401"))
            {
                Write("Unauthorized");
            }
            catch (Exception ex) if  (ex.Message.Equals("402"))
            {
                Write("Payment Required");
            }
            catch (Exception ex) if (ex.Message.Equals("403"))
            {
                Write("Forbidden");
            }
            catch (Exception ex) if (ex.Message.Equals("404"))
            {
                Write("Not Found");
            }
            ReadLine();
        }
    }
}

 

7) Await in a Catch and Finally Block

C# 6. ile birlikte artık Catch ve Finally blockları arasında await keyword'ünü kullanıp asynchronous çalışan kodlar yazabiliyoruz. Daha önce bunu yapabilmemiz mümkün değildi ve ihtiyaç halinde (logging etc.) çeşitli kontroller yapıp catch bloğundan çıktıktan sonra async yapmak istediğimiz işlemi yapardık.

Öncesinde 

using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
            bool isException=false;
            try
            {
                throw new notimplementedexception();
            }
            catch (Exception exception)
            {
                isException = true;
            }
             if(isException)
                 var result = await LogManager.Log("Hata oluştu !" + exception.Message);
            Console.ReadLine();
        }
    }
}

C# 6.0 ile

using System;
using System.Net.Http;
using System.Threading.Tasks;
using static System.Console;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                throw new notimplementedexception();
            }
            catch (Exception exception)
            {
                var result = await LogManager.Log("Catch => Hata oluştu !" + exception.Message);
            }
            finally
            {
                var result = await LogManager.Log("Finally => Hata oluştu !" + exception.Message);
            }            
           ReadLine();
        }
    }
}

 

8) Index initializers

Önceden bir List'e veya Dictionary'ye belirli bir index'ine item atamak istediğimizde önce initialize eder sonra nokta operatörüyle değerleri atardır. 6.0 ile birlikte initialize sırasında bu bu atam işlemlerini yapabilmekteyiz.

Öncesinde 

using System;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
           var dic = new Dictionary<int, string>
                        {
                            { 1, "one" },
                            { 4, "four" },
                            { 10, "ten" }
                        };
        }
    }
}

C# 6.0 ile

using System;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
           var dic = new Dictionary<int, string>
                          {
                             [1] = "one",
                             [4] = "four",
                             [10] = "ten"
                          };
        }
    }
}

Yukarıda 2 syntax'a baktığımızda ne fark var ki ikisi de aynı diyebiliriz ancak şöyle bir ayrım var. 6.0'dan önceki yazımda .Add() metodu kullanılarak initialize işlemi yapılıyor. Yeni syntax'da ise direkt olarak index assignments kullanılmakta ve bu da performans olarak daha verimli bir hale gelmekte. 

 

9) nameof Operatörü

Tanımlamış olduğumuz değişkenin veya objenin string olarak adına ulaşmak istediğimizde 6.0 ile birlikte gelen nameof operatörünü kullanabiliriz.

Öncesinde 

using System;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
            DoSomething("Selam");
            Console.ReadLine();
        }

        public static void DoSomething(string name)
        {
            if (name == null) throw new Exception("Name is null");
        }
    }
}

C# 6.0 ile

using System;
using static System.Console;

namespace CSharpSix
{
    class Program
    {
        static void Main(string[] args)
        {
            DoSomething("Selam");
            ReadLine();
        }

        public static void DoSomething(string newName)
        {
            if (newName == null) throw new Exception(nameof(newName) + " is null");
        }
    }
}