İlkay İlknur
Just a developer...

C# 5.0 Paralel İşlemlerin Yönetimde Async & Await Kullanımı ve Task Combinator Metotları

Perşembe, 29 Aralık 2011 10:29 by ilkayilknur

Merhaba Arkadaşlar,

Önceki yazılarımızda sizlerle C# 5.0 ile gelecek olan Asenkron Programlama yeniliklerini gerek konseptsel olarak gerekse örnekler üzerinde uygulamalı olarak incelemiştik. Bu yazımızda ise C# 5.0 gelen Asenkron Programlama yeniliklerinin bir diğer kullanım alanını inceliyor olacağız.

.NET Framework 4.0’ın duyurulmasıyla beraber Framework içerisinde bildiğimiz gibi Parallel Programming konsepti kapsamında pek çok yenilik getirildi. Bu gelen yeniliklerle beraber artık uygulamalarımız içerisinde paralel olarak çeşitli süreçler çalıştırabilmekteyiz ve yönetimlerini de biraz zorlanarakta olsa sağlayabilmekteyiz. .NET Framework 4.0 içerisindeki Parallel Programlama yapısını inceldiğimizde bildiğimiz üzere herşeyin temelinde Task tipinin yattığını görmekteyiz. Aslında Task tipini incelediğimizde Paralel veya Asenkron bir işlemi temsil etmek için mükemmel bir tip olduğunu söyleyebiliriz. Bunun nedeni ise yapılan işlem ile ilgili tüm bilgileri içerisinde bulundurması. Asenkron işlemin durumunun kontrol edilmesi, içerisinde oluşan bir exception’ın elde edilmesi, asenkron işlemin tamamlanmasından hemen sonra çalıştırılacak olan işlemlerin belirtilmesi gibi tüm işlemleri bu tip üzerinden gerçekleştirebilmekteyiz. (Sanırım C# 5.0 ile gelen yeniliklerin nerede devreye gireceğini yavaş yavaş tahmin etmeye başladınız :) ) Evet bu yazımızın konusu Paralel işlemlerin yönetiminde Async ve Await keywordlerinin kullanımı olacak.

C# 5.0 ile beraber gelen yenilikleri uygulamalı olarak incelediğimiz yazımızdaki kısımları hatırlamamız gerekirse await keywordü sayesinde Task,Task<T> veya void dönüş tipine sahip olan asenkron işlemlerinin yönetimini sağlayabiliyorduk. Bu nedenle paralel işlemlerini yönetimini de aynı şekilde sağlayabileceğimizi düşünebiliriz. Örneğin,  Task.Factory.StartNew  metodunu kullanarak bir işlemi arka planda işletmeye başladığımızda StartNew metodu bize Task tipinde bir nesne örneği döndürmekte. İşte tam da bu noktada Async ve Await keywordleri devreye girmekte ve bu şekilde paralel işlemlerin yönetimini kolay bir şekilde sağlayabilmekteyiz.

Şimdi hemen Visual Studio üzerinden bir WPF uygulaması yaratalım ve ilgili AsyncCTPLibrary dll’ini projemize referans olarak ekleyelim ve uygulamamızı geliştirmeye başlayalım.

Uygulamamızın arayüzü oldukça basit olacak ve sadece 1 adet Textblock ve Button’dan oluşuyor olacak.

<Window x:Class="AsyncAwaitParallel.MainWindow" 
      xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
      xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
     Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Content="Compute" Height="23" HorizontalAlignment="Left" 
            Margin="12,12,0,0" Name="button1" VerticalAlignment="Top" 
            Width="75" Click="button1_Click" />
        <TextBlock HorizontalAlignment="Left" Margin="12,41,0,0” 
             Name="txtResult" VerticalAlignment="Top" />
    </Grid>
</Window>

 

Uygulama içerisinde ise aslında bizim için hiçbir anlamı olmayacak ancak CPU’yu oldukça yoran ve maksimum düzeyde çalıştıran işlemler gerçekleştireceğiz. Bu işlemler de Math tipi içerisinde bulunan Sqrt, Sin ve Tan fonksiyonları olacak. Basit bir for döngüsü içerisinde bu fonksiyonları paralel olarak arka planda çalıştırıp elde ettiğimiz değerleri topluyor olacağız. Daha sonra ise topladığımız değerleri de ekrandaki textbox’a yazdırıyor olacağız.

private void button1_Click(object sender, RoutedEventArgs e)
{
     double SqrtSum = 0, SinSum = 0, TanSum = 0;
     Task.Factory.StartNew(() =>
     {
         for (int i = 0; i < 50000000; i++)
         {                         
              SqrtSum += Math.Sqrt(i);
         }                 
      }).ContinueWith((t) => {
            txtResult.Text += String.Format("SqrtSum = {0}", SqrtSum)});
      
      Task.Factory.StartNew(() =>
      {
         for (int i = 0; i < 50000000; i++)
         {
              SinSum += Math.Sin(i);
         }
      }).ContinueWith((t) => {
             txtResult.Text += String.Format("SinSum = {0}", SinSum)});             
      
      Task.Factory.StartNew(() =>
      {                 
         for (int i = 0; i < 50000000; i++)
         {
              TanSum += Math.Tan(i);
         }
      }).ContinueWith((t) => {
               txtResult.Text += String.Format("SqrtSum = {0}", SqrtSum)});

Evet gördüğünüz gibi herbir CPU yoğun işlemi Task.Factory.StartNew metodunu kullanarak arka plana aldık. Böylece hem arayüzümüzün cevapsız kalmasının önüne geçerek uygulamamızın donmamasını sağladık. Hem de arka planda CPU’nun etkin bir biçimde tüm çekirdeklerini kullanmasını sağladık. Şimdi isterseniz uygulamamızı çalıştıralım. 

Upps !!! İşte karşımızda asenkron işlem çalıştırma bunalımı :) Arka planda bir işlem gerçekleştiriyorsunuz ve bunun sonucu ekrana yazamıyorsunuz çünkü ekranda kullanacağımız kontrol başka bir thread’e ait :). Neyse şimdilik bu kısmı pas geçelim ve MessageBox.Show kullanarak sonuçları ekran görüntüleyelim. Ancak yazımızın ilerleyen bölümlerinde bu noktaya değiniyor olacağız. ;) MessageBox.Show metodunu kullarak elde ettiğimiz görüntüler ise şu şekilde.

Uygulamayı çalıştırdığımızda işlemler çeşitli sıralarda arka planda çalıştırıldı ve Task sona erdiğinde sonucu MessageBox kullanarak görüntüledik. Ancak baktığımızda yine önceki yazılarımızda da değindiğimiz Callback tabanlı bir yaklaşım izledik. ContinueWith metodunu kullanarak içerisinde lambda ifadesi yardımıyla Task’ın sonlamasından sonra çalışacak işlemleri belirttik. Peki ya task bittikten sonra başka bir taskın çalışmasını isteseydik ve o task’ta bittiğinde başka bir taskı başlatsaydı :)

Task.Factory.StartNew(() => { }).ContinueWith((t) =>
{
       Task.Factory.StartNew(() => { }).ContinueWith(k =>
        {                         
        });                 
}); 

Yukarıda gördüğümüz gibi yine iç içe girmiş ifadelerle karşılaşıyor olacağız. Aslında yine başa döndük diyebiliriz. Asenkron programlamada da aynı bunalıma düşmüştük ve C# 5.0 ile bu bunalımdan hızlıca çıkmıştık. Öyleyse hemen C# 5.0 ile uygulamamızı yeniden düzenleyelim. Öncelikle metodumuz içerisinde Task tipi üzerinden paralel işlemlerin yönetimini yapacağımızdan dolayı metodumuzun başına async modifier’ını ekliyoruz.

Sonrasında ise ilk olarak her Task.Factory.StartNew metodunu kullandığımız ifadelerin başına await keyword’ünü yerleştirelim (StartNew metodu Task tipini döndürdüğünden dolayı await ifadesini kullanabilmekteyiz.) ve sonrasında o işlem sonucunda elde ettiğimiz değerleri ekrana yazdıralım.

private async void button1_Click(object sender, RoutedEventArgs e)
{
     double SqrtSum = 0, SinSum = 0, TanSum = 0;
     await Task.Factory.StartNew(() =>
     {
          for (int i = 0; i < 50000000; i++)
          {
               SqrtSum += Math.Sqrt(i);
          }
     });
     txtResult.Text += String.Format("SqrtSum = {0}", SqrtSum);
     
     await Task.Factory.StartNew(() =>
     {
          for (int i = 0; i < 50000000; i++)
          {
               SinSum += Math.Sin(i);
          }
     });
     txtResult.Text += String.Format("SinSum = {0}", SinSum);
     await Task.Factory.StartNew(() =>
     {
           for (int i = 0; i < 50000000; i++)
           {
                TanSum += Math.Tan(i);
           }
      });
    
      txtResult.Text += String.Format("\nTanSum = {0}", TanSum);
} 

Uygulamayı çalıştırdığımızda sonuçların ekranda göründüğünü görüyor olacağız.

 

 

 

 

Ancak uygulamayı çalıştırdığımızda sonuçların ekranlara yazılma sırasının hep sqrt,sin ve tan şeklinde olduğunu farketmişinizdir. Acaba gerçekleştirdiğimiz kullanım kodumuzunun paralel olarak işletilmesinin önüne mi geçiyor ? Şimdi gelin isterseniz await ifadesinin kullanımını bir hatırlayalım.

Compiler, await keyword’ünü gördüğü noktadan itibaren mevcut ifadenin sonrasında işletilecek olan ifadeleri bir devam kodu olarak işaretlemekte. Yani mevcut işlem sonlandırıldıktan sonra diğer ifadeleri işletmekte. Böyle olunca da aslında önce Sqrt işlemi gerçekleşmekte sonrasında da Sin ve Sin işleminden sonra da Tan hesaplaması yapılmakta. Peki kodumuzu nasıl paralel olarak işleteceğiz. Aslında sadece 1-2 ufak değişiklik yapmamız bizim için yeterli.

private async void button1_Click(object sender, RoutedEventArgs e)
{
     double SqrtSum = 0, SinSum = 0, TanSum = 0;
     Task t1 = Task.Factory.StartNew(() =>
     {
         for (int i = 0; i < 50000000; i++)
         {
              SqrtSum += Math.Sqrt(i);
         }
     });
     Task t2 = Task.Factory.StartNew(() =>
     {
         for (int i = 0; i < 50000000; i++)
         {
               SinSum += Math.Sin(i);
         }
     });
     Task t3 = Task.Factory.StartNew(() =>
     {
         for (int i = 0; i < 50000000; i++)
         {
              TanSum += Math.Tan(i);
         }
     });
     await t1;
     await t2;
     await t3;
     txtResult.Text += String.Format("SqrtSum = {0}", SqrtSum);
     txtResult.Text += String.Format("SinSum = {0}", SinSum);
     txtResult.Text += String.Format("\nTanSum = {0}", TanSum);
}

 

Yukarıdaki gibi kodumuzu değiştirirsek aslında istediğimiz paralel işletimi sağlamış olmaktayız. Çünkü öncelikle işlemleri arka planda başlatıyoruz ve sonrasında await ifadelerini kullanıyoruz , böylece de işlemlerin sona ermesinden sonra oluşan değerleri ekrana yazdırıyoruz. Ayrıca ekrana yazdırma işlemini artık başka bir thread içerisinde yapmadığımızdan dolayı doğrudan arayüz threadi üzerinden işlemleri gerçekleştirmemiz sayesinde yukarıda düşmüş olduğumuz asenkron işlem bunalımına burada düşmemiş oluyoruz ;)

Evet arkadaşlar gördüğünüz gibi paralel işlemlerin yönetimini de async ve await keywordleri ile sağlayabilmekteyiz. Eğer taskları aynı anda başlatmak isterseniz yukarıdaki gibi bir kullanım gerçekleştirebilirsiniz. Bunun yanında eğer tasklarınız birbirine bağımlıysa ve biri bittikten sonra diğerinin başlaması gerekiyorsa da bir önceki kullanım tam size göre olacaktır.

Taskları teker teker yönetmenin yanında toplu olarak yönetmemizi sağlayan Task Combinators adını verdiğimiz metotlar hali hazırda .NET Framework içerisinde bulunmakta. Bu metotlar uygulamamız içerisinde yarattığımız tüm taskların işletilmesinin sona ermesini beklemek gibi işlemler gerçekleştirebilmekte. Ancak bu metotlar bool değer döndürdüğü için örnek olarak tüm taskların işletiminin sona erdikten sonra bir takım işlemleri gerçekleştirmemiz oldukça sıkıntılı olmakta. Bu nedenle .NET Framework 4.5 ile Task tipi içerisine dönüş tipi Task olan Combinator metotlar ekleniyor olacak. Şimdi gelin Task Combinator metotlarını kısaca inceleyelim.

Task Combinators

Not : Task Combinators olarak bahsettiğimiz metotlar eğer Visual Studio 2010 kullanıyorsanız projenize referans olarak eklemiş olduğumuz AsyncCTPLibrary  dll’i içerisindeki TaskEx tipi içerisinde bulunmakta. Bunun nedeni makinanızda .NET Framework 4.0 kurulu olduğundan dolayı mevcut metotları bozmamak. Ancak Visual Studio 2011 kullanıyorsanız .NET Framework 4.5 yüklü olacağından dolayı ilgili metotlar Task tipi içerisinde olacaktır ve TaskEx tipi burada bulunmayacaktır. Biz örneklerimizi Visual Studio 2010 üzerinden geliştirdiğimiz için TaskEx tipi üzerinden ilerliyor olacağız.

TaskEx tipi içerisinde bulunan static metotları incelediğimizde parametre olarak IEnumerable<Task> tipinden parametreler alan WhenAll, WhenAny isimli Task tipinden bir nesne örneği döndüren metotları görmekteyiz. Bu metotlar önceden de bildiğimiz gibi yaratmış olduğumuz taskların sonlanmasını beklemekte. Ancak Task tipinden bir nesne örneği döndürdüğünden dolayı async ve await keywordlerini kullanarak tüm taskların bitiminden sonra istediğimiz işlemleri gerçekleştirebilmekteyiz.

private async void button1_Click(object sender, RoutedEventArgs e)
{
     double SqrtSum = 0, SinSum = 0, TanSum = 0;
     Task t1 = Task.Factory.StartNew(() =>
     {
         for (int i = 0; i < 50000000; i++)
         {
              SqrtSum += Math.Sqrt(i);
         }
     });
     Task t2 = Task.Factory.StartNew(() =>
     {
         for (int i = 0; i < 50000000; i++)
         {
               SinSum += Math.Sin(i);
         }
     });
     Task t3 = Task.Factory.StartNew(() =>
     {
         for (int i = 0; i < 50000000; i++)
         {
              TanSum += Math.Tan(i);
         }
     });
     
     await TaskEx.WhenAll(t1, t2, t3);
     txtResult.Text += String.Format("SqrtSum = {0}", SqrtSum);
     txtResult.Text += String.Format("SinSum = {0}", SinSum);
     txtResult.Text += String.Format("\nTanSum = {0}", TanSum);
}

 

Yukarıdaki gerçekleştirime baktığımızda WhenAll metoduna yaratmış olduğumuz taskları parametre olarak geçirerek tüm tasklar içerisindeki hesaplama işlemlerinin bitmesini bekledik ve await keyword’ü sayesinde hesaplama işlemi sona erdikten sonrada ilgili sonuçları ekrana yazdırmış olduk.

TaskEx tipine baktığımızda pek çok farklı Combinator metot bulunmakta. Bu combinator metotların yanında bir de Delay isimli bir metot olduğunu görmekteyiz. Bu metot ta aslında Task tipinden bir nesne örneği döndüren Thread.Sleep metodunun ta kendisi :) Ancak tabi ki Task tipi işin içerisine girdiğinden dolayı await keywordü ile çok kolay bir şekilde bekletme işi sona erdikten sonra istediğimiz işlemleri gerçekleştirebiliriz. Örneğin tüm tasklar sona erdikten her bir sonucu 1’er saniye ara ile ekrandaki kontrolümüze yazabiliriz.

private async void button1_Click(object sender, RoutedEventArgs e)
{
     double SqrtSum = 0, SinSum = 0, TanSum = 0;
     Task t1 = Task.Factory.StartNew(() =>
     {
         for (int i = 0; i < 50000000; i++)
         {
              SqrtSum += Math.Sqrt(i);
         }
     });
     Task t2 = Task.Factory.StartNew(() =>
     {
         for (int i = 0; i < 50000000; i++)
         {
               SinSum += Math.Sin(i);
         }
     });
     Task t3 = Task.Factory.StartNew(() =>
     {
         for (int i = 0; i < 50000000; i++)
         {
              TanSum += Math.Tan(i);
         }
     });
     
     await TaskEx.WhenAll(t1, t2, t3);
     txtResult.Text += String.Format("SqrtSum = {0}", SqrtSum);
     await TaskEx.Delay(1000);
     txtResult.Text += String.Format("SinSum = {0}", SinSum);
     await TaskEx.Delay(1000);
     txtResult.Text += String.Format("\nTanSum = {0}", TanSum);

 

}

Gördüğümüz gibi Delay metodu 1 saniye sürecek olan bir Task yarattı ve biz de await ile bu task’ın sona ermesinden sonra çalışacak ifade olarak ekrana sonucu yazdıracak olan ifadeyi bildirdik.

Evet arkadaşlar gördüğünüz gibi aslında async ve await keywordlerini pek çok farklı noktada kullanabilmekteyiz. Ancak baktığımızda aslında Task tipini temel olarak alan işlemlerde await keywordünü kullanabildiğimizi gördünüz. Bu nedenle de Framework içerisinde bulunan metotlarda da Task tabanlı bir altyapıya geçiş yapılmakta. Bu şekilde programlama dili tarafından sunulan kolaylıklar framework ile de desteklenmekte. Await kullanımı için aslında bir takım belirli prensipler bulunmakta. Await kullanacağımız ifadenin tipinin Task,Task<T> veya void olması gibi. Bu şekilde kullanımlara aslında Awaitable Pattern adını veriyoruz. Yani gerçekleştirdiğiniz altyapı awaitable pattern’a uygun ise kolaylıkla async & await ifadesini kullanabilirsiniz. ;)

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

Bir sonraki yazımızda görüşmek üzere,

Hoşçakalın

Not : Uygulama Visual Studio Async CTP3 ile geliştirilmiştir.

Tags:   , ,
Categories:   .NET Framework 4.0 | .NET Framework 4.5 | Async | C# | C# 5.0
Actions:   E-mail | del.icio.us | Permalink | Yorumlar (0) | Yorumlar RSSRSS Yorum Takibi
Share

.NET Framework 4.0 - Paralel İterasyonların Break veya Stop Metotları ile Sonlandırılması

Cuma, 2 Nisan 2010 12:06 by ilkayilknur

Merhaba Arkadaşlar,

Yaklaşık 1-1,5 haftadır bloguma herhangi birşey yazamadım. Bunun tek nedeni ise aslında tembellik.Smile Başka bir bahanenin arkasına sığınmıyorum.

Bu yazımızda ise yine paralel programlama konusundan devam ediyor olacağız ve Paralel olarak çalışan döngülerin nasıl sonlandırılabileceğini incelemeye çalışacağız. Bildiğimiz gibi paralel olmayan döngülerde Break metodunu kullanarak döngülerden çıkabiliyorduk. Ancak Paralel olarak çalışan döngülerde bu şekilde kullanım sözkonusu değil. Bunun nedeni ise aslında Paralel tarafta kullandığımız for döngüsünün aslında bir metot olması. Bu nedenle paralel döngüleri sonladırmak için ParallelLoopState tipi içerisinde bulunan Break ve Stop metotlarını kullanıyor olacağız. Bu iki metot arasında aslında anlatması ve anlaması biraz zor olan bir fark var. Bu farkı anlamak için isterseniz bir örnek yapalım ve daha detaylı bir şekilde inceleyelim. 

İlk olarak ParallelLoopState tipine bakıyor olacağız. Parallel tipi içerisinde bulunan For ve Foreach metotlarına baktığımızda Action<int,ParallelLoopState> şeklinde iterasyon içerisinde işletilecek komutları alan aşırı yüklenmiş metotlar olduğunu görmekteyiz. Peki bu ParallelLoopState tipi ne işe yarıyor ? Bu tip aslında çalışma zamanı sırasında derleyici tarafından üretiliyor ve Paralel olarak işletilen döngü ile ilgili çeşitli işlemleri yapabilmemizi ve çeşitli durum bilgilerini almamızı sağlıyor. Bir de örneğimizde ConcurrentStack tipini kullanıyor olacağız. Bu tipte .Net Framework 4.0 ile beraber gelen Concurrent Collectionlar içerisinde yer almakta. Aslında bildiğimiz koleksiyonların paralel olarak çalışan versiyonları olarak özetleyebiliriz. Bu tipler ile ilgili detaylı olarak ilerleyen günlerde bir yazı yazıyor olacağım. 

Örnek olarak ise elimizde 1 ile 1000 arasında integer değerleri tutan bir koleksiyon olacak ve paralel olarak bu koleksiyonu dolaşıp 500'den küçük değerleri elimizdeki ConcurrentStack içerisine ekleyeceğiz. Örnek kodumuz ise şu şekilde olacak.

 

static void Main()
{
   IEnumerable<int> sayilar = Enumerable.Range(1, 1000);
   ConcurrentStack<int> stack = new ConcurrentStack<int>();
   Parallel.For(0, sayilar.Count() - 1, (p, loopState) =>
           {
              Console.WriteLine("{0} sayısı kontrol ediliyor",p);
              if (p > 500)
                 loopState.Stop();
              else
                 stack.Push(p);
              if (loopState.IsStopped)
                 Console.WriteLine("{0} sayısını kontrol eden durduruldu", p);
           });
   Console.WriteLine("Stackte {0} eleman var",stack.Count);
}

Stop komutu yaratılmış olan ve yaratılacak tüm iterasyonlara mümkün olan en uygun  zamanda işlemlerini sonlandırma mesajı göndermektedir. Ayrıca ParallelLoopState tipi içerisindeki IsStopped propertysi ile de o anda çalışan iterasyona stop mesajı gönderilip gönderilmediğini de algılayabiliyoruz.

Örnek bir ekran görüntüsü ise şu şekilde

Yukarıda gördüğümüz gibi 998 sayısını kontrol ederken stop çağrısı gönderilmekte. Böylece yaratılan iterasyonlara işlemlerini en kısa sürede ve uygun zamanda bitirmeleri gerektiği çağrısı gönderilmiştir. Ancak yine de gördüğümüz gibi bazı iterasyonlar anında sonlanmamış çalışmalarına devam etmiştir. Böylece stacke 4 eleman eklenmiş durumda. Önemli olan bir nokta da diğer elemanları kontrol etmek için yaratılan paralel iterasyonların hiç işlem yapmadan sonlanmış olmaları.

Şimdi gelelim aynı örneği Break metodunu kullanarak yapmaya.

 

static void Main()
{
   IEnumerable<int> sayilar = Enumerable.Range(1, 1000);
   ConcurrentStack<int> stack = new ConcurrentStack<int>();
   Parallel.For(0, sayilar.Count() - 1, (p, loopState) =>
           {
              Console.WriteLine("{0} sayısı kontrol ediliyor",p);
              if (p > 500)
                 loopState.Break();
              else
                 stack.Push(p);
              if (loopState.IsStopped)
                 Console.WriteLine("{0} sayısını kontrol eden durduruldu", p);
           });
   Console.WriteLine("Stackte {0} eleman var",stack.Count);
}

Break metodu ise mesajın gönderildiği iterasyondan önce yaratılan iterasyonların işlemlerini bitirdikten sonra sonlanmaları mesajını vermektedir.

Burda da gördüğümüz gibi bu sefer stack içerisinde 501 eleman bulunmakta. Yani yaratılan iterasyonlar işlemlerine devam ettiler ve bu süreçte de gerekli kontroller yapılarak stack'e 501 eleman eklenmiş durumda.

Bir yazımızında daha sonuna geldik. Ancak yazıdan da anlayabileceğiniz üzere Break ve Stop metotları arasındaki fark biraz karmaşık. Bu nedenle farklı kaynaklardan da aynı konuda yazıları okumakta fayda var. Örneğin MSDN üzerinde aynı konu ile ilgili yazılmış olan yazıyı buradan inceleyebilirsiniz.

Görüşmek Üzere,

 

Tags:   ,
Categories:   .NET Framework 4.0 | Parallel Programing
Actions:   E-mail | del.icio.us | Permalink | Yorumlar (0) | Yorumlar RSSRSS Yorum Takibi
Share

Dynamic Language Runtime Script Hosting - Görsel Ders

Pazar, 21 Şubat 2010 18:00 by ilkayilknur

Merhaba Arkadaşlar,

Bu görsel dersimizde Dynamic Language Runtime ile beraber gelecek olan Script Hosting yapısını inceliyoruz.

Get Microsoft Silverlight

 

Görsel dersi buradan indirebilirsiniz.

Görüşmek üzere,

Tags:   ,
Categories:   .NET Framework 4.0 | C# 4.0
Actions:   E-mail | del.icio.us | Permalink | Yorumlar (0) | Yorumlar RSSRSS Yorum Takibi
Share

.NET Framework 4.0 - Tuple Sınıfı

Pazartesi, 11 Ocak 2010 20:44 by ilkayilknur

Merhaba,

Blogumun ilk teknik yazısında .NET Framework 4.0 ile beraber gelecek olan Tuple sınıfını inceliyor olacağız. Tuple kavramı aslında F# programlama dili içerisinde kullanılan bir kavram. Bizlere tek bir tip içerisinde birden fazla farklı tipteki verileri saklama olanağı sağlamakta. Baktığımız zaman aslında bizlere bu kavram çok ta yabancı gelmemekte. Biz zaten C# 3.0 ile gelen anonymous type'ları kullanarak tek bir tip içerisinde birden fazla tipteki veriyi saklayabiliyoruz. Ancak anonymous tiplerin kullanımlarında birtakım kısıtlamalar bulunmakta. Bunlardan da yazının sonunda bahsediyor olacağım. Smile   

Öncelikle örneklere geçmeden önce Tuple sınıfının yapısına bir göz atalım.

 

Gördüğümüz System isim uzayı içerisinde 8 adet çeşitli generic tipler alan Tuple sınıfları bulunmakta. Her bir nesne yaratıldığı sırada aldığı tipteki değerleri içerisinde taşımakta. Tuple sınıfına baktığımızda maksimum 8 adet veri taşıyabildiğimizi görüyoruz. Ancak dikkatlice bakarsak T7 tipinden sonraki tip adı T8 değil de TRest olarak isimlendirilmiş. Biz bu parametreye eğer bir Tuple nesnesi verirsek aslında bu tip sayısının limitini kolayca arttırabiliriz. 

Bir Tuple nesnesini yaratmak için ise çeşitli alternatif yollar bulunmakta.

1- new ile yeni bir Tuple nesnesi oluşturma 

Tuple nesnesini oluşturmak için Tuple sınıfını yazdıktan sonra generic parametrelere ihtiyacımız olan tipleri belirttikten sonra yapıcı metot içerisinde ilk değer atamalarını yapabiliriz. 

var data = new Tuple<int, string>(2, "ilkay");
var data1 = new Tuple<double, int, string>(12.3, 34, "Tuple Denemesi");

2- Create static metodunu kullanarak Tuple nesnesi oluşturma

İkinci alternatif yolumuz ise Tuple sınıfı içerisinde bulunan Create adındaki static metodu kullanmak. Aslında kullanımının yukarıdaki kullanımdan hiçbir farkı bulunmamakta. Sadece burada ihtiyacımız olan tipleri Create adındaki generic metotta belirtmekteyiz.

var data = Tuple.Create<int, string>(2, "ilkay");
var data1 = Tuple.Create<double, int, string>(12.3, 34, "Tuple Denemesi");

Tuple nesnemizi başarıyla yarattık. Şimdi gelelim yarattığımız değerlere nasıl ulaşacağımıza. Aslında nesne içerisindeki üyelere baktığımızda zaten üye adları bize oldukça yardımcı olmakta.

Üyelere baktığımızda Item ile başlayan propertyler karşımıza çıkmakta. Bu propertylerin tipleri Tuple nesnesi yaratırken belirttiğimiz tipleri ve değerleri taşımakta.

Yazımın başında Tuple nesnesinin en fazla 8 adet veri taşıyabileceğinden ve 8. parametrenin tipinin TRest olarak adlandırıldığından bahsetmiştim. TRest parametresini kullanarak son tipe eğer yeni bir Tuple nesnesi eklersek bu 8 parametre sınırını rahatça ortadan kaldırabiliriz.

Örneğin,

var data = new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int>>(1, 2, 3, 4, 5, 6, 7, Tuple.Create<int, int, int>(8, 9, 10));

Console.WriteLine(data.Rest.Item3);

Yukarıdaki örnekte 10 elemanlı bir Tuple nesnesi elde etmekteyiz. 7. elemandan sonraki elemanlara ise Rest referansı kullanarak ulaşmaktayız. Örnekte 10. eleman ekrana yazdırılmaktadır.

Gelelim Tuple sınıfını nerelerde kullanabileceğimize. Anonymous tiplerin bir takım kısıtlamaları olduğundan bahsetmiştim. Bunlardan biri anonymous tiplerin metotlara parametre olarak geçirilememesi. Ancak Tuple sınıfı kullanılarak bu kısıtlamanın önüne geçerek parametre olarak Tuple sınıfını geçirebiliriz. Böylece anonymous tiplerde gerçekleştirmek istediğimiz fonksiyonaliteyi gerçekleştirmiş olmaktayız. Bir diğer kısıt ise yine anonymous tiplerin metotlardan geri dönüş tipi olarak ta kullanılamaması. Bu sefer yine dönüş tipi olarak Tuple sınıfını kullandığımızda metottan birden fazla değer döndürebilmekteyiz (out parametreleri kullanmadan). Son olarak bahsedeceğim kullanım alanı ise zorunlu olarak tek bir parametre alan metotlar. Bunlara örnek vermek gerekirse parametre alan Thread metotları sadece tek bir object parametresi almak zorunda. Ancak birden fazla parametre vermek istediğimizde Tuple sınıfını akıllıca ve hızlı bir şekilde kullanabiliriz. 

static void Main(string[] args)        
{            
        var data = new Tuple<int, int>(2, 3);
        ParameterizedThreadStart start = new ParameterizedThreadStart(ThreadTest);
        Thread thread = new Thread(start);
        thread.Start(data);        
}
static void ThreadTest(object obj)
{
        Tuple<int, int> data = obj as Tuple<int, int>;
        Console.WriteLine("Item1 : {0} \nItem2 : {1}", data.Item1, data.Item2);
        Console.ReadLine();
}
  
 

Tuple sınıfını yukarıdaki şekilde kullanarak 2 adet integer tipindeki parametreyi metodumuza kolayca geçirebilmekteyiz. Tuple sınıfının generic parametrelerini dynamic olarak belirleyerek çok daha esnek bir şekilde ve farklı senaryolarla bu sınıfı kullanabiliriz.

.NET Framework 4.0 ile beraber gelecek olan Tuple sınıfı incelememizin sonuna geldik. 

Herkese iyi akşamlar

Categories:   .NET Framework 4.0
Actions:   E-mail | del.icio.us | Permalink | Yorumlar (0) | Yorumlar RSSRSS Yorum Takibi
Share