Caner Tosuner

Leave your code better than you found it

IComparable Interface'ini Kullanarak Sıralama İşlemi

Primitive type'lar için sıralama işlemini bir şekilde list.Sort() vs gibi metotlar kullanarak yapabiliyoruz. Peki kendi yazdığımız objeler için bunu nasıl yaparız ? .Net tarafında iki objeyi belli field&property'lere göre sıralamamızı veya compare etmemizi sağlayan IComparable<T> interface'i bulunmakta. Bu interface ile List<T> tipidne tanımladığımız değişkenimize bizim belirttiğimiz kritere göre sıralama özelliği kazandırmış oluyoruz. Örnek bir case üzerinden gidelim; Rectangle adında bir objemiz olsun içerisinde Rectangle objeleri bulunan bir array'imiz olsun bu arrayde bulunan objeleri alanı en küçük olandan büyük olana doğru sıralayalım. İlk olarak Rectangle objemizi aşağıdaki gibi tanımlayalım.

    public class Rectangle 
    {
        public double Length { get; set; }
        public double Breadth { get; set; }

        public double Area
        {
            get
            {
                return Length * Breadth;
            }
        }
    }

Şimdi ise üsttede belirtiğimiz gibi dikdörtgen objelerini küçük olandan büyük olana doğru array'de sıralayalım. Bu işlem yapabilmenin bir yolu IComparable interface'ini kullanmak. Rectangle objemizi bu interface'den implement etmek. Implementasyon sonucunda Rectangle class'ı CompareTo adında bir metota sahip olur. Bu metot aracılığıyla compare işlemi yaparak array'imizi sıralayabiliriz. Rectangle class'ımızın implementasyon sonrasındaki görünümü aşağıdaki gibi olacaktır.

    public class Rectangle : IComparable<Rectangle>
    {
        public double Length { get; set; }
        public double Breadth { get; set; }

        public double Area
        {
            get
            {
                return Length * Breadth;
            }
        }

        public int CompareTo(Rectangle other)
        {
            if (this.Area == other.Area)
            {
                return this.Area.CompareTo(other.Area);
            }
            return other.Area.CompareTo(this.Area);
        }
        public override string ToString()
        {
            return this.Area.ToString();
        }
    }

 Şimdi ise sırada yazdığımız bu kodu test etme işlemi var. 

        public static void Main()
        {
            var list = new List<Rectangle>
            {
                new Rectangle() {Length = 7, Breadth = 3},
                new Rectangle() {Length = 5, Breadth = 1},
                new Rectangle() {Length = 9, Breadth = 4},
                new Rectangle() {Length = 2, Breadth = 7},
                new Rectangle() {Length = 3, Breadth = 5}
            };

            list.Sort();//Sort metodu ile array'i belirtmiş olduğumuz kritere göre sıralıyoruz

            foreach (var element in list)
            {
                Console.WriteLine(element);
            }
        }

Projeyi çalıştırdığımızda aşağıdaki gibi bir ekran elde etmiş olacağız.

36
21
15
14
5

 

ConcurrentStack ve ConcurrentQueue

Thread-safe collection ilk olarak .Net framework 4.0 ile System.Collections.Concurrent namespace'i altında hayatımıza girdi. System.Collections.Concurrent namespace'i altında thread-safe geliştirmeler yapmada kullanabilmek için ConcurrentStack ve ConcurrentQueue adında 2 tane tip bulunmaktadır.

ConcurrentStack

ConcurrentStack LIFO (last in first out) prensibine göre çalışan bir data stracture dır. Generic ConcurrentStack<T> thread safe olan Stack<T>'nin karşılığı olarak düşünebiliriz ve .Net Framework 4.0 ile hayatımıza girmiştir.

Bu class ile kullanılabilecek metotlar ; 

  1. Push(T element) T tipindeki objeyi stack'e eklemek için kullanılır,
  2. PushRange T tipinde ki array'leri stack'e eklemek için kullanılır,
  3. TryPop(out T) stack'te bulunan ilk item'i döner, başarılı ise true değilse false döner
  4. TryPeek(out T) stack'te bulunan bir sonraki item'ı almak için kullanılır ancak bu item'ı stack'ten silmez, başarılı ise true değilse false döner,
  5. TryPopRange TryPop metodu ile aynı çalışır sadece geriye tek bir obje değilde bir array döner.

 ConcurrentStack<T>'nin instance oluşturma ve Push metodu kullanımı aşağıdaki gibidir.

            var concurrentStack = new ConcurrentStack<int>();

            for (var i = 0; i < 10; i++)
            {
                concurrentStack.Push(i);
            }

Concurrent stack'te bulunan bir obje için get işlemi yapmak istediğimizde ise TryPop(out T) metodunu kullanıyoruz. 

            int outData;
            bool isSuccess = concurrentStack.TryPop(out outData);

Full kullanım örneği olarak aşağıda bulunan  metot 1'den başlayıp 100'e kadar olan sayıları Concurrent stack'e atıp sonrasında stack'ten okuma işlemini yapıp ekrana sayıları display etmektedir..

        public static void Main()
        {
            var concurrentStack = new ConcurrentStack<int>();

            for (var i = 1; i < 100; i++)
            {
                concurrentStack.Push(i);
            }

            while (concurrentStack.Count > 0)
            {
                int stackData;

                bool success = concurrentStack.TryPop(out stackData);

                if (success)
                {
                    Console.WriteLine(stackData);
                }
            }
        }

Projeyi çalıştırdığınızda 99'dan başlayıp 0'a kadar olan sayıları display edecektir.

ConcurrentStack aynı zamanda ToArray() property'sini destekler ve stack'i array'a convert etmemize olanak sağlar.

var integerArray = concurrentStack.ToArray();

Stack'in içerisinde item var mı yok mu diye kontrol etmek istediğimizde ise IsEmpty property'sini kullanarabiliriz.

if(!concurrentQueue.IsEmpty)
{
    ;//todo
}

 

ConcurrentQueue

ConcurrentQueue FIFO (first in first out) prensibine göre çalışan bir data stracture dır. Generic ConcurrentQueue<T> thread safe olan Queue<T>'nin karşılığı olarak düşünebiliriz ve yinde ConcurrentQueue de ConcurrentStack gibi .Net Framework 4.0 ile hayatımıza girmiştir.

 ConcurrentQueue<T>'nin bazı önemli metotlarını aşağıdakiler olarak sıralayabiliriz.

  1. Enqueue(T element)  T tipindeki objeyi queue'ye eklemek için kullanılır,
  2. TryPeek(out T) queue'da yada kuyrukta bulunan bir sonraki item'ı almak için kullanılır ancak bu item'ı queue'dan silmez, başarılı ise true değilse false döner,
  3. TryDequeue(out T) bu metot kuyruktaki ilk objeyi almak için kullanılır. TryPeek(out T) metodunun tersine objeyi aldıktan sonra kuyruktan siler, başarılı ise true değilse false döner,

Aşağıda bulunan code-snippet'ı nasıl integer değerler saklamak için ConcurrentQueue instance oluşturulur göstermektedir.

var concurrentQueue = new ConcurrentQueue<int>();

Kuyruğa integer değer atmak için ise aşağıdaki gibi Enqueue metodu kullanılabilir.

concurrentQueue.Enqueue(100);

Full kulalnıma bakacak olursak aşağıda bulunan  metot 1'den başlayıp 100'e kadar olan sayıları ConcurrentQueue'ye atıp sonrasında ConcurrentQueue'den okuma işlemini yapıp ekrana sayıları display etmektedir..

	public static void Main()
	{
		    var concurrentQueue = new ConcurrentQueue<int>();

            for (var i = 1; i < 100; i++)
            {
                concurrentQueue.Enqueue(i);
            }

            int queueData;

            while (concurrentQueue.TryDequeue(out queueData))
            {
                Console.WriteLine(queueData);
            }
	}

Projeyi çalıştırdığınızda 1'den başlayıp 100'e kadar olan sayıları ekrana display edecektir.

Hem ConcurrentStack hem de ConcurrentQueue class'ları thread safe'dirler ve internal olarak locking ve synchronization konularını yönetebilirler.

ConcurrentQueue aynı zamanda ToArray() property'sini destekler ve queue'yu array'a convert etmemize olanak sağlar.

var integerArray = concurrentQueue.ToArray();

Queue'nun içerisi boş mu dolu mu diye kontrol etmek istediğimizde ise IsEmpty property'sini kullanarabiliriz.

while(!concurrentQueue.IsEmpty)
{
     int result;

     concurrentQueue.TryDequeue(out result);

     Console.WriteLine(result);
}

Thread safe programlama ile uğraşıyorsanız ConcurrentStack ve ConcurrentQueue oldukça faydalı sınıflardır. Özellikle mesajlaşma yapıları & web service vs. gibi geliştirmeler gerektiren yerlerde biz developer'lara oldukça kolaylıklar sunmaktadır, unutmamakta fayda var :)

C# Custom Exception Oluşturma

Exception oluşturmak.. kulağa saçma geliyor dimi :) Heralde yazmış olduğumuz uygulamada bir exception meydana gelmesi en istemediğimiz şey dir. Ama bazen bilinçli olarak uygulamamızda exception fırlatmak isteyebiliriz (tabi handle edilmiş exception).

Exception uygulama çalışırken runtime da meydana gelen hatadır. Exception handling ise runtime da meydana gelen bu exception'ları yazılım tarafında ele alma tekniğidir diyebiliriz. Uygulamanızda bir exception meydana geldiğinde .Net kütüphanesinin direkt olarak fırlattığı stack trace açıklaması veya tuhaf exception mesajını kulanıcıya/client'a göndermek istemeyiz. Bu gibi durumlar için Custom Exception kullanımı devreye giriyor. Custom Exception kullanarak daha yönetilebilir ve daha anlamlı hata mesajları tanımlayabilir client'a dönebiliriz veya Log işlemlerinde handle ederken exception türüne göre daha farklı loglama işlemleri yapmak isteyebiliriz.

.Net tarafında bütün exception'ların base'i System.Exception class'ı dır ve custom tanımlanan exception class'larıda direkt veya dolaylı olarak bu class'tan inherit olmaktadır. Bizde yazacağımız custom exception class'ını System.Exception class'ından inherit edicez.

 

Custom Exception Tanımlama

Login işlemlerinde hata exception fırlatmak için kullanılacak LoginException adında bir custom exception tanımlayalım.

public class LoginException : System.Exception
    {
       //todo
    }

Şimdi uygulama içerisinde kullanımı için gerekli constructor tanımlamalarını yapalım.

   public class LoginException : System.Exception
    {
        public LoginException()
            : base()
        { }

        public LoginException(String message)
            : base(message)

        { }

        public LoginException(String message, Exception innerException)
            : base(message, innerException)
        { }

        protected LoginException(SerializationInfo info, StreamingContext context)
            : base(info, context)
        { }
    }

Yukarıda görüldüğü üzre LoginException'nın constructor'larını tanımaldık ve System.Exception class'ı için yapılacak constructor yönlendirmelerini yaptık, böylece LoginException class'ını kullanıma hazır hale getirdik.

 

Custom Exception Kullanımı

Yazmış olduğumuz LoginException'ı kullanırken aşağıdaki gibi try/catch içerisinde handle edip kullanabilir veya Postsharp Exception Handling, Logging yazısında da olduğu gibi üst veya ayrı katmanlarda exception'ı handle edebiliriz.

 void Login(string userName, string password)
        {
            try
            {
                if (userName != "canert" && password != "qwerty123")
                    throw new LoginException("Invalid login operation");
            }
            catch (LoginException loginException)
            {
                Console.WriteLine(loginException.Message);
            }
        }

İhtiyaç dahilinde çok daha farklı case'lerde kullanılmak üzre Custom Exception'lar tanımlayabilirsiniz. Exception fırlatmak düz mantıkla düşünüldüğünde tuhaf gelebilir ancak çoğu projelerde hayat kurtarır ve yazmış olduğunuz kodları tek bir yerden yönetebilme veya reusability gibi konuları projelerinizde uygulayabilmek için tercih edebilirsiniz ancak bu exception'ları handle etmeyi unutmamalıyız.

C# 7.0 Yenilikleri

Daha C# 6.0 'ın tüm özelliklerini yeni yeni kavramışken Microsoft C# 7.0 ile gelecek olan özelliklerden bazılarını açıkladı bile. Gelecek olan feature'lara baktığımızda çokta fazla major yenilikler yok gibi ancak yine de kayda değer şeyler var. Gelin bu feauture'ları ufaktan bi inceleyelim

Öncelikle C#7.0 ın özellikleri VS2015'de default olarak yok. Normalde vs2015'de yeni bir proje açtığınızda default C# 6.0 ile gelir. 7.0'ın özelliklerini aktifleştirmek için VS de proje oluştururken ufak bir kaç şey yapmak gerekiyor.

İlk olarak C#da bir tane console proje oluşturalım ve sonrasında solution'da bulunan projeye sağ tıklayıp özellikler diyelim. Açılan ekrandan Build tabına geçelim ve "conditional compilation symbols" textbox'ına __DEMO__ yazalım. Projeyi build ettikten sonra artık C# 7.0 özelliklerini otomatik olarak alacaktır.

 

 

C# 7.0 Features

  • Local functions
  • Binary literals
  • Digit separators
  • Pattern matching
  • Ref returns and locals

 

Local functions

Bir çok yazılım dili fonksiyon içinde fonksiyon yazımına izin verirken C# için bu geçerli değildi. 7.0 ile artık fonksiyon içerisine fonksiyon tanımlıyor olacağız.

public int Method_1(int x,int y)
	{
		int Metho_2(int x)
		{
			return x*x;
		}
	 
		return Metho_2(x)*y;
	}

 

Binary literals and Digit separators

Bu özelliğin aslında 6.0 ile geleceği söyleniyordu ancak galiba yetiştiremediler ki 7.0 ile geliyor. Bu özellikle birlikte artık numeric tanımlamaları binary olarak yazabileceğiz. 

	public void BinaryLiterals()
	{
		var numbers = new[] { 0b0, 0b1, 0b10 };
		foreach (var number in numbers)
		{
		  Console.WriteLine(number);
		}
	}

 

Daha büyük sayıları daha kolay okuyabilmek için ise "_" ayıracını kullanarak digitleri gruplayabiliriz ve bu decimal, hexa veya binary'ler içinde geçerlidir. 

int oneBillion = 1_000_000_000;
int num_1 = 0x7FFF_1234;
int num_2 = 0b1001_0110_1010_0101;

 

Pattern matching

Pattern matching özelliği fonkysionel programlama dillerinde çok yaygın bir özellik ve C# 7.0 da "is" operatörü ile bazı özellikleri bize sunuyor ancak final sürümü ile birlikte daha fazla özellik ekleneceği söyleniyor.

1) Type Pattern

Type pattern reference typle'ların runtime type test işlemi yaparken işimize yarayacak olan bir özellik.

public void Foo(object item)
{
    if (item is string s)
    {
        WriteLine(s.Length);
    }
}

2) Constant Pattern

Constant pattern bir ifadenin runtime değerini test etmek için kullanılır.

public void Foo(object item)
{
    switch (item)
    {
        case 10:
            WriteLine("It's ten");
            break;
        default:
            WriteLine("It's something else");
            break;
    }
}

3) Var Pattern

public void Foo(object item)
 {
     if(item is var x)
     {
         WriteLine(item == x); // prints true
     }
 }

4) Wildcard Pattern

public void Foo(object item)
 {
     if(item is *)
     {
         WriteLine("Hi there"); //will be executed
     }
 }

5) Recursive Pattern

public int Sum(LinkedListNode<int> root)
{
    switch (root)
    {
        case null: return 0;
        case LinkedListNode<int> { Value is var head, Next is var tail }:
            return head + Sum(tail);
        case *: return 0;
    }
}

 

Ref returns and locals

C# ın ilk versiyonundan itibaren "ref" keyword'ünü kullanarak metotlara pass by reference ile parametreler geçebiliyoruz. Bu özellikle birlikte metottan pass by reference ile değer dönmesini sağlıyor.

static void Main()
 {
     var arr = new[] { 1, 2, 3, 4 };

     ref int item = ref Get(arr, 1);

     Console.WriteLine(item); //2

     item = 10;

     Console.WriteLine(arr[1]); //10

     Console.ReadLine();
 }

ref int Get(int[] array, int index)
{
   return ref array[index]; 
}

 

Kısaca C# 7.0 ile gelecek olan feature'lara değindik ancak relase olması için henüz çok erken ve ilerleyen sürümlerde üstte belirttiğimiz özelliklerinin bazıları değişebilir de. 7.0 ile ilgili yeni duyumlar geldikçe de yazmaya devam...

 

Aspect Oriented Programming Nedir

Aspect Oriented Programming (AOP) tükçesi "Cephe Yönelimli Programlama" bir programlama paradigmasıdır. İsim olarak bazılarımıza yabancı geliyor olabilir çünkü çok yeni bir kavram değil ve gelişen yazılım teknolojileri ve AOP nin daha kolay ve verimli implement edilmesini sağlayacak "PostSharp" gibi tool'ların çıkmasıyla birlikte epey bir önemli hale gelir oldu AOP.

Biz yazılımcılar daha iyi kodlar yazmak için hep kullandığımız bir cümle var "Separation of Concern". AOP'nin çıkış noktası aslında buna dayanıyor diyebiliriz. AOP birbiriyle kesişen ilgilerin (Cross-Cutting Concerns) ayrılması üzerinedir. Uygulama genelinde kullanılacak olan yapıları (logging,exception hand., cache, etc.) core tarafta yazdığımız koddan ayırarak bir çeşit ayrı küçük programcıklar şeklinde yazıp projede kullanmayı hedefler diyebiliriz.

Örnek olarak 70.000 kişinin çalıştığı çok büyük bir holding için uygulama geliştiriyoruz geriye bütün çalışan listesini dönen bir metod yazıyor olalım ve klasik her uygulamada olması gereken belli başlı şeyler vardır; Cache,ExceptionHandling, Logging gibi bizde metodumuzda bunları yapıyor olalım;

 public IEnumerable<Employee> GetEmployeeList()
        {
			//Request'i yapan kişinin yetkisi varmı yokmu kontrol et
			//metoda girerken request'i log'la
			
            try
            {
                var resultList = DbQuery("Select * from Employee"); // database de ki tabloya sorgu attığımız varsayalım ve 70 bin kayıt gelsin
				
                //geriye dönen sonuçları cache'e at bir sonrakine cache'den ver

                return resultList;
            }
            catch (Exception ex)
            {
                // meydana gelen Exception'ı handle edip log'la ve client'a gidecek olan response'u modify et  
                throw;
            }
			
			//metoddan çıkarken response'u log'la
        }
}

Yukarıda bulunan metodu incelediğimizde ne kadar eksik olduğunu görebiliyoruz. Yorum satırlarında yazan işlemler için geliştirmeler yapmamız gerekmekte ancak bu geliştirmeyi nasıl yapacağız  ? CheckUserAuth(), LogRequest(), LogException(), LogResponse(), ModifyResponse() gibi metodlar yazıp bu metodları ilgili yerlerde her metodda yazmak herhalde ilk akla gelen çözüm ancak AOP bize daha farklı şekilde yapmamız gerektiğini söylüyor. Bunları ayrı modüller olarak tasarlayıp daha kullanılabilir, okunabilir ve SOLID prensiplerine uygun geliştirmeler yapmamız gerektiğini söyler.

Peki birbirleri ile çakışan ilgileri birbirlerinden nasıl ayıracağız ? İşte bu noktada karşımıza interceptor çıkmakta.

Interceptor

Interceptor’ belirli noktalarda metot çağrımları sırasında araya girerek çakışan ilgilerimizi işletmemizi ve yönetmemizi sağlamakta. Buda metotların çalışmasından önce veya sonra bir takım işlemleri gerçekleştirebilmemeizi sağlar ve AOP nin yapısı tamamiyle bunun üzerine kurulu desek yanlış olmaz heralde. Interceptor'u implemente etme olayına girmicem çünkü yukarıda da bahsettiğim gibi .Net tarafında Nuget üzerinden indirip kullanabileceğimiz Postsharp kütüphanesi bu işi diplerine kadar yapmakta ve bizlere sadece attribute tanımlamaları yapmayı bırakmakta. 

Şimdi yukarıda yazmış olduğumuz kodu gelin birde AOP standartlarına uygun şekilde yazalım.

        [UserAuthAspect]
        [LoggingAspect]
        [AppCacheAspect(25000)]
        [ExceptionAspect]
        public IEnumerable<Employee> GetEmployeeList()
        {
            var resultList = DbQuery("Select * from Employee");
            return resultList;
        }

[UserAuthAspect] [LoggingAspect] [AppCacheAspect] [ExceptionAspect] attribute'lerini tanımladık ve AOP nin dediği gibi Cross-Cutting yani kesişen yerleri Aspect'ler kullanarak attribute seviyesinde kullanılabilir hale getirdik.Yazmış olduğumuz 2. metot ile 1. metot arasındaki satır sayısı farkına baktığımızda dağlar kadar fark var ve en önemlisi daha okunabilir bir kod yazmış olduk.  

Aspect-Oriented Programming'in Sağladıkları

  1. İçi içe yazılmış ve sürekli tekrar eden kodlardan kurtulabiliyoruz,
  2. Daha temiz ve anlaşılır kodlar yazabiliyoruz,
  3. Yazmış olduğumuz kodları daha abstract hale getirerek modülerliğini arttırıyoruz,
  4. Bakım ve geliştirme maliyetlerini azaltıyoruz,
  5. Uygulamamızı daha yönetilebilir ve daha esnek hale getirebiliyoruz.

Görüldüğü üzre AOP yaklaşımı geliştirdiğimiz uygulamalar için bizlere bir çok faydalar sunmakta ve Postsharp gibi çeşitli tool'lar ile birlikte projenize AOP'ye uygun hale getirmek dahada kolay hale gelmiş durumda. Bundan sonraki AOP ile ilgili yazılarda Postsharp kullanarak Cache, Logging, ExceptionHandling gibi örnekler ile deva ediyor olacağız.  

Web Config Custom Section

WebConfig Asp.Net tabanlı projelerde istemci tarafında ilk çalıştırılan XML tabanlı bir sayfadır diyebiliriz. İçerisinde bir çok bilgi barındırır ve bir istemci yani kullanıcı websitenize erişmeye çallıştığında ilk webconfig çalışır ve içerisinde set edilen bilgilere göre projenizi çalıştırır & ayağa kaldırır.

Nedir bu bilgile diye soracak olursa; 

  • Debug, Release mode ayarı,
  • .Net Framework bilgisi,
  • Varsa Database conn string bilgisi,
  • Kullanılan .Net Kütüphanelerinin versiyon bilgileri,
  • Uygulama Debug modda nasıl Release modda nasıl çalışacak bunun bilgisi,
  • Site içi ve dışı yönlendirme ayarları vs.

Görüldüğü üzre uygulama ile ilgili bir çok ayar mevcut kısacası uygulamanın configurasyonu ile ilgili sayısızca özellik & ayar set edilebilir.

Bunun gibi ayarların dışında web.config içerisinde uygulama içi kullanılan çeşitli özel bilgiler & ayarlar da saklayabilir. Peki neden bu bilgileri burda saklıyoruz veya turuyoruz ?? Uygulamada Constant diye herhangi bir class oluşturup bunun içerisinde de tutabiliriz. Ama bu sefer ne olur derseniz. Orda tuttuğunuz bilgiyi değiştirmek için tekrardan projeyi açıp class içerisinde set edilen değeri değiştirip uygulamayı tekrardan ilgili yere deploy etmemiz gerekmekte. İşte aslında WeConfig bizi bu durumdan kurtarıyor. Webconfig dosyası deploy edildiği yerde projede ki tanımlı olduğu haliyle XML formatında tutuluyor yani uygulama içinde tanımlı olan class gibi dll'e çevrilip içini tekrardan açamayacağımız bir türde durum değil. Bu nedenle değişme olasılığı yüksek olan bilgileri WebConfig içerisinde tutup ihtiyaç duyulduğunda deploy edildiği dosyada ki folder e giderek çok rahat bir şekilde içini açıp ilgili ayarı değiştirebiliriz. Örnek olarak; uygulama kullanılan iletişim sayfasında ki email adresi veya uygulamaın herhangi bir yerinde kullanılan ve sonradan değiştirmeye açık olan bir text/metin. 

<?xml version="1.0" encoding="utf-8" ?>
<configuration>   
     <system.web>
      <compilation defaultLanguage="c#" debug="true" />
    </system.web>
    
    <appSettings>
      <add key="ContanctEmailAddress" value="info@canertosuner.com" />
    </appSettings>
 </configuration>

Yukarıda da görüldüğü gibi uygulama içerisinde yönetmek istediğimiz değerleri <appSettings> içerisinde unique bir key vererek value alanlarına değerleri set edebiliriz. Bu değeri code tarafından okumak istediğimizde de bilindiği üzre şu şekilde yapıyoruz;

string emailAddress= ConfigurationSettings.AppSettings["ContactEmailAddress"];

Peki ya kendimiz aynı <appSettings> section'ı olduğu gibi bir custom secton yapmak istersek..

Case şu şekilde olsun. Kredi faizi hesaplayan bir uygulamamız var ve bu uygulamadaki faiz oranlarını webconfig den okuyup hesaplıyor. Aslında bu gibi bilgiler direkt olarak DB den de alınabilir ama bizim rojemizde webConfig den alalım. 

1.Adım WebConfigde section tanımı yapma

<configuration>
  <configSections>
    <section name="KrediFaizleri" type="MyProject.KrediFaizleriSection" />
  </configSections>
</configuration>
 

Yukarıda da görültüğü gibi KrediFaizleri adında bir section ımızın olacağı bilgisini verdik.

 

2.Adım Section ve içerisindeki elementleri yazma

</configSections>  tag'inin altında aşağıdaki gibi kodumuzu yazalım

  <KrediFaizleri> 
    <Krediler>
      <add tip="ihtiyac" oran="1.65"/>
      <add tip="tasit" oran="1.96"/>
      <add tip="konut" oran="1.20"/>
    </Krediler>
  </KrediFaizleri>

Burda Krediler diye bir array var ve içerisinde Kredi objesi olduğunu düşünelim. Bu obje içerisinde de tip ve oran adında 2 tane string property var. Aslında yukarıda ki xml kodunun tam karşılığı bu. Yukarıda 3 adet kredi için oran bilgisi girdik şimdi sırada bu değerleri webconfigden okumamızı sağlayacak C# kodlarını yazmaya geldi

 

3.Adım KrediFaizleri elementini tanımlama

Yukarıda Krediler arrayinin içerisinde Kredi objesi olduğunu söylemiştik.Aslında bu bir element. Biz adına KrediFaizleriElement diyelim ve classımız aşağıdaki gibi olacaktır.

 public class KrediFaizleriElement : ConfigurationElement
    {
        [ConfigurationProperty("tip", IsKey = true, IsRequired = true)]
        public string KrediTipi
        {
            get { return (string)this["tip"]; }
            set { this["tip"] = value; }
        }

        [ConfigurationProperty("oran", IsKey = true, IsRequired = true)]
        public string Oran
        {
            get { return (string)this["oran"]; }
            set { this["oran"] = value; }
        }
    }

 

4.Adım KrediFaizleri array'ini tanımlama

Bu adımda webconfig de ki array'i almamızı sağlayan ConfigurationElementCollection den türeyen bir class yazıyoruz. Bu class'ı kullanarak tanımladığımız section içerisinde ki collection'ı içerisindeki Element ler ile birlikte alıyoruz.

[ConfigurationCollection(typeof(KrediFaizleriElement))]
    public class KrediFaizleriCollection : ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new KrediFaizleriElement();
        }

         protected override object GetElementKey(ConfigurationElement element)
        {
            return ((KrediFaizleriElement)element).KrediTipi;
        }
    }

  

5.Adım KrediFaizleriSection class ını oluşturma

Fark ettiyseniz  en içten en dışa doğru ilerliyoruz. İlk olarak Element sonra Collection şimdide sırada Section var. Aşağıda yazılan KrediFaizleriSection webconfigde tanımladığımız KrediFaizleri tag'ini okumamızı sağlayacak olan class. İçerisinde Faizler array i var ve bu array'in içerisindede KrediFaizleriElement objeleri bulunacak.

public class KrediFaizleriSection: ConfigurationSection
    {
        [ConfigurationProperty("KrediFaizleri", IsDefaultCollection = true)]
        public KrediFaizleriCollection Faizler
        {
            get { return (KrediFaizleriCollection )this["KrediFaizleri"]; }
            set { this["KrediFaizleri"] = value; }
        }
    }

   

Son Adım Webconfig de tanımlı değerleri okuma ve filtreleme işlemi.

Bu adıma kadar şu sırayla lerledik;

  1. WebConfig de Section tanımlama,
  2. WebConfig de Section içerisindeki Collection ve Elementleri tanımlama,
  3. C# tarafında Element&Model class'ını oluşturma,
  4. C# tarafında Collection class'ını oluşturma,
  5. C# tarafında Section class'ını oluşturma,

Şimdi ise sırada bu class ları kullanarak WebConfig de tanımlı değerleri okuma işlemi var. Bunun için aşağıdaki class ı kullanabiliriz. 

 public static KrediFaizleriElement KrediOranınıGetir(string krediTipi)
        {
            //KrediFaizleri section'nını içerisindeki elementler ile birlikte aldık
            var element=
                ConfigurationManager.GetSection("KrediFaizleriSection") as KrediFaizleriSection;

             //Paramaetre olarak verilen kredi tipine göre linq sorgusu yaptık
             var returnValue = element.Faizler.Cast<KrediFaizleriElement>().SingleOrDefault(c => c.KrediTipi== krediTipi);
           
           //İlgili kredi WebConfigde varmı kontrol ettik
            if (returnValue == null)
                throw new InvalidOperationException(string.Format("Kredi Tipi {0} bulunamadı !", krediTipi));
            //Bulunan krediyi geri döndürdük
            return returnValue;
        }

İşlemlerimiz bu kadar. KrediOranınıGetir metodunu gerekli yerde çağrısını yaparak kullanabiliriz.

yield nedir nasıl kullanılır

Bilineceği üzere bir class ın foreach iterasyonuna sahip olabilmesi için IEnumerable interfacesini implement etmesi gerekmekte. IEnumerable 'ı implemente eden class bu implementle birlikte override edilmesi gereken GetEnumerator metoduna sahip olur ve bu metodun içerisini doldurduktan sonra artik bu class da foreach ile gezebilecek duruma gelir. GetEnumerator metodunu override ederken, MoveNext(), Reset() metotlarini ve Current isimli propertyleri ile ilgili logic'i handle etmemiz gerekiyordu. İşte yield sayesinde bunları yapmaktan kurtuluyoruz. 

Mantigi oldukca basit ama bazi projelerde uygulamaya entegrasyonu sıkıntı çıkarabiliyor. Iste bu  sıkıntıları gidermek için C# 2.0 ile birlikte gelen yield keywordu sayesinde bu işlemlerin tumu bizim için arka planda yapilmis olacak.

Senaryomuz şu şekilde olsun; geriye db de kayıtlı olan ürünleri dönen bir metodumuz var ve bu metodu yield kullanmadan ve yield kullanarak yazmaya çalışalım.

Yield kullanmadan

     public static IEnumerable<Product> GetAllProducts()
            {
                using (var db = new DbEntity())
                {
                    var productList = from product in db.Product
                                      select product;
                    return productList.ToList();
                }
            }

 

Bu normal şartlarda kullandığımız yöntem. İlgile metodu aşağıdaki gibi çağırıp dönen listeye direk olarak erişebilirsiniz.

var productList = GetAllProducts();

yield kullanarak ise şu şekilde yazabiliriz 

Yield Kullanarak

public static IEnumerable<Product> GetAllProducts()
            {
                using (var db = new DbEntity())
                {
                    var productList = from product in db.Product
                                   select product;
                    foreach (var item in productList)
                    {
                        yield return product;
                    }
                }
            }

yield da ise şu şekilde çalışır;

döngüye her girdiğinde yield return product satırında fonksiyonun çağrıldığı yere ilgili product item'ını döner yani return'ü gördü diye foreach den ve metoddan direkt çıkmaz ve listenin içindeki tüm elemanlar bitinceye kadar bu işlemi yapmaya devam eder.

IL Disassambler(ILDASM) acarak code tarafında MoveNext,Reset ve Current gibi uyeler yazmamamiza ragmen yield keywordu sayesinde arka planda bunlarin yazildigini goruyoruz.

 Bir diğer örnek olarak ise şunu verebiliriz; 

public void Consumer()
{
    foreach(int i in Integers())
    {
        Console.WriteLine(i.ToString());
    }
}

public IEnumerable<int> Integers()
{
    yield return 1;
    yield return 2;
    yield return 4;
    yield return 8;
    yield return 16;
    yield return 45;
}

Foreach de dönerken her bir int i için IEnumerable<int> Integers() metoduna giderek değerleri bize teker teker döndürebiliyor.

C# "params" Kullanımı

C# dilinde yazmış olduğumuz bir metoda params keyword'ü ile parametre tanımlaması yaptığımızda bu o metodun çok sayıda parametresi olduğuna işaret eder ve bu parametreleri teker teker metodu çağırdığımız yerde ayrı ayrı "," ile ayırıp set etmek yerine bir sefer de parametre olarak verebiliriz. Parametre olarak bir array alan metod olmalı ve tanımlarken tip belirtecinden önce "params" keyword'ü eklenmelidir. Göndereceğimiz array tek boyutlu olmalı ve bir metodda tek bir params anahtar sözcüğü kullanmalıyız. Params keyword'ü alan ilgili metoda yanında başka parametrelerde gönderebiliriz. ancak dikkat etmemiz gereken şey params tanımlamasını tüm parametrelerden sonra yazmamız gerektiği.

Örnek olarak bir tane int array alan bir metod olsun ve metod bu aldığı array de bulunan sayıların toplamını geriye return etsin.

Params kullanmadan

int SayilariTopla(int a, int b, int c, int d, int e)
{
    return a+b+c+d+e;
}

static void Main(string[] args)
{
     int result = SayilarTopla(2,1,4,5,7);
}

Yukarıda ki örnekte görüldüğü gibi 5 tane int parametre alan bir metod ve geriye bu sayıların toplamını dönüyor. Peki bu metod 6-7 veya 1-2 parametre alarakta çalışmasını istersek ne yapacağız ? Ayrı ayrı birkaç metod daha yazacak değilizdir. İşte burda params'ın gücü devreye giriyor

 

Params kullanarak 

int SayilariTopla(params int[] sayilar)
{
  int toplam= 0;
  foreach (int i in sayilar) 
  {
     toplam+= i; 
  }
  return toplam;
}

static void Main(string[] args)
{
    int result1= SayilariTopla(5);
    int result2= SayilariTopla(1, 2,-3);
    int result3= SayilariTopla(-4,4,3,7,-7,1,8,0);
}

Params ile birlikte yukarıda ki gibi SayilariTopla metoduna artık istediğimiz kadar parametre geçip kullanabiliriz. 

C# Indexer Nedir

Indexer özel tanımlı bir property'dir ve sadece class içerisinde tanımlanabilir. Tanımlandığı class'a indexlenebilir özelliği kazandırır. Array işlemlerinde kullandığımız [ ] operatörünü tanımlamış olduğumuz bir bir class'ı diziymiş gibi işlemler yapabilmek içinde kullanabiliriz. Örneğin Department diye bir class olsun ve departman isimlerini önce ayrı bir array içerisinde sonrada indexer yardımıyla class içerisinde tutalım.

Önce çalışan isimlerini ayrı bir List array'de kullanmak istediğimizde nasıl yazıyoruz ona bakalım.

	public static void Main()
	{
		var arr = new List<string>();
		arr[0]="Bilgi İşlem";
		arr[1]="Proje Yönetimi";
		arr[2]="Analiz";
		arr[3]="İş Geliştirme";
		arr[4]="Destek Sistemler";	
	}

Yukarıda da olduğu gibi gayet basit bir şekilde bir List tanımlayıp departman isimlerini bu List'in içine attık.

 

Indexer kullanarak bunu nasıl yapardık birde ona bakalım,

 	public static void Main()
	{
		Department dprt = new Department();
		
		dprt[0]="Bilgi İşlem";
		dprt[1]="Proje Yönetimi";
		dprt[2]="Analiz";
		dprt[3]="İş Geliştirme";
		dprt[4]="Destek Sistemler";	
		
		Console.WriteLine(dprt[4]); //Destek Sistemler
	}

	public class Department
	{
		public string Name { get; set; }
		public int ID { get; set; }

		//indexer tanımlaması
		private string []names = new string[5]; 
		public string this [int index] 
		{ 
		   get 
		   { 
			   return names[index]; 
		   } 
		   set 
		   { 
			   names[index] = value; 
		   } 
		} 
	}

Yukarıda da görüldüğü üzre Department class'ımıza indexer kullanarak indexlenebilir özelliği kazandırdık ve [ ] kullanarak tıpkı array kullanıyormuş gibi değer atama ve değer okuma işlemlerini yapabildik

Görüldüğü gibi bir class içerisinde property tanımlar gibi indexer tanımlayabiliyoruz. Düşünüldüğünde çok gerek duyulan bir özellik değil gibi duruyor çünkü genelde başka şekilde ihtiyacımızı görüyoruz ama ama örnek olarak Ado.net ile uğraşan arkadaşlar SqlDataReader class'ını bilirler database'den den belli bir column'da bulunan değeri okumak için aşağıdaki gibi dr["Name"] yazarak o değere ulaşmamızı sağlar bunu yapabilmemizin sebebi SqlDataReader class'ı içerisinde indexer tanımlandığından dolayı [ ] diyerek get işlemi yapabildik

SqlDataReader dr = cmd.ExecuteReader();  
ArrayList names= new ArrayList();  
while (dr.Read())  
{  
     names.Add(dr["Name"]); //SqlDataReader class'ı içerisinde indexer tanımlandığından dolayı [ ] diyerek get işlemi yapabildik 
}  

 

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.