İlkay İlknur

just a developer...

Task.FromResult Metodu Ne İş Yapar, Nerede Kullanılır ?

Bu makaleye Github üzerinden katkıda bulunabilirsiniz.

Bugün C# ile kod yazarken en sık kullandığımız keywordlerden ikisi şüphesiz ki async & await keywordleri. Her ne kadar bu keywordleri bilinçli olarak kullanıyoruz muyuz konusunda tam emin olamasam da bazen async & await keywordunu kullanmaktan biraz daha ötesine geçmek gerekiyor.

Task objesi bildiğimiz üzere .NET içerisinde bir asenkron operasyonla ilgili tüm bilgileri içerisinde tutan sınıf. Yani bir asenkron operasyonun o anki durumu ne ? veya işlem gerçekleşirken bir exception oldu mu ? gibi sorularımız cevaplarını alacağımız sınıf bu sınıf. Dolayısıyla C# içerisindeki async ve await keywordleri de bu task tipini kullanarak arka planda uygun bir şekilde gereken yapıları oluşturuyorlar.

Task içerisindeki FromResult metodu da en basit anlamda yeni bir Task objesi yaratarak Task'ın durumunu tamamlanmış olarak işaretliyor ve parametre olarak verdiğiniz değeri de Task'ın sonucu olarak içerisine atıyor. Bu durumda aklınıza şu soru geliyor olabilir. Normalde async & await kullandığımızda Task yaratmamız gerekmiyor. Peki bu metot neden var ? Neden ortada bir asenkron operasyon yokken Task yaratmak durumunda kalıyoruz ? Hemen kısaca kullanım alanlarına bakalım...

Interface içerisindeki asenkron metot implementasyonları

Diyelim ki 3rd party bir kütüphane kullanıyorsunuz ve o kütüphaneyi kullanabilmeniz içinde belirli bir interface'i implemente eden bir tip yaratmanız gerekiyor.

interface IThirdLib
{
    Task<int> CountAsync();
}

Bu interface içerisindeki CountAsync metodunu implemente etmek istediğinizde bulunduğunuz platformda bu işlem asenkron yapılmıyor olabilir veya static bir değer döndürmeniz gerekebilir.

class MyClass : IThirdLib
{
    public Task<int> CountAsync()
    {
        return 0;
    }
}

veya

class MyClass : IThirdLib
{
    public Task<int> CountAsync()
    {
        return Directory.EnumerateDirectories(@"C:\").Count();
    }
}

Bu durumda async await keywordu kullanmadığımız için Task tipini arka planda compiler bizim için yaratmıyor ve dolayısıyla int'i ben Task tipine çeviremem diye hata veriyor. Aklınıza hemen Task.Run yaparım onu da await ederim geliyorsa hemen o fikri unutun ve sakın yapmayın. Task.Run'ı şuursuz olarak her Task gereken yerde kullanmamak gerektiğini sakın unutmayın.

Yukarıdaki gibi bir durumla karşılırsanız Task.FromResult metodunu kullanarak tamamlanmış bir Task objesi yaratıp onu metotdan dönebilirsiniz.

class MyClass : IThirdLib
{
    public Task<int> CountAsync()
    {
        return Task.FromResult(5);
    }
}

veya

class MyClass : IThirdLib
{
    public Task<int> CountAsync()
    {
        return Task.FromResult(Directory.EnumerateDirectories(@"C:\").Count());
    }
}

Böylece bizim implementasyonumuz senkron çalışacak ama aynı zamanda bizim yazdığımız tipi kullananlar da async await kullanarak metodumuzu çağırsalar bile herhangi bir sorun yaşamayacaklar.

Bazı durumlarda senkron bazı durumlarda asenkron çalışan metotlar

Yazdığımız metotlar duruma göre senkron veya asenkron çalışabilirler. Örneğin bazı durumlarda senkron olarak validasyon yapılıp bunun sonucuna göre asenkron operasyona başlamadan exception fırlatılabilir. Örneğin MSDN'deki şu örneğe bakarsak...

static Task<long> GetFileLengthsAsync(string filePath)
{
    if (!Directory.Exists(filePath))
    {
        return Task.FromException<long>(
                    new DirectoryNotFoundException("Invalid directory name."));
    }
    else
    {
        string[] files = Directory.GetFiles(filePath);
        if (files.Length == 0)
            return Task.FromResult(0L);
        else
            return Task.Run(() => {
                long total = 0;
                Parallel.ForEach(files, (fileName) => {
                    var fs = new FileStream(fileName, FileMode.Open,
                                            FileAccess.Read, FileShare.ReadWrite,
                                            256, true);
                    long length = fs.Length;
                    Interlocked.Add(ref total, length);
                    fs.Close();
                });
                return total;
            });
    }
}

Bir klasörüdeki tüm dosyaların toplam büyüklüğünü almak istediğimizde örneğin ilk if'e bakarsanız klasörün validasyonu yapılıyor ve eğer klasör yoksa metot doğrudan geri dönüyor. Else içerisindeki 1. if'e bakarsak da klasör içerisinde hiç dosya yoksa doğrudan 0 dönüyor. Sonrasında ise Task.Run ile asenkron operasyon başlatılıyor. Dolayısıyla bu metot aslında en içteki else ifadesine kadar senkron çalışıyor. Sonrasında ise asenkron olarak devam ediyor. İşte bu gibi durumlarda da Task.FromResult metodu kullanılabilir.

Task.FromResult'a benzer diğer metotlar

Task tipi içerisinde FromResult metoduna benzeyen başka metotlar da bulabilmeniz mümkün. Örneğin, FromException veya FromCancelled gibi metotlar. Bu metotlar da isimlerindeki durumlara uyan Task tiplerini yaratıp size verirler. İçerisindeki exception olan task tipi veya iptal edilen bir Task tipi gibi...

Task.CompletedTask

.NET Framework 4.6 ile beraber Task içerisine CompletedTask propertysi eklendi. Bu Task tipi de herhangi bir değer döndürmeyen tamamlanış bir Task objesi içerisinde barındırıyor. Yani yukarıdaki gibi durumlardan birinde metodun dönüş tipi Task<int> değil de Task olsaydı Task.CompletedTask kullanabiliriz.

Taskları Cacheleme

Task.FromResult metodu her çağırdığımızda yeni bir Task objesi bize geri veriyor. Dolayısıyla her Task.FromResult(0) dediğimizde hep sonuç olarak 0 değerini barındıran bir task bize geri dönüyor.

Bu gibi durumlarda sık sık bu metodun çağırılması aslında gereksiz bir şekilde object yaratılmasına ve memoryde gereksiz bir şekilde fazladan bu objelerin yer almasına neden oluyor. Bu gibi durumlarda sıkça başvurulan yöntemlerden biri bu Taskların cachelenmesi. Bunun için örnek olarak Roslyn içerisindeki Task cacheleme mekanizmasına bakmanızı tavsiye ederim.

Roslyn içerisindeki SpecializedTasks sınıfı source code'u : https://github.com/dotnet/roslyn/blob/master/src/Workspaces/Core/Portable/Utilities/SpecializedTasks.cs



C# 5.0 Asenkron Programlama Özelliklerini .NET Framework 4.0, Windows Phone 7.5 & 8.0 ve Silverlight 4.0 & 5.0 Platformlarında Kullanın

Blogda bulunan C# 5.0 Asenkron programa ilgili makaleleri okuyanlardan zaman zaman "Acaba bu özellikleri .NET 4.0 veya Windows Phone 7.5 gibi platformlarda kullanabiliyor muyuz ? " gibi mailler alıyorum. Tabi .NET 4.0 kullananlar için projenizi .NET 4.5'e yükseltip kullanın demek verilebilecek en kolay cevap :) Ama tabi projeyi yükseltin demek her zaman söylendiği kadar kolay olamayabiliyor :) Çok kapsamlı çalışmaların, testlerin yapılması gerekebiliyor. Bu nedenle tabi ki insanlar projeyi yükseltmekten ziyade daha kolay yoldan bu özellikleri kullanabilmeyi tercih edebiliyorlar. Windows Phone veya Silverlight tarafına geçtiğimizde ise tabi ki C# 5.0 compilerının yanında compilerın arka planda kullandığı yapılarında bu platformlara taşınmış olması gerekiyor. Neyse çok fazla uzatmayalım ve şöyle Visual Studio 2012 ile .NET Framework 4.0 'da bir Console uygulaması yaratalım ve duruma bir bakalım :)

Şimdi basit bir async metot yazmaya çalışalım ve bakalım nasıl bir durumla karşı karşıya kalacağız.

Çok basit bir şekilde C# 5.0 ile yapabildiğimiz, bir işlemi arka planda çalıştırma olayını .NET Framework 4.0 ile gerçekleştirdik. Şimdi projemizi derleyelim ve bakalım bir sorun ile karşılaşacak mıyız ?

Mesaj aslında oldukça net :) Async ve await keywordlerini C# 5.0 compilerını kullandığımız için kullanabilmekteyiz. Ancak compilerın arka planda kullanacağı tipler haliyle .NET Framework 4.0 içerisinde bulunmuyor ve compiler da haliyle kodu arka planda yeniden yazmak için bu tiplere ihtiyaç duyuyor . Peki compilerın ihtiyacı olan bu tipleri acaba dışarıdan bir library aracılığıyla versek :) kısayoldan sonuca ulaşabilir miyiz ?

Microsoft.Bcl.Async

Microsoft.Bcl.Async nuget paketi Microsoft tarafından geliştirilen ve .NET Framework 4.0, Windows Phone 7.5 / 8.0 ve Silverlight 4.0/5.0 platformları için compilerın arka planda ihtiyaç duyduğu tipleri içerisinde barındıran bir paket. Bunun yanında ilgili platformlarda bulunan bazı tiplere task-based asenkron metotlar da extension metot olarak bu paket sayesinde gelmekte. O zaman bu package'ı projemize ekleyelim ve bakalım nasıl bir değişiklik olacak.

Microsoft.Bcl.Async paketini proye ekledikten sonra projemiz sorunsuz bir şekilde derlendi. Bunun yanında eğer WebClient tipi gibi içerisinde asenkron metotlar bulunan tiplere bakarsak bu tipler içerisinde de task-based asenkron metotların extension metotlar olarak eklendiğini görüyor olacağız.

Gördüğünüz gibi artık C'# 5.0 özelliklerini .NET 4.0, Silverlight ve Windows Phone'da kullanmak oldukça kolay :)  Yapmanız gereken tek şey proje solutionınızı Visual Studio 2012'ye yükseltemek. Evet yanlış okumadınız :) Şu ana kadar yaptıklarımız sadece Visual Studio 2012 içerisinde geçerli. En azından sizi framework upgrade'inden kurtardım ;) Umarım sizler için faydalı bir yazı olmuştur.


Sakarya Üniversitesi Mobil Uygulama Geliştirme Seminerinin Ardından

29 Nisan'da Daron Yöndem, Engin Polat ve Nokia Developer Champion Sema Kudu ile beraber Sakarya Üniversitesi'ndeki Mobil Uygulama Geliştirme seminerindeydik. Windows Phone ile uygulama geliştirme ve Azure Mobile Services gibi konuların ele alındığı seminerde ben de "Mobil Dünya'da Asenkron Programlama" isimli bir oturum gerçekleştirdim. Mobil taraftaki asenkron programlama olanaklarından ve asenkron programlama yöntemlerinden bahsettim.

Üniversitede seminere gitmeyeli uzun bir süre olmuştu açıkcası :) Sakarya Üniversitesi'ndeki arkadaşların misafirperverliğiyle çok güzel bir gün geçirdik. Özellikle etkinliğin düzenlenmesinde büyük emeği bulunan Mert Yazıcı'ya çok teşekkür ederim. Çok güzel ağırlandık. :)

Etkinlikte yaptığım sunuma buradan ulaşabilirsiniz.

Başka etkinliklerde görüşmek üzere.



DEU BT 2. Teknoloji Zirvesi'nin Ardından

Geçtiğimiz hafta gerek twitter gerekse facebook üzerinden yaptığım paylaşımlardan hatırlayacağınız üzere geçen haftasonu İzmir'de DEU BT 2. Teknoloji Zirvesindeydim. İstanbul'dan benimle beraber Umut Erkal da İzmir'e gelirken İzmir'de de Fatih Boy ve Tuğberk Uğurlu ile beraber 4 konuşmacı ile etkinliği gerçekleştirdik.

Etkinlikte ben de tahmin edeceğiniz üzere C# 5.0 isimli bir oturum gerçekleştirdim. :) Bunun yanında Pazar günü de bu sefer Portable Class Library'ler ile nasıl cross-platform uygulamalar geliştirebiliriz sorusunun cevaplarını aradık.

Her ne kadar İzmir'e çok yabancı olmasam da :) yine de bize çok süper ev sahipliği yapan Fatih Boy'a ve Tuğberk Uğurlu'ya ayrıca teşekkür etmek isterim. Süper 2 gün geçirdik.

Bu etkinlik kendi açımdan benim için sezonun ilk etkinliğiydi. Bundan sonra yeni etkinlikler de mutlaka olacaktır :) Yeni etkinlikler de görüşmek üzere :)



Bir C# Developerının Windows Phone 8 SDK'inden Beklentileri ve Hayal Kırıklıkları

Geçenlerde Windows Phone 8 SDK'i ile beraber gelen yenilikleri didiklerken bir yandan da SDK'in release notlarina gözatiyordum. Listedeki maddelere bakarken gözüm bir anda programlama dili tarafindaki yenilklere takildi  ve notlar içerisinde C# 4.0 ve C# 5.0 ile beraber gelen yenilikleri kullanabilecegimizi gördüm. Ben de bu release notlarini okuduktan sonra kollari sivadim ve C# 4.0 ve C# 5.0 yeniliklerini nereye kadar kullanabildigimizi, SDK'in bu özelliklerin ne kadarini destekledigini incelemeye basladim. Bakalim SDK benim beklentilerimle ne kadar uyusmus :) Iste basliyoruz...

C# 4.0 dynamic Keywordü

C# 4.0'in temasi olan dinamik programlama çerçevesinde C# tarafinda yapilan en büyük yeniliklerden biri dynamic keywordünün dil içerisine eklenmesi. dynamic keywordü ile beraber bildigimiz gibi nesneler içerisindeki üye ve metot erisimlerinde compilerin derleme islemi sirasinda yaptigi tip kontrollerini de dogrudan derleme zamani yerine çalisma zamanina tasiyarak gerçeklestirebiliyoruz. Böylece çogu zaman reflection kullanarak 5-10 satirda yazacagimiz islemi dynamic keywordü sayesinde  1 satirda halledebiliyoruz.

Windows Phone tarafinda dynamic keywordü nerede etkin olarak isimize yarar diye düsündügümde aklima ilk sirada JSON islemleri geliyor. Dönen bir JSON'i deserialize ederek elimizdeki nesnedeki üyelere dinamik olarak erismek aklima ilk gelen senaryo.

Not : Bu islemi aslinda dönen JSON'in formatina uygun tipler hazirlayarak da gerçeklestirebiliriz. Böylece derleme zamaninda elimizde ilgili nesne formatlari olur ve buna uygun olarak kodumuzu yazariz. Ancak her dönen JSON formatina göre nesneler olusturmak bizim isimizi zorlastirir. Yoksa illa ki dynamic kullanacagiz diye bir sart yok :)

Ilk olarak bir console uygulamasi ile basit  bir JSON ürettim. Ürettigim JSON  içerisinde de  Customer tipinden bir array bulunmakta. Customer tipinin propertyleri ise asagidaki gibi.

[DataContract]
public class Customer
{
    [DataMember]
    public int Id { getset; }

    [DataMember]
    public string Name { getset; }
}

Sonrasinda ise yazdigim Console uygulamasindan asagidaki gibi içerisinde dummy elemanlar bulunduran bir JSON elde ettim.

[ { "Id":1, "Name":"Ilkay" }, { "Id":2, "Name":"Ahmet" }, { "Id":3, "Name":"Osman" } ]

Test için gerekli JSON'i elde ettikten sonra simdi sira geldi Windows Phone uygulamasina. Hemen yeni bir Windows Phone 8 uygulamasi yaratiyoruz ve projemize Nuget'ten asagidaki komutla JSON.NET kütüphanesini ekliyoruz.

Not : Nuget yerine JSON.NET projesinin codeplex sayfasina giderek ilgili dll'leri bilgisayariniza indiribilir ve indirdiginiz dll'i manuel olarak Windows Phone 8 projenize ekleyebilirsiniz.

JSON.NET Kütüphanesini Projeye Eklenmek Için Gereken Nuget komutu : PM> Install-Package Newtonsoft.JSON

Nuget paketini de projemize ekledikten sonra sira geldi kodlama asamasina.

public void DynamicJsonTest()
{
    string json = "[{\"Id\":1,\"Name\":\"Ilkay\"},{\"Id\":2,\"Name\":\"Ahmet\"},{\"Id\":3,\"Name\":\"Osman\"}]";

    dynamic list = JArray.Parse(json);

    foreach (dynamic customer in list)
    {
        var id = customer.Id;
        var name = customer.Name;
    }
}

Gördügünüz gibi test amaciyla oldukça basit bir kod yazdim. DynamicJsonTest isimli metot içerisinde ilk olarak json isimli degiskenin içerisine daha önceden elde ettigim JSON stringini attim. Sonrasinda ise JSON.NET içerisindeki JArray tipinin içerisinde bulunan static Parse metodunu kullanip buradan dönecek olan nesneyi de dynamic olarak tanimladim. Böylece bana gelecek olan nesne içerisindeki propertylere kolay bir bir sekilde dinamik olarak erisebilir olacagim. Sonrasinda ise elimde olan array içerisindeki elemanlari foreach ile dönerek yine dynamic olarak tanimladigim her bir elemanin içerisinde bulunan Id ve Name propertylerini okumaya çalistim.

Eger burada dynamic keywordünü kullanmasaydim zaten bu sekilde kod yazmam mümkün degildi. Ben dynamic kullanarak kodun derlenme asamasinda bu kisimlarda yapilacak olan type-check islemini compilerin çalisma zamanina birakmasini sagladim.

Simdi yazdigimiz metodu, uygulama içerisindeki page açildiginda çagirilacak sekilde düzenleyelim ve uygulamayi çalistiralim.

class MainPage
{
    public MainPage()
    {
        InitializeComponent();

        DynamicJsonTest();
    }
}

Olmadi :( Itiraf etmem gerekirse bu hayal kirikligi Microsoft ile ilgili degil. Yani bu noktada olmamis diyerek topu direkt MS'e atamiyorum. :) Bu exceptioni almamizin temel nedeni kullandigimiz JSON.NET kütüphanesinin Windows Phone SDK 7.1 için yazilmis olmasi. Özetle bu senaryoyu test edebilmemiz için JSON.NET'in tam anlamiyla Windows Phone 8 destekleyen sürümünü beklememiz gerekmekte. Ancak tabi ki Windows Phone tarafinda dynamic keywordünün eklenmesi, benim aklima ilk olarak JSON senaryosunu getirmisti. Ancak simdilik bu konuda basarili bir sonuç alamadik. Gelelim ikinci senaryomuza.

ExpandoObject

Windows Phone içerisinde dynamic keywordü denince aklima ikinci olarak nedense ExpandoObject geldi. :) ExpandoObject basit olarak dynamic keywordü ile kullanildiginda içerisine dinamik olarak property eklenebilen bir tip. Windows Phone tarafinda ne ise yarar, nerelerde kullanilir sorularina cevap bulamasam da acaba eklemisler mi diye SDK içerisine bakamadan edemedim. :) Bir de baktim ki ExpandoObject orada System.Dynamic namespace'i içerisinde duruyor :) Hemen hizli bir sekilde asagidaki gibi test kodumu yazdim ve testimi gerçeklestirdim.

public void ExpandoObjectTest()
{
    dynamic expando = new ExpandoObject();
    expando.Id = 1;
    expando.Name = "Ilkay";

    MessageBox.Show(String.Format("ExpandoObject Degerleri Id={0} Name={1}", expando.Id, expando.Name));
}

Yukaridaki kodu uygulama ilk açildiginda page içerisinde çalistirdigimizda bakalim ne olmus ?

Bu testimizde basarili olduk. Ancak dedigim gibi ExpandoObject Windows Phone tarafinda nerede isimize yarar ben bulamadim. Hatta normalde de nerede kullanabilirim sorusuna cevap bulmus degilim :)

Özetle C# 4.0 Özellikleri

Yukaridaki iki madde açikcasi dynamic programlama özelliginde aklima gelen spesifik senaryolar. Tabi ki dynamic kullanabilecegimiz pek çok senaryo Windows Phone içerisinde mevcut. Özellikle reflection kullanmamiz gereken yerlerde artik dynamic ile ilerleyecek olmamiz bile bizim için oldukça önemli. Simdi geçelim C# 5.0 kismina.

C# 5.0 Async

C# 5.0 diyince hepimizin aklina asenkron programlama geliyor. Bildigimiz üzere C# 5.0 ile beraber gelen async ve await keywordleri ile beraber asenkron çagrimlari çok kolay bir sekilde gerçeklestirebiliyoruz ve callback implementasyonlarindan bu sayede yirtabiliyoruz :) Windows Phone tarafinda da yeni SDK ile beraber async ve await keywordlerini kullanabiliyormusuz. Tabi burada aklima gelen bu keywordleri kullanmanin ötesinde SDK içerisinde bulunan tiplerin  task tabanli asenkron patternini ne kadar uyguladiklarini incelemek oldu. Bunun için aklima gelen ilk tip WebClient tipi ve dolayisiyla da DownloadStringAsync metodu oldu. Bakalim bekledigimiz implementasyonu görebilecek miyiz ?

Varan 1 :) Callbackler orada öylece durup duruyor :D Async & await kullanarak ben internetten bir content download edemeyeceksem ne anladim bu isten. :) Neyse moralimi bozmayayim dedim bakalim belki baska yerlerde bu özelligi kullanabiliyorumdur. Sonra aklima gelen ikinci yer ve bana göre de en kritik nokta olan webServis baglantilari. Özellikle Business appleri gelistirme senaryolarinda tüm business logiclerini webservis tarafina yiktigimizda yazdigimiz uygulamanin web servise olan bagimliligi oldukça fazla oluyor. Web servis çagrilarini asenkron olarak yaptigimizdan dolayi da tüm kod neredeyse callback çöplügüne dönüyor. Özellikle Windows 8 tarafinda web servis proxylerinin task-based olarak üretilmesiyle artik yaptigimiz asenkron web servis çagrilarinda async ve await'i hizli bir sekilde kullanabilmekteyiz. Bakalim Windows Phone 8 tarafinda bu durum nasilmis ?

Hemen Visual Studio üzerinden Add Service Reference'a tikladim ve oradan da sol alttaki Advanced butonuna tiklayarak karsima çikan ekrana öylesine uzun süre baktim :( Neden mi ?

Varan 2 :( Itiraf etmem gerekirse WebClient vs... neyse de web servisleri tarafinda durumun böyle olmasi bu konuda beni bayagi hayal kirikligina ugratti. Bunun üzerine son bir umut asenkron islemlerin yine sikça kullanildigi launchers and choosers kismini test etmek geldi aklima. Insanda bir umut iste :)

Sonuç ne mi oldu ?

Yine hayal kirikligi :(

Tüm bu yaptigim testlerden sonra anladgim kadariyla Microsoft mevcut Windows Phone API'larina hiç dokunmamis. Bunun yaninda yeni gelen API'larda ise asenkron islemler C# 5.0 ile beraber gelen async ve await keywordleri ile kullanilabilecek sekilde task-based olarak implemente edilmis.

Özetle C# 5.0 Özellikleri

Kagit üzerinde baktigimizda Windows Phone 8 SDK'i içerisinde C# 5.0 ile beraber gelen asenkron programlama yeniliklerini kullanabiliyoruz. Ancak bu yenilikleri kullanabilecegimiz senaryolara baktigimizda maalesef henüz ilgili destegin verilmedigini görüyoruz SDK içerisinde. Bu nedenle Windows Phone 8 SDK'i C# 5.0 yeniliklerini kullanabilme adina bence sinifta kaldi. Blog yazilarina islerim dolayisiyla bir süre ara verdikten sonra böyle bir inceleme yazisiyla dönmek istedim. Tabi biraz iç karartici oldu orasi ayri :) 2012'nin son blog postu olan bu yazim vesilesiyle hepinize iyi yillar dilerim !


Windows Runtime Componentlerinde Asenkron İşlemler - C# 5.0

Bir önceki yazimizda Windows Runtime Componentlerini inceledikten sonra simdi sira geldi WinRT Component'leri gelistirme sirasinda karsimiza siklikla çikabilecek olan bir durumu incelemeye. :) Bildigimiz gibi Windows 8 ile beraber gelen WinRT altyapisi içerisinde bulunan pek çok yapi asenkron sekilde çalismak üzere tasarlandi. Hatta bu asenkron altyapiyi kullanmayi kolaylastirmak için de C# 5.0 ile beraber gelen asenkron programlama yeniliklerini kullanmaktayiz.

Hal böyle olunca Windows Runtime Componentleri içerisinde de asenkron olarak çalisacak yapilar gelistirmek artik neredeyse kaçinilmaz bir hal aldi. Bu yazimizin konusu da Windows Runtime Componentleri içerisinde asenkron sekilde çalisacak yapilari nasil gelistirecegimiz ile ilgili.

.NET Framework içerisinde asenkron islem diyince artik bir çirpida aklimiza gelen tip Task tipi olmakta. Önceki yazilarimizdan da hatirlayacagimiz üzere asenkron islemlerin yönetimini Task tipi üzerinden kolaylikla yapabiliyoruz. Peki Windows Runtime Component'i gelistirme tarafina geçtigimizde Task tipi hala daha orada mi yoksa karsimiza baska seyler mi çikiyor hemen durumu bir örnek yaparak inceleyelim.

Örnek olarak basit bir sekilde bir RSS adresinden ilgili contenti asenkron olarak download eden bir metot yazacagiz.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;

namespace AsyncWinRTComponent
{
    public sealed class AsyncHelper
    {
        public async static Task<string> DownloadRSSAsync()
        {
            HttpClient client = new HttpClient();

            var result = await client.GetAsync("http://www.ilkayilknur.com/feed");

            return await result.Content.ReadAsStringAsync();
        }
    }
}
Koda baktigimizda artik alistigimiz asenkron implementasyonlardan hiç bir farki yokmus gibi görünüyor :) Öyleyse kodu derleyelim bakalim herhangi bir sorunla karsilasacak miyiz :)

Error 1 Method 'AsyncWinRTComponent.AsyncHelper.DownloadRSSAsync()' has a parameter of type 'System.Threading.Tasks.Task<System.String>' in its signature. Although this generic type is not a valid Windows Runtime type, the type or its generic parameters implement interfaces that are valid Windows Runtime types. Consider changing the type 'Task' in the method signature to one of the following types instead: Windows.Foundation.IAsyncAction, Windows.Foundation.IAsyncOperation, or one of the other Windows Runtime async interfaces. The standard .NET awaiter pattern also applies when consuming Windows Runtime async interfaces. Please see System.Runtime.InteropServices.WindowsRuntime.AsyncInfo for more information about converting managed task objects to Windows Runtime async interfaces. C:\Users\ilkayilknur\AppData\Local\Temporary Projects\AsyncWinRTComponent\Class1.cs 39 42 AsyncWinRTComponent

Upps :) Bu hatanin artik ne demek oldugunu biliyoruz bir önceki yazimizdan. Bu demek oluyor ki Task tipi WinRT tarafinda bulunan bir tip degil. Sadece .NET Framework tarafinda bulunuyor ve asenkron olarak gelistirdigimiz  public metotlardan Task tipini dönemiyoruz. Peki ne yapacagiz ?

IAsyncAction ve IAsyncOperation<T> Interfaceleri

Windows Runtime tarafinda asenkron islemler dedigimizde de karsimiza Task tipi yerine IAsyncAction ve IAsyncOperation<T> interfaceleri çikiyor. IAsyncAction interface'i herhangi bir deger döndürmeyen asenkron islemlerin yönetiminde kullanilirken IAsyncOperation<T> interface'i de asenkron islem sonucu bir deger dönülen metotlarda kullanilmakta. Ayni zamanda IAsyncAction interface'i içerisinde yaptiginiz asenkron çagri ile ilgili olarak progress bilgisini de almamiz mümkün degil.

Simdi yapmis oldugumuz örnegimize dönersek asenkron metodumuz içerisinde islem sonlandiktan sonra bir deger döndürdügümüz için IAsyncOperation<T> interface'ini kullanmamiz gerekmekte. Peki kullanimi nasil gerçeklestirecegiz ? Burada en önemli konu bu. Çünkü metodumuzu async modifier'i ile tanimladik. Ancak metottan Task tipi dönemiyoruz ve Windows Runtime Component'i içerisinden de metodu disari açmamiz için de Task tipi yerine IAsyncOperation<T> dönmemiz gerekmekte. Yani isler oldukça karisik. :D

Simdi ilk olarak parça parça ilerleyelim ve yazmis oldugumuz metodu public yerine private olarak tanimlayalim ve metodu Windows Runtime Component'i içerisinde çalisacak olan bir iç metot olarak düsünelim.

private async static Task<string> DownloadRSSAsyncInternal()
{
    HttpClient client = new HttpClient();

    var result = await client.GetAsync("http://www.ilkayilknur.com/feed");

    return await result.Content.ReadAsStringAsync();
}

Evet simdi Windows Runtime Component'imiz basarili bir sekilde derlenebiliyor çünkü Task tipini döndügümüz metodumuzu private olarak tanimladik. (Metot isminin sonuna Internal kelimesini de yerlestirdik ki disari açtigimiz metotta yine DownloadRSSAsync ismini kullanabilelim ;) )

Simdi DownloadAsyncRSS isimli metodumuzu IAsyncOperation<T> dönecek sekilde tanimlayalim ve esas önemli kisma gelelim.

Bu yazdigimiz metot içerisinde de aslinda elimizdeki bulunan DownloadRSSAsyncInternal isimli metottan dönen Task tipini IAsyncOperation<T> interface'ine çevirebilirsek isimizi bitirebiliriz gibi görünüyor ne dersiniz ? Acaba elimizde bulunan bir Task tipini IAsyncOperation<T>'a çevirebilecek bir yapi var midir bakalim.

Iste aradigimiz tam da böyle birsey :) Windows Runtime içerisindeki yapilara baktigimizda içerisinde .NET Framework tipleri ve WinRT tipleri arasinda hizli geçisler yapabilecegimiz pek çok extension metot mevcut. Bu çerçevede elimizde bulunan Task tipinden de IAsyncOperation<T> üretmek için de AsAsyncOperation isimli bir metot WinRT içerisinde bulunmakta. Ayni sekilde elimizde bulunan Task tipinden IAsyncAction üretmemiz de mümkün. Öyleyse yazacagimiz Windows Runtime Component'in son hali su sekilde olacak.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;

namespace AsyncWinRTComponent
{
    public sealed class AsyncHelper
    {
        private async static Task<string> DownloadRSSAsyncInternal()
        {
            HttpClient client = new HttpClient();

            var result = await client.GetAsync("http://www.ilkayilknur.com/feed");

            return await result.Content.ReadAsStringAsync();
        }

        public static IAsyncOperation<string> DownloadRSSAsync()
        {
            return DownloadRSSAsyncInternal().AsAsyncOperation();
        }
    }
}

Gördügünüz gibi Windows Runtime Componentleri içerisinde asenkron metotlar yazip disari açmak da oldukça kolay. Özellikle .NET Framework ile WinRT tipleri arasindaki geçisleri yapmak gördügünüz gibi extension metotlarla oldukça kolaylasmis durumda. Bu extension metotlar sadece asenkron programlamada kullandigimiz tipler için degil ayni zamanda Streamler gibi yapilarda da islerimizi oldukça kolaylastirmakta. Tavsiyem eger WinRT tarafinda bir tipte sikisip kaldiysaniz ve bir islemi nasil yapacaginiza tam olarak emin degilseniz ve bu isi .NET Frameworkle hemen çözebileceginizi düsünüyorsaniz elinizdeki Windows Runtime tipini .NET Framework tipine çeviren bir extension metot olup olmadigini mutlaka arastirin ;)

Umarim faydali olmustur.

Hepinize kolay gelsin,

Not: Blogumu RSS veya sosyal sitelerden takip etmekte sikinti çekenler için blogumu mail ile de takip edebilirsiniz. Buradaki linke tiklayarak kaydinizi yapabilirsiniz. 



C# 5.0 Windows Runtime Desteği ve Windows Runtime Componentleri Geliştirme

Windows 8 ile beraber gelen WinRT altyapisi üzerinde bulunan en güzel özelliklerden biri de hiç süphesiz ki üzerinde pek çok programlama diliyle uygulama gelistirilebilmesi. Örnegin C, C++, C# veya VB bilen bir developer arayüz tarafinda da XAML kullanarak uygulamasini gelistirebilirken ayni zamanda Javascript ve HTML bilen bir front-end developer da artik Windows 8 üzerinde uygulama gelistirebilmekte. Peki ayni uygulama içerisinde bu dillerden ikisini birden kullanabiliyor muyuz ? sorusu eminim bir çogumuzun aklina geliyordur. :) Örnek olarak, bir Windows 8 uygulamasi gelistirirken data katmani bir C# developer tarafindan gerçeklestirilse ve arayüz kismi da javascript ve HTML ile yapilsa ortaya efsanevi senaryolar çikabilir :)  Peki bunu yapmak mümkün mü ? Evet mümkün :) Simdi gelin bunu nasil gerçeklestirebilecegimizi inceleyelim :)

WinRT (Windows Runtime) Components

Normalde uygulamalarimizi gelistirirken gerek baska uygulamalarin kullanmasi amaciyla gerekse kendi uygulamamiz içerisinde bir takim kavramlari dogrudan uygulama içerisine gömmek yerine yazdigimiz kodlari yeniden kullanabilmek amaciyla class libraryleri kullanmaktayiz. Windows Store uygulamalari tarafina baktigimizda ise bahsettigimiz senaryolarda kullanmak üzere adresimiz yine class library olmakta. Ancak Windows Store tarafinda yazmis oldugunuz class library'i hangi programlama dili ile yazdiysaniz bu class library'i kullanan Windows Store uygulamasi da class library'i yazdiginiz programlama dilinde olmali. Yani C# ile yazdigimiz bir class libraryi HTML & Javascript kullanarak yazilmis bir Windows Store uygulamasinda kullanamiyoruz.

Iste tam bu noktada karsimiza Windows Runtime Componentleri çikiyor. Windows Runtime Componentleri temelde class libraryler gibi yazmis oldugumuz kodun farkli uygulamalar tarafindan kullanilmasini saglarken bu gelistirmis oldugumuz komponentler ayni zamanda kendi yazildiklari programlama dili disinda yazilan uygulamalar tarafindan da kullanilabilmekte.

Windows Runtime Component ismi ilk olarak Visual Studio 2012 içerisinde uygulama yaratma penceresini açtigimizda Windows Store Apps bölümüne baktigimizda karsimiza çikmakta.

Projeyi seçip yarattigimizda karsimiza çikan sinif içerisinde dikkatimizi çeken ilk nokta yaratilan sinifin sealed olmasi !

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WinRTComponent
{
    public sealed class Class1
    {
    }
}

Öncelikli olarak Windows Runtime Component'i gelistirme konusunda söylemem gereken ilk sey yazdigimiz komponentin pek çok farkli programlama dili ile yazilmis olan uygulamalarda kullanilmasi nedeniyle gelistirme kisminda ne yazik ki bir takim sinirlamalarla karsilasmaktayiz. Çünkü her programalama dilinin kendine has bazi özellikleri ve kullanim detaylari bulunmakta bu nedenle de ortak bir paydada bulusabilmek ve yazilan komponentin tüm dilleri tarafindan düzgün olarak kullanilabilmesi için bu konulan kurallara uymak zorundayiz :)

Evet sinirlamalarin nedenini de açikladiktan sonra gelelim Visual Studio 2012'nin yaratmis oldugu default sinifta karsimiza çikan sealed keywordüne :) WinRT Componeneti gelistirmesi sirasinda karsilasacagimiz ilk kural Windows Runtime Component'i içerisinden disari açacagimiz siniflarin mutlaka sealed olmasi kurali :)  Ancak burada dikkat etmemiz gereken nokta bu kuralin sadece Windows Runtime Component'i içerisinden disari açacagimiz siniflarda geçerli olmasi. Yani eger sinifinizi public olarak tanimlamazsaniz ve sadece komponent içerisindeki iç islemlerde kullanirsaniz bu noktada siniflarinizda istediginiz sekilde inheritance kullanabilirsiniz. ;)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WinRTComponent
{
    //Windows Runtime bu duruma kizmaz :)
    class SealedOlmayanClass : List<string>
    {
    }
}

Simdi gelelim Windows Runtime Componenti içerisinde bulunan tiplerimizin içerisindeki metotlara. Çok basit olarak yazdigimiz Windows Runtime Component'in bir DataAccess componenti oldugunu düsünelim ve su sekilde bir metot yazalim.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WinRTComponent
{
    public sealed class DataAccess
    {
        public static List<Speaker> GetSpeakerList()
        {
            return new List<Speaker>             {                 new Speaker{NameSurname="Daron Yöndem",Subject="Azure"},                 new Speaker{NameSurname="Umut Erkal",Subject="Kinect"},                 new Speaker{NameSurname="Yusuf Öztürk",Subject="Powershell"},             };
        }

        public sealed class Speaker
        {
            public string NameSurname { getset; }
            public string Subject { getset; }
        }
    }
}
Gördügünüz gibi oldukça basit bir sekilde DataAccess isimli bir tip içerisine GetSpeakers isminde bir static metot ekledik ve bu metot içerisinde de hizli bir sekilde bir List içerisine dummy olarak datalari doldurup bu listi metottan döndürdük. Simdi bu yazdigimiz kodu derleyelim ve ne ile karsilasacagiz bakalim  :)

"Error 1 Method 'WinRTComponent.DataAccess.GetSpeakerList()' has a parameter of type 'System.Collections.Generic.List<WinRTComponent.Speaker>' in its signature. Although this generic type is not a valid Windows Runtime type, the type or its generic parameters implement interfaces that are valid Windows Runtime types. Consider changing the type 'System.Collections.Generic.List<T>' in the method signature to one of the following types instead: 'System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyList<T>, System.Collections.Generic.IEnumerable<T>'. C:\Users\ilkayilknur\AppData\Local\Temporary Projects\WinRTComponent\DataAccess.cs 11 37 WinRTComponent "

Yukarida yazanlar aslinda bize Windows Runtime Componenti içerisinde uygulamamiz gereken bir kurali basitçe hatirlatiyor. Nedir bu kural derseniz Windows Runtime Componenti içerisinde bulunan tipler içerisindeki public olarak tanimlanmis olan metotlarin imzalarinda mutlaka Windows Runtime tipi kullanilmalidir. Yani public olarak disari açtigimiz metotlarimizin parametrelerinin tipleri ile metodun dönecegi tip mutlaka Windows Runtime içerisinde bulunan bir tip olmalidir.

Bizim örnegimizde kullanmis oldugumuz tip aslinda baktigimizda bir .NET Framework tipidir. Ancak Windows Runtime interface tabanli bir altyapi oldugu için List tipinin Windows Runtime tarafindaki karsiligi IList interface'idir. Öyleyse yazdigimiz kodu su sekilde degistirirsek artik metodumuzu gelistirdigimiz komponent içerisinden disari açabiliriz.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WinRTComponent
{
    public sealed class DataAccess
    {
        public static IList<Speaker> GetSpeakerList()
        {
            return new List<Speaker>()
            {
                new Speaker{NameSurname="Daron Yöndem",Subject="Azure"},
                new Speaker{NameSurname="Umut Erkal",Subject="Kinect"},
                new Speaker{NameSurname="Yusuf Öztürk",Subject="Powershell"},
            };
        }
        //Windows Runtime buna da kizmaz :)
        private static List<Speaker> GetSpeakerListPrivate()
        {
            return new List<Speaker>()
            {
                new Speaker{NameSurname="Daron Yöndem",Subject="Azure"},
                new Speaker{NameSurname="Umut Erkal",Subject="Kinect"},
                new Speaker{NameSurname="Yusuf Öztürk",Subject="Powershell"},
            };
        }
    }
    public sealed class Speaker
    {
        public string NameSurname { getset; }
        public string Subject { getset; }
    }
}

Simdi kodumuz düzgün bir sekilde derlendi :) Simdi gelin yazmis oldugumuz komponenti HTML ve JS kullanarak yazacagimiz uygulamamiz da kullanalim.

Öncelikle projemizi yaratalim.

Projemizi yarattiktan sonra hizli bir sekilde ayni dll referansi eklyormusuz gibi Add Reference diyerek yazmis oldugumuz komponenti projemize referans olarak ekleyelim.

Ilk olarak default.html içerisine basit olarak bir table yerlestirelim ve bu tablo içerisinde Windows Runtime Component'i içerisinden gelecek datalari görüntüleyebilecek sekilde düzenlemeler yapalim.

<!DOCTYPE html>
<html>
<head>
    <meta charset=&quot;utf-8&quot; />
    <title>ComponentConsumer</title>

    <!-- WinJS references -->
    <link href=&quot;//Microsoft.WinJS.1.0/css/ui-dark.css&quot; rel=&quot;stylesheet&quot; />
    <script src=&quot;//Microsoft.WinJS.1.0/js/base.js&quot;></script>
    <script src=&quot;//Microsoft.WinJS.1.0/js/ui.js&quot;></script>

    <!-- ComponentConsumer references -->
    <link href=&quot;/css/default.css&quot; rel=&quot;stylesheet&quot; />
    <script src=&quot;/js/default.js&quot;></script>
</head>
<body>
    <table>
        <thead>
            <tr>
                <td>Isim</td>
                <td>Konu</td>
            </tr>
        </thead>
        <tbody id=&quot;table-body&quot;>
        </tbody>

    </table>
</body>
</html>

Simdi ise geldik Windows Runtime Componentimizi kullanacagimiz Javascript tarafina. Javascript tarafinda hemen default.js tarafina geçelim ve kodumuzu yazmaya baslayalim. Komponentin namespace'i olan WinRTComponent namespace'i  javascript tarafindan dogrudan erisilebilir durumda. Öyleyse hemen hizlica Windows Runtime Component'imizi kullanan ve ilgili tabloyu dolduran javascript kodumuzu yazalim.

(function () {
    "use strict";

    WinJS.Binding.optimizeBindingReferences = true;

    var app = WinJS.Application;
    var activation = Windows.ApplicationModel.Activation;

    app.onactivated = function (args) {
        if (args.detail.kind === activation.ActivationKind.launch) {
            if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                var speakers = WinRTComponent.DataAccess.getSpeakerList();

                for (var i = 0; i < speakers.length; i++) {
                    var speaker = speakers[i];

                    var body = document.getElementById("table-body");

                    body.innerHTML += "<tr><td>"+ speaker.nameSurname + "</td><td>"+ speaker.subject + "</td></tr>";
                }
            } else {

            }
            args.setPromise(WinJS.UI.processAll());
        }
    };

    app.oncheckpoint = function (args) {
    };

    app.start();
})();

"Öncelikle yazmis oldugum javascript ve html'i okuyan front-end developerlara bu konuda oldukça tecrübesiz ve kötü oldugumu bildirmemde fayda var. :) (Sonra böyle kod mu olur seklinde serzenislerde bulunmayin :) )"

Neyse konumuza dönersek yukarida hizli bir sekilde kodumuzu yazdik ancak bu noktada sizlerin dikkatini çekmek istedigim önemli bir nokta var.

Farkettiyseniz yukarida yazmis oldugumuz C# kodlarinda metodumuzun adi GetSpeakerList'ti. Ayni sekilde propertylerimizin ismi de NameSurname ve Subject'ti. Ancak javascript tarafina geçtigimizde gördügünüz gibi metotlarimizin ve propertylerimizin ismi javascript tarafinda kullanilan casing'e göre yeniden düzenlendi ve böylece HTML ve JS tarafinda bulunan developerlarin bu isimlendirmelerde hiç bir sekilde sikinti çekmemesi saglandi. Microsoft'un development deneyimi konusunda yapmis oldugu en güzel hareketlerden biri olarak bu noktayi belirtmeden geçemedim ;)

Evet uygulamamizi gelistirdik simdi bir de uygulamamizi çalistiralim ve çalistigini görelim :)

Gördügünüz gibi HTML ve Javascript ile yazdigimiz uygulamamiz içerisinde C# ile yazmis oldugumuz Windows Runtime Componentini kullanabilmekteyiz. Windows Runtime Componentlerini aslinda pek çok farkli noktalarda devreye sokup uygulamalarimizi gelistirebilmekteyiz. Örnegin bu senaryomuzda oldugu gibi DataAccess tarafinda Windows Runtime Componentleri kullanabilecegimiz gibi hesaplama kisimlarinda, parse  vb..  gibi pek çok farkli noktada yine Windows Runtime Componentleri kullanabilmeteyiz. Hatta Windows Runtime Component'i kullanarak bir Windows Store projesini tüm yükü tek developera vermemek adina back-end kismini C# developera yaptirabilir arayüz tarafini da backend'den gelen metotlari çagirarak bir HTML & Javascript uygulamasi olarak gelistirebilirsiniz. Burada önemli olan Windows Runtime Componentlerini dogru zamanda devreye sokabilmek.

Bir kaç Windows Runtime Component Gelistirme Kurali Daha...

Yazimizin sonlarina gelirken yukarida bahsetmis oldugum Windows Runtime Component'i gelistirirken dikkat etmemiz gereken kurallara 2-3 ufak kurali daha ekleyerek yazimiza son verelim :)

  • Windows Runtime Component'i içerisinde disariya açilan structlarin sadece public alanlari olmalidir. Içerisinde private alanlar barindiramazlar. Ayrica disariya açilan structlar içerisinde propertyler de bulunamaz.
  • Windows Runtime Componentleri içerisinde sadece sistem içerisinde tanimli olan generic tipler kullanilabilir.
  • Windows Runtime Component'i içerisinde disariya sunulan tipler sealed olmalidir. Tek istisna XAML kontrolleridir. XAML kontrollerinde inheritance kullanilabilir.

Evet kurallarimiz bu kadar.

Umarim faydali bir yazi olmustur.

Hepinize kolay gelsin !



C# 5.0 - Caller Info Attributes ile Kolaylaşan INotifyPropertyChanged İmplementasyonu

Windows 8, Windows Phone 7, Silverlight ve WPF ile uygulama gelistirirken kullandigimiz ve MVVM gibi senaryolarda da siklikla basvurdugumuz INotifyPropertyChanged implementasyonu çogu zaman eger bir kod snippeti kullanmiyorsaniz biraz can sikici ve zaman kaybedici bir implemantasyon olabilmekte.

INotifyPropertyChanged interface'ini neden kullandigimizi kisaca hatirlamamiz gerekirse, elimizde bulunan tiplerin binding islemlerinde içlerinde barindirdiklari propertylerde bir degisiklik oldugu durumlarda bagli olduklari UI kontrolü üzerinde durumlarini  güncellemeleri için gerekli olan elemanlari saglamakta INotifyPropertyChanged interface'i.

INotifyPropertyChanged interface'inin içerisine baktigimizda ise çok basit bir sekilde sadece 1 event ile karsilasiyoruz.

namespace System.ComponentModel
{
    public interface INotifyPropertyChanged
    {
        event PropertyChangedEventHandler PropertyChanged;
    }
}

Evet interface implementasyonu aslinda sadece 1 eventin tetiklenmesini gerektiriyor. Bu eventi tetiklerken bir de hangi propertynin degerinin degistigini de eventi tetikleme sirasinda bildirmemiz gerekiyor ki bizim eventimizi dinleyen kontroller degisen propertynin hangisi oldugunu algilayabilsin ve buna göre de sadece degisen propertynin degerini ekranda güncellesin.

Simdi hemen basitçe Insan isminde bir tip tanimlayalim ve bu tip içerisinde Ad ve Soyad isimli property ekleyelim ve bu propertylerde de degisiklik oldugunda bind edildikleri kontrollerde de ilgili degisikliklerin yansitilmasi için bir de INotifyPropertyChanged interface'ini implemente edelim.

public class Insan : INotifyPropertyChanged
{
    private string ad;

    public string Ad
    {
        get { return ad; }
        set
        {
            ad = value;
            RaisePropertyChanged("Ad");
        }
    }

    private string soyad;

    public string Soyad
    {
        get { return soyad; }
        set
        {
            soyad = value;
            RaisePropertyChanged("Soyad");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
        }
    }
}

Evet implementasyon bu kadar basit. Farketmis oldugunuz ilk sey eminim ki auto-implemented property kullanmayisimizdir. Çünkü burada ilgili propertye deger atanma sirasinda araya girmemiz gerekmekte. Bu nedenle auto-implemented property kullanamiyoruz. Bunun haricinde ise interface içerisinde bulunan eventi gerekli parametrelerle tetiklemek için RaisePropertyChanged isimli bir de yardimci metot yazdik. Bu metotta parametre olarak içerigi degisen propertynin ismini almakta. Daha sonra ise yazmis oldugumuz bu metodu propertylerin setterlari içerisinde çagirmaktayiz.

C# 5.0 - Caller Info Attributes ile Basitlesen Implementasyon

Yukaridaki implementasyona baktigimizda aslinda bize zaman kaybettiren ve interface'in implementasyonunu zorlastiran nokta aslinda RaisePropertyChanged metodunu çagirirken sürekli olarak degisen propertynin ismini vermemiz.

Simdi C# 5.0 ile beraber CallerInfo Attributes konusunu hatirlarsak aslinda Caller Info Attributes bize yapilan bir çagrim  ile ilgili olarak bilgi vermekteydi. Yani çagrim yapilan yerin satiri, bulundugu dosya ve çagrimda bulunan üyenin adi gibi alanlari saglamaktaydi. Sanki çagrim yapan üyenin adi tam bizim ihtiyacimiz olan sey :) O zaman RaisePropertyChanged metodunu su sekilde degistirsek nasil olur bir bakalim :)

public void RaisePropertyChanged([CallerMemberName]string propertyName = null)
{
    if (PropertyChanged != null)
    {
        PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
    }
}

Gördügümüz gibi bu sekilde artik metodu çagiran üyenin adini C# 5.0 ile beraber gelen Caller Info Attributes ile alabilmekteyiz. Insan tipi içerisinde bulunan propertylerin setterlarinda da artik RaisePropertyChanged metodunu parametre vererek çagirmamiza gerek yok. Çünkü compiler bu parametreyi artik kendisi arka planda ilgili metoda opsiyonel parametre degeri olarak geçirmekte.

Son olarak Insan tipinin içerigi su sekilde olmakta.

public class Insan : INotifyPropertyChanged
{
    private string ad;

    public string Ad
    {
        get { return ad; }
        set
        {
            ad = value;
            RaisePropertyChanged();
        }
    }

    private string soyad;

    public string Soyad
    {
        get { return soyad; }
        set
        {
            soyad = value;
            RaisePropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged([CallerMemberName]string propertyName = null)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
        }
    }
}

Baktigimizda aslinda programalama dili tarafinda gelen bir yenilik bambaska bir tarafta belki de hiç aklimiza gelmeyecek bir noktada gördügünüz gibi isimizi kolaylastiriyor. Iste programlama dillerinin de en güzel yani bu :)

Hepinize kolay gelsin :)



Windows 8 Metro Style Data Uygulamaları ve GridView Kontrolü - C# & XAML

Merhaba Arkadaşlar,

Windows 8 metro style development konusuna giriş yaptığımız yazılarımızın ardından verdiğimiz kısa aradan sonra kaldığımız yerden serimize devam ediyoruz :). Ara vermemizin sebebi de aslında Windows 8 RP ve Visual Studio 2012 RC sürümlerinin çıkmasını beklememizdi. Kabul ediyorum biraz da benim tembelliğimdi :)

Bu yazımızda ise sizlerle Windows 8 Metro Style Data uygulamalarına giriş yapıyor olacağız ve bu kapsamda da GridView kontrolünü incelemeye başlayacağız. Ancak öncelikle Windows 8 tarafındaki data erişimine bir bakalım.

Wind0ws 8 Metro Style Uygulamalarında Veri Erişimi

Windows 8 tarafında bir data-driven Metro Style Uygulama geliştirmek istediğimizde aslında temelde data çekebileceğimiz 2 kaynak bulunmakta.

Bunlar,

  • Web üzerinde bulunan kaynaklar
  • Database içerisinde saklanan veriler.

Web üzerinden bulunan kaynaklardan kastımız aslında RSS gibi veya REST tabanlı doğrudan HTTP tabanlı isteklerde bulunup yanıtı alabileceğimiz kaynaklar.

Database'den kastımız da database içerisinde bulunan veriler ve bu verilerin Windows 8 Metro Style uygulamalarda kullanılması.

Web üzerinde bulunan kaynaklara erişimi ise WinRT üzerinde asenkron olarak gerçekleştirebilmekteyiz. Bunun nedeni de aslında web üzerindeki kaynağa erişim sırasında uygulamanın UI threadini bloklamamak ve kullanıcıya daha iyi bir kullanıcı deneyimi sunabilmek.

Şimdi database tarafına gelirsek. Elimizde bir WPF uygulamamız olduğunu düşünelim ve bu uygulamayı Windows 8 Metro Style uygulamaya çevirmeye çalışacağımız varsayalım. Aklımıza gelecek sorulardan ilki elimizdeki verileri Windows 8 Metro ortamına nasıl aktaracağımız olacak. Çünkü WPF tarafında code-behind tarafında SqlConnection sql = new SqlConnection(); şeklinde database'e bağlanabiliyorduk. (Bu kullanımın doğru olduğunu savunmuyorum sadece böyle yazıldığını varsayıyorum :) ) Peki Windows 8 Metro tarafında da işler böyle yürüyor mu ?

Cevap hayır :) Öyle direk hayır deme bize nedenlerini söyle diyecekseniz peki o zaman başlıyorum :)

  • Öncelikle yazdığınız uygulamanın ARM işlemcili tabletlerde dahi çalışacağını unutmayın. Bu nedenle database manipülasyonunu bu cihazlar üzerinden yapmak dönen datayı işlemek gibi işlemler sizin uygulamanızın performansını oldukça etkileyecektir. Ayrıca uygulamanız amacını da aşacaktır. Bunun yanında data cacheleme mekanizmaları da bu senaryoda olmayacaktır.
  • İkinci nedenimiz de geliştirdiğimiz uygulamada veri erişim kısmında bir değişiklik olacağı zaman bunu tabletteki uygulamayı güncelleyerek mi yapacağız. :) Sizin uygulamanız belki farklı platformlarda da kullanılıyor ve bir platformdaki istek için database de bazı güncellemeler yaptınız ve aslında bu yaptığınız değişiklik sizin uygulamanızda veriyi gösterme anlamında hiçbirşey ifade etmiyor ancak veri erişim katmanında değişiklik gerekiyor. Bu durumda Marketplace'e uygulamamızın update'ini mi göndereceğiz. Bir update süreci sertifikasyon gibi aşamaları gerektirdiği için belki 1 haftadan önce marketplace'te yer alamayacağını düşünürsek. Sanırım database'e metro style uygulamadan bağlanma hevesiniz bayağı bir azaldı. Gelelim son nedene.
  • Zaten şu anda istesenizde Windows 8 Metro Style tarafında SqlConnection sql = new SqlConnection(); yapamıyorsunuz :). Çünkü öyle bir API yok :D Zaten olsa da yapmamanız gerekir yukarıda bahsetmiş olduğum nedenlerden dolayı. ;)

Not : Yukarıda bahsettiğim nedenler uygulamanızın ana datasına erişimin nasıl yapılması ile ilgiliydi. Yani metro style uygulama ile ilgili bir takım user settinglerini localde saklayabileceğiniz yapılar zaten şu anda WinRT içerisinde mevcut.

Peki elimizde bir database var ve biz bu elimizdeki verileri nasıl dışarıya açacağız ? Cevap oldukça basit : Web Servis :) Peki web servise erişimi nasıl yapacağız ? Bunun da cevabı oldukça basit. Asenkron :) Asenkron işlemlerin yönetiminde de C# 5.0 ile beraber gelen async / await yapısını kullanıyor olacağız. Detaylı bilgi için buraya :)

Bu konsept aslında sırf Metro Style uygulamalar ile sınırlı değil. Eğer elinizde bulunan dataları çeşitli platformlarda kullanmanız gerekiyorsa araya mutlaka bir web servis katmanı alarak dış platformları data access katmanında soyutlamanız doğru bir hareket olacaktır ;)

Şimdi gelelim artık metro style uygulama geliştirme tarafına :)

GridView Kontrolü

Metro Style uygulama geliştirirken en sık kullanacağımız kontrollerden biri de GridView kontrolü. Elimizdeki verileri toplu şekilde göstermek istediğimizde içerisindeki elemanları Grid şeklinde gösterebilen bir kontrol GridView kontrolü. Evet çok fazla uzatmadan artık pratikte görmek için File=>New Project ve Blank Metro Style Application'ı seçerek boş bir uygulama yaratıyoruz.

Uygulama olarak bir restoranımız olduğunu düşünelim ve bu restoran için bir Windows 8 Metro Style uygulama geliştireceğimizi varsayalım. Restoran içerisinde satılan yiyecekler ile ilgili bilgiler ise web servis aracılığıyla geliyor olacak ve biz yemek listesini web servis içerisinde bulunan GetFoods metodu ile alacağız. Bu metot içerisinden dönen Food tipinin içeriği ise şu şekilde.

[sourcecode language="csharp"] public class Food { [DataMember] public string FoodName { get; set; } [DataMember] public decimal Price { get; set; } [DataMember] public string ImageUrl { get; set; } } [/sourcecode]

Şimdi ilk olarak bir GridView kontrolü alalım ve MainPage.xaml içerisine sahneye yerleştirelim.

[sourcecode language="xml"] <Page x:Class="App4.MainPage" IsTabStop="false" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App4" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <GridView x:Name="foodGrid" Margin="50,140,15,15"/> </Grid> </Page> [/sourcecode]

Şimdi sıra geldi datayı web servisten çekme kısmına. İlk olarak web servis referansını projemize ekliyoruz.

Advanced butonuna tıkladığımızda asenkron web servis erişiminin bulunduğu bölüme dikkat ;)

Şimdi sıra geldi web servis bağlantısına ve aldığımız dataları GridView kontrolüne bağlamaya. Bu işlemi sayfanın OnNavigated eventi içerisinde gerçekleştiriyor olacağız. OnNavigated metodu MainPage.xaml sayafasına navigasyon gerçekleştiğinde tetiklenmekte.

[sourcecode language="csharp"] protected async override void OnNavigatedTo(NavigationEventArgs e) { FoodService.ServiceClient client = new FoodService.ServiceClient(); ObservableCollection<Food> foodList = await client.GetFoodsAsync(); foodGrid.ItemsSource = foodList; } [/sourcecode]

Gördüğünüz gibi eğer daha önce XAML tabanlı bir platformda (Silverlight,WPF,Windows Phone vb...) geliştirme yaptıysanız kodlar sizin için oldukça tanıdık gelecektir. Windows 8 Metro Style tarafında da genel konsept aslında aynı. GridView kontrolünün ItemsSource propertysine elimizdeki listeyi veriyoruz.

Şimdi F5'e basarak uygulamamızı çalıştıralım ve uygulamamız nasıl görücek bir bakalım.

Sanki tam istediğimiz gibi olmadı :) Nesnelerimiz GridView kontrolü içerisinde göründü ancak bağladığımız Food tipinin sadece ismi ekrana yazıldı. Yani toString metodu çağrıldı.

Daha önce XAML tarafıyla uğraşanlar GridView içerisinde ItemTemplate yaratmadık diyeceklerdir. :)  Çünkü GridView şu anda içerisine verdiğimiz nesneleri nasıl göstereceği konusunda bilgi sahibi değil ;)

ItemTemplate yaratmanın 2 yöntemi var. İlki Visual Studio 2012 ile beraber templateları özelleştirme. Bir diğeri de Expression Blend içerisinden templateları özelleştirme.

Visual Studio 2012 içerisinde yapmak istediğimizde, designer görünümünde kontrole sağ tıkladığımızda tıpkı Expression Blend'de yaptığımız gibi kontrolün templatelarını özelleştirebilmekteyiz.

Bir diğer yöntem de Expression Blend kullanmak. Çünkü artık Expression Blend, Visual Studio 2012 kurulumuyla beraber bilgisayarımıza yüklenmekte ve rahatça kullanabilmekteyiz. Bunun için de değiştirmek istediğiniz sayfaya sağ tıkladığınızda "Open in Blend" 'e basarak ilgili sayfayı Expression Blend içerisinden editleyebilmekteyiz.

Ben eskiden kalma alışkanlıklarımdan dolayı tasarım tarafını daha çok Blend tarafında hallediyorum. Ancak yukarıda göstermiş olduğum gibi siz de isterseniz bu işlemleri Visual Studio 2012 içerisinden gerçekleştirebilirsiniz.

Şimdi geri dönelim konumuza :) GridView kontrolüne içerisindeki her bir nesneyi nasıl göstereceğini söyleyecektik. Bunun için de GridView içerisinde bir ItemTemplate yaratmamız gerekmekte. Biz ItemTempate olarak yemeğin resmini ve resmin altında da yemeğin adını yazıyor olacağız.

[sourcecode language="xml"] <Page x:Class="App4.MainPage" IsTabStop="false" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App4" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <DataTemplate x:Key="FoodTemplate"> <Grid> <Image HorizontalAlignment="Left" Height="246" VerticalAlignment="Top" Width="250" Stretch="UniformToFill" Source="{Binding ImageUrl}"/> <Border BorderThickness="1" HorizontalAlignment="Left" Height="22.393" Margin="0,223.607,0,0" VerticalAlignment="Top" Width="250" Background="Black" Opacity="0.4"> <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Margin="3,2,0,0" Text="{Binding FoodName}"/> </Border> </Grid> </DataTemplate> </Page.Resources> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <GridView x:Name="foodGrid" Margin="50,140,15,15" ItemTemplate="{StaticResource FoodTemplate}"/> </Grid> </Page> [/sourcecode] Şimdi uygulamamızı yeninden çalıştıralım ve neler değişti bir bakalım :)

Tamam kabul ediyorum uygulama biraz fazla akıl çelici oldu :) Ancak gördüğünüz gibi GridView içerisinde ItemTemplate'ı yaratarak istediğimiz şekilde nesneyi gösterebildik.

Data uygulamalarına giriş yaptığımız bu yazımızda sizlerle Windows 8 Metro Style uygulamalarda veri erişiminin nasıl yapılabileceğimizi ve en sık kullanılan data kontrollerinden olan GridView kontrolünü nasıl kullanabileceğimizi incelemiş olduk.

Daha önce  mevcut Windows 8 uygulamaları görenler "yahu bunun böyle gruplanmış halleri oluyor. Mesela zeytinyağlılar bir grupta, tatlılar bir grupta görünüyor" o nasıl oluyor diye sorabilirler :) Bu da diğer yazımızın konusu olsun ;)

Görüşmek Üzere,



C# 5.0 - Caller Info Attributes

Merhaba Arkadaşlar,

Şu ana kadar C# 5.0 ile ilgili makalelerimizde sizlerle C# 5.0 ile beraber gelecek olan Asenkron Programlama yeniliklerini inceledik. Her ne kadar C# 5.0'ın teması Asenkron Programlama olsa da C# 5.0 ile beraber gelecek olan bir programlama dili yeniliği daha bulunmakta.

Asenkron Programlama'nın yanında biraz üvey evlat muamelesi  :) görüyor olsa da bu yeniliğin adı Caller Info Attributes.

Geliştirdiğimiz uygulamalar içerisinde olmazsa olmaz dediğimiz Cross Cutting bölümlerden biri de Loglama bölümüdür. Geliştirdiğimiz uygulamaların büyüklüğüne, hedef kitlesine, konseptine bağlı olarak içerlerinde yapmış olduğumuz loglamalar daha da derinleşebilmekte veya belirli sınırlar dahilinde kalabilmektedir.

__FILE__ , __LINE__ Macrolarının C# Karşılığı ?

Eğer daha önce C, C++ gibi dillerle uğraştıysanız bu programalama dilleri içerisinde yaptığınız çağrımların, bulunduğu satır numarasını veya bulunduğu dosyanın pathi gibi bilgileri yukarıda görmüş olduğumuz gibi macroları kullanarak alabilmekteyiz. Peki bu macroların C# tarafında karşılığı var mı ?

Cevabımız hayır :) Yukarıdaki macroların yaptıklarını aslında şu anda Reflection kullanarak kendimiz de gerçekleştirebilmekteyiz. Ancak programlama dili tarafında C#'ın 4.0 sürümü de dahil olmak üzere istediğimiz bu fonksiyonaliteyi  otomatize eden, doğrudan arka planda kendisi sağlayan bir yapı bulunmamakta.

Tabi bizler de developer olarak işlerimizi hızlandırması açısından bu macroları C# içerisinde kullanabilmeyi istiyoruz. İşte C# 5.0 ile beraber gelen Caller Info Attributes özelliği ile bu fonksiyonaliteleri artık compiler kendisi otomatize etmekte ve developerların kullanımına sunmakta.

Peki kullanım aynı C ve C++'ta olduğu gibi __FILE__,__LINE__ macroları şeklinde mi olmakta ? Bu sorunun cevabı  da hayır :) Çünkü macrolar C# ve VB içerisinde desteklenmiyor ve desteklenmeyecekte. Bunun yerine Attribute mantığını kullanarak hızlı bir şekilde implementasyonlarımızı gerçekleştirebiliyoruz.

Caller Info Attributes özelliği kapsamında System.Runtime.CompilerServices namespace'i içerisinde  3  yeni attribute ile karşılaşmaktayız.

Bunlar,

[CallerFilePath] : Çağrım yapılan yerin bulunduğu dosyanın pathini verir.

[CallerLineNumber] : Çağrım yapılan yerin bulunduğu dosya içerisindeki satır numarasını verir

[CallerMemberName] : Çağrım yapan üyenin adını verir.

Bu 3 attribute'ün de target type'ı Parameter olarak set edilmiştir. Yani sadece metot imzalarındaki parametrelere bu attributeleri verebiliriz.

Peki buna göre kullanımı nasıl yapacağız ?

Aslında herşey oldukça basit. Attributeleri C# 4.0 ile beraber gelen Optional Parameters özelliği ile beraber kullanacağız.

Evet çok uzatmadan kod kısmına geçelim.

[sourcecode language="csharp"] static void Log(string message, [CallerFilePath]string path = null, [CallerLineNumber]int line = 0, [CallerMemberName]string memberName = null) { Console.WriteLine(String.Format(@"Message:{0}\n FilePath:{1}\n LineNumber:{2}\n MemberName:{3}", message, path, line, memberName)); } [/sourcecode]

Yukarıda gördüğümüz gibi çok basit bir şekilde Caller Info Attributeleri kullanarak istediğimiz değerleri elde edebiliyoruz. Bunu yaparkende Optional Parameters özelliğini kullanıyoruz. Böylece optional olan parametreyi metot çağrımı sırasında boş geçtiğimizde compiler arka planda ilgili parametreye attribute özelliğine göre ilgili değeri atıyor.

Şimdi isterseniz yukarıda yazmış olduğumuz Log metodunu basit bir console uygulamasından çağıralım ve sonucu görelim.

[sourcecode language="csharp"] static void Main(string[] args) { Log("Caller Info Attributes kullanıyoruz !"); } [/sourcecode]

Uygulamayı çalıştırdığımızda ise aşağıdaki gibi bir sonuçla karşılaşmaktayız.

Console uygulamasında Log metodunu çağırırken gördüğünüz gibi path,line ve memberName parametrelerini metoda geçirmedik. Böylece de compiler arka planda kendisi bu alanların karşılıklarını otomatik olarak atadı.

C# 5.0 ile beraber gelen Caller Info Attributes özelliği gördüğümüz gibi biz developerların özellikle loglamalar sırasında  sıklıkla kullanacağı özelliklerden biri olma konusunda kuvvetli bir aday. Özellikle işimizi oldukça kolaylaştırdığınız söylememiz herhalde yanlış olmaz. ;)

C# 5.0 ile ilgili yazılarımız şimdilik bu kadar. Gelişmeler, iyileştirmeler ve ekstra yenilikler oldukça blog üzerinden inceliyor olacağız.

Görüşmek Üzere,