SİTE
İÇİ ARAMA |
|
|
Blogroll |
|
|
|
|
|
|
C# 4.0 - Bizleri Bekleyen Yenilikler |
|
| Gönderiliyor lütfen bekleyin... |
|
|
Uzun bir süredir (son bir senelik zaman dilimi
içerisinde) C# 4.0
ile birlike gelen yeniliklerden haberdarız.
Şöyle bir kaç sene öncesini hatırlıyorum da. :)
Visual
Studio 2005, Whidbey
kod adı ile yayınlanmış ve
C# 2.0
ile birlikte gelen pek çok yenilik olmuştu.
Ancak bunlar içerisinde belkide en önemli olanı,
CLR(Common
Language Runtime) çekirdiğinde değiştirilme yapılmasını
da zorunlu kılan generic
mimariydi. Tabiki generic dışında gelen,
yield
anahtar kelimesi, isimsiz metodlar(anonymous
methods), static sınıflar
ve
diğerleride önemli gelişmelerdi. Zaman ilerledi ve
C# 3.0
ile birlikte bu
kez hayatımıza, generic modelinden daha fazla etki yapan
LINQ(Language INtegrated Query)
girdi. Bir geliştirici olarak her zaman için
yeniliklere açık olmamız ve yakalayabildiğimiz ölçüde takip etmemiz gerektiğini
düşünüyorum. Bu bir geliştirici için neredeyse bir yaşam tarzı. Dolayısıyla artık
C# 4.0 üzerinde konuşmanın zamanı geldide geçiyor.
C# 4.0
ile birlikte gelen yeniliklerin daha çok
dinamik çalışma zamanını(Dynamic Language Runtime-DLR)
kullanan diller üzerinde odaklanmış durumda olduğunu söyleyebiliriz.
Peki bu ne anlama geliyor? DLR
tarafını ilgilendiren dillere ait nesneler ile
daha kolay konuşulması olarak küçük bir sebep belirtebiliriz. Bu nedenle
C# 4.0
ile birlikte gelen önemli yeniliklerden birisi olan
dynamic
anahtar kelimesi sayesinde,
Python,
Ruby veya
Javascript
ile üretilen nesnelerin
C# 4.0
tarafında late-binding
ile ele alınması mümkün. Hatta var olan
.Net
nesnelerinin reflection
kullanılmadan ele alınması veya
COM objelerine
ait üyelerin çağırılmasında bu anahtar kelimeyi kullanabiliyoruz. Aslında C#' ın
2.0, 3.0 versiyonunda gelen yenilikler nasıl ki belirli ihtiyaçlar nedeni ile ortaya
çıkmışsa, C# 4.0 ile gelen yenilikleride bu anlamda düşünmemiz ve araştırmamız gerekiyor.
Bu yazımda sizlerle ilk olarak, dynamic kelimesi ile ilgili olan araştırmalarım
sonucu elde ettiğim bilgileri paylaşıyor olacağım. İşe ilk olarak aşağıdaki şekilde
görülen yapıya sahip olduğumuzu düşünerek başlayacağız.

Şimdi bu yapıyı kısaca açıklayalım.
Commands
isimli
sınıf kütüphanesi(Class
Library) IGraphic
arayüzünü(Interface)
uygulayan Circle
ve
Rectangle isimli sınıflara sahiptir.
IGraphic arayüzü
namespace Commands
{
public interface IGraphic
{
void Draw();
}
} |
Circle sınıfı
using System;
namespace Commands
{
public class Circle
:IGraphic
{
#region IGraphic Members
public void Draw()
{
Console.WriteLine("Circle...");
}
#endregion
}
}
|
Rectangle Sınıfı
using System;
namespace Commands
{
public class Rectangle
:IGraphic
{
#region IGraphic Members
public void Draw()
{
Console.WriteLine("Rectangle...");
}
#endregion
}
}
|
Console Application
tipinden olan uygulamamız, Commands
isimli sınıf kütüphanesini referans etmekte olup başlangıçta aşağıdaki kod içeriğine
sahiptir.
using Commands;
namespace CSharp4Features
{
class Program
{
static void Draw<T>(T graphObject)
where T : IGraphic
{
graphObject.Draw();
}
static void Main(string[] args)
{
#region Başlangıçtaki
durumumuz
Draw<Circle>(new Circle());
Draw<Rectangle>(new Rectangle());
#endregion
}
}
}
|
Uygulamayı çalıştırdığımızda aşağıdaki sonucu alırız.

Bu kod parçasında dikkat edilmesi gereken önemli
noktalardan birisi, generic Draw<T>
metodudur. Burada yer alan generic T tipine
IGraphic
arayüzünden türeme koşulu getirilmiştir. Bu sebepten dolayı,
IGraphic arayüzünü uygulayan tüm tiplere
ait Draw metodunu çağırmamızı
sağlayan tek bir metod geliştirmiş oluyoruz. Dolayısıyla tek yapılması gereken,
Draw<T> metodunun kullanıldığı
yerde, doğru tipe ait(bu örnek için Rectangle veya Circle)
nesne örneğini parametre olarak aktarmaktır.
Şimdi burada, bizi dynamic kelimesine götürecek
bir veya bir kaç sebep arayacağız. İşte bir kaç blog içerisinde yakaladığım
ortak soru geliyor...Ya Console
uygulaması, IGraphic
arayüzüne erişemiyor olsaydı. Bunu ayarlamak son derece kolay. Tek yapmamız gereken
IGraphic arayüzünün public
olan erişim belirleyicisini kaldırmak.(Bir başka deyişle
internal'a çekmekki bu durumda söz konusu tipe sadece tanımlı olduğu
assembly içerisinde erişilebildiğini hatırlayalım) Bu durumda
Draw<T> metodumuz için
derleme zamanı(Compile Time)
hatası alınacaktır. Peki ne yapılabilir? Reflection
tekniklerinden yararlanarak ilgili tipin
Draw metodunun çağırılması
sağlanabilir. Yani kodu aşağıdaki hale getirebiliriz.
using Commands;
using System.Reflection;
namespace CSharp4Features
{
class Program
{
static void Draw<T>(T graphObject)
{
MethodInfo methodInfo
= typeof(T).GetMethod("Draw");
if (methodInfo == null)
{
System.Console.WriteLine("Method
bulunamadı");
}
methodInfo.Invoke(graphObject,
new object[0]);
}
static void Main(string[] args)
{
Draw<Circle>(new Circle());
Draw<Rectangle>(new Rectangle());
}
}
}
|
İlk olarak
typeof
metodu ile T tipi elde
edilmekte ve Draw isimli
metod istenerek MethodInfo
tipinden bir referansa aktarılmaktadır. Bilindiği üzere
reflection mimarisinde, çalışma zamanında
tipler ve üyelerine ait bilgiler elde edilmekte ve istenirse üyelerin yürütülmesi(örneğin
metodların çağırılması) sağlanabilmektedir. Bu nedenle
ilk olarak T tipinin
çalışma zamanı referansı üzerinden Draw
metodu elde edilmeye çalışılır. Sonrasında ise eğer
MethodInfo referansı
null değilse
Invoke fonksiyonuna gerekli
parametreler gönderilerek Draw
metodunun icra edilmesi sağlanır. (Tabiki çalışma zamanında
gelen T nesne örneğine ait olan Draw metodunun)
Uygulamayı
bu haliyle çalıştırdığımızda yine aynı sonuçları alırız.

Ancak tabiki metodun bu yeni halinde tip güvenliğinden(type-safety)
bahsetmemiz mümkün değildir. T için herhangibir tip kullanılabilir.
Peki dynamic anahtar kelimesi burada nasıl bir
yaklaşım sunmaktadır. İşte aynı metodun C# 4.0
için dynamic anahtar kelimesi ile yazılmış hali.
using Commands;
using System.Reflection;
namespace CSharp4Features
{
class Program
{
static void Draw<T>(T graphObject)
{
dynamic obj = graphObject;
obj.Draw();
}
static void Main(string[] args)
{
Draw<Circle>(new
Circle());
Draw<Rectangle>(new
Rectangle());
}
}
}
|
Bu seferde aynı çıktıyı alırız. Tabi burada dikkat
edilmesi gereken bir kaç nokta vardır ve kodun kısalmış olması bunlardan birisi
değildir ;) Öncelikli olarak Draw
metodu, söz konusu Circle
veya Rectangle nesne
örneklerine çalışma zamanında bağlanmaktadır. Bu zaten bizim reflection tekniği
ile yapmakta olduğumuz bir işlemdir. Diğer yandan
.Net Reflector aracı yardımıyla üretilen uygulama
koduna bakıldığında söz konusu metod için aşağıdaki
IL çıktısının oluşturulduğunu görebiliriz.

private static void Draw<T>(T graphObject)
{
object obj = graphObject;
if (<Draw>o__SiteContainer0<T>.<>p__Site1 == null)
{
<Draw>o__SiteContainer0<T>.<>p__Site1
= CallSite<Action<CallSite, object>>.Create(new CSharpInvokeMemberBinder(CSharpCallFlags.None,
"Draw", typeof(Program), null, new CSharpArgumentInfo[]
{ new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null) }));
}
<Draw>o__SiteContainer0<T>.<>p__Site1.Target(<Draw>o__SiteContainer0<T>.<>p__Site1,
obj);
} |
Görüldüğü gibi
Draw<T>
metodu içerisinde <Draw>o_SiteContainer0<T>
isimli generic bir tipin kullanıldığını ve bununda
IL(IntermediateLanguage) tarafına
eklendiğini görmekteyiz. Bir başka deyişle derleme işleminden sonra yine reflection
kullanılan kod parçaları içeriye dahil edilerek,
Circle
veya Rectangle tiplerinden
olan nesnelerin Draw
metodunun çağırılması sağlanmış oldu.
NOT :
Burada dikkat edilmesi gereken önemli bir noktada şudur. Eğer Draw<T> metoduna,
Circle ve Rectangle dışında bir tip atarsak(özellikle Draw metodu olmayan) bu durumda
RuntimeBinderException
tipinden bir istisna alırız.
Tabiki bu kısım ve detaylarını daha iyi kavramak
için belki biraz daha zamana ihtiyacımız olacak. Ancak bu anahtar kelimenin tek
kullanım şeklinin, reflection ile elde edilen tiplere ait üyelerin çağırılmasını
kolaylaştırmak olmadığınıda belirtmek isterim. Öyleki,
dynamic kelimesi ile
COM objelerinin ve bu sayede
unmanaged API' lerin
dinamik olarak ele alınması mümkün olabilir. Hatta,
JSON formatına sahip bir nesnenin
dynamic kelimesi ile kolayca ele alınabileceğini söyleyebiliriz. Bu noktada
Office API' sine ait
nesnelerin dynamic kelimesi ile son derece etkili ve kolay kullanılabildiğini de
belirtmek isterim. Üstelik işin içerisine yine
C#
4.0 ile gelen opsiyonel ve isimlendirilmiş parametreler(Optional
and Named Parameters) adlı yeniliklerinde girdiğini söyleyebilirim.
Özet olarak artık C# programlama dilinin, dinamik olarak
türlendirilmiş tiplere ait nesnelerle daha kolay konuşabildiğini söyleyebiliriz.
Elbetteki C# 4.0 ile birlikte gelen başka
yeniliklerde var. Bu yeniliklerde, diğerleri gibi belirli ihtiyaçlardan ortaya
çıkmıştır. Şimdi bir süreliğine aşağıdaki kod parçasını göz önüne alalım.
using System;
using System.Reflection;
using Word=Microsoft.Office.Interop.Word;
namespace NewFeatures2
{
class Program
{
static void Main(string[] args)
{
Word.Application wrdApp
= new Microsoft.Office.Interop.Word.Application();
wrdApp.Visible = true;
object fileNamePath =
@"C:\Yeni Ozellikler.docx";
object missingValue = Missing.Value;
wrdApp
.Documents
.Open(ref fileNamePath,
ref missingValue, ref missingValue, ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue);
Console.WriteLine("Kapatmak için bir tuşa basınız");
Console.ReadLine();
}
}
}
|
İlk olarak şunu belirtmek isterim; bu basit Console
uygulamasında Microsoft.Office.Interop.Word
assembly' ına ait bir referans yer almaktadır.

Program, sistemde yüklü olan
Yeni Ozellikler.docx isimli bir
Word dosyasını açmak
için gerekli kodları içermektedir. Bu Word dosyasının açılması içinse,
Microsoft.Office.Interop.Word
assembly' ından yaralanılmaktadır. Bu assembly aslında Word ile konuşabilmemizi
sağlayan COM API'sini sarmalayan(Wrap) bir
yönetimli(Managed)
kütüpanedir. Kod içerisinden, Word dökümanına erişebilmek için
Application tipinden bir nesne oluşturulmaktadır.
Uygulamanın görünürlüğü Visible özelliği ile set edildikten sonra ise,
Open metodundan yararlanarak
ilgili Word belgesinin açılması sağlanmaktadır. Ancak bu kod parçasında geliştirici
açısından bazı zorluklar olduğu rahatlıkla gözlemlenebilir.
1- Open
metoduna ait 16 adet
parametrenin tamamının girilmesi zorunludur. Aşağıdaki şekilde durumun sıkıcılığı
gözler önündedir.

NOT :
Burada Juval Lowy'
nin
IDesign
şirketinde kullandığı ve pek
çok şirket tarafından da benimsenen
C# kodlama standartları aklıma geliyor.
Buradaki belirttiği bir maddeyi hatırlıyorum.
"Metodların
argüman sayılarının 5 i geçmesinden kaçının. Eğer öyleyse struct tipini kullanın".
;) Burada bir
COM API'
sinin Wrap edilmiş
kütüphanesi içerisindeki bir fonksiyonun parametre yapısının değiştirilemeyeceği
fikri belleğimizi tamamıyla kaplayabilir. Tabi
C#
3.0 sonrasında bir fırsat olabileceği de akıllara
gelebilir. "Bir Extension metod yardımıyla Open metodu
yerine bir alternatif geliştirebilir miyim? En azından parametre sayısını düşürmemizi
kolaylaştıracak..." Bunu denemenizi öneririm.
2- Parametreler
COM nesnesine iletildiğinden,
dışarıdan yapılacak olan atamalarda object
tipinin kullanılması gerekmektedir.
3- ref anahtar
kelimesinin kullanılması zorunludur.
Gerçekte,
Open
metodu içerisinde işimize yarayan ve bizim için anlam ifade eden tek bir parametre
yer almaktadır. O da açılmak istenen dosyanın adıdır. Diğer parametrelerinin hiçbirini
kullanmadığımız halde yazmak zorunda olduğumuzu görüyoruz. Keşke sadece gerekli
olanları yazsabilseydik; o zaman bu iş daha kolay olmazmıydı? :( Nitekim buradaki
Open metodu haricinde, çok daha fazla sayıda argüman ile çalışabilen COM fonksiyonellikleri
söz konusu olabilir. Böyle bir durumda tam olarak tüm parametreleri yazma zorunluluğu
bir kenara dursun, bunların bütünün ne işe yaradığınında bilinmesi gerekir.
Sanırım bu cümlelerden zaten nereye varmak istediğimi
anlatabilmişimdir. .Net in gelecek nesillerinin en büyük hedeflerinden birisi dinamik
dillere ait nesneler ile konuşabilmek ve bunu mümkün olduğunca kolaylaştırmaktır.
Bu noktada COM API' leri gibi nesnelerinde kullanımı söz konusudur. Aynen yukarıda
geliştirdiğimiz örnekte olduğu gibi. Bu nedenle
C#
4.0 içerisinde seçimlik parametre kullanımına izin
veren geliştirmeler yapılmıştır(Optional Parameters).
Buna göre yukarıdaki kod parçasını C# 4.0 stilinde aşağıdaki gibi geliştirebiliriz.
Optional Parameters ile
using System;
using System.Reflection;
using Word=Microsoft.Office.Interop.Word;
namespace NewFeatures2
{
class Program
{
static void Main(string[] args)
{
Word.Application
wrdApp = new Microsoft.Office.Interop.Word.Application();
wrdApp.Visible
= true;
wrdApp.Documents.Open(@"C:\Yeni
Ozellikler.docx");
Console.WriteLine("Kapatmak için bir tuşa basınız");
Console.ReadLine();
}
}
}
|
Bu kod parçası çalıştığında da aynı sonucu alırız.
Yine Word belgesi açılacak ve içeriği görüntülenecektir. Hem kodun okunurluğu kolaylaşmıştır,
hem de kısalmıştır. Diğer taraftan parametre değerini aktarırken
ref kullanılmadığına dikkat etmemiz
gerekiyor.(Ommit ref özelliği)
Üstelik object tipinden
değişken ataması yerine doğrudan dosya adresininin içeriğini gönderebildiğimizede
dikkat edelim.
Tabi ihtiyaçlar bitmek bilmiyor. Burada görüldüğü
gibi gereksiz olan parametrelerin hiç biri bildirilmemiştir. Ayrıca
ref anahtar kelimeside herhangibir
şekilde kullanılmamıştır. Ancak arada başka bir parametrenin daha kullanılması gerekirse...
:(
Söz gelimi 3ncü parametre dosyanın yanlız okunabilir(ReadOnly)
modda açılıp açılmayacağını belirtir. Optional Parameter
tekniğini kullanırsak ikinci parametreyi atlamamız mümkün olmayacaktır. Acaba böyle
bir vakada kodu yine istemediğimiz şekliyle aşağıdaki gibi geliştirmemiz mi gerekir?
using System;
using System.Reflection;
using Word=Microsoft.Office.Interop.Word;
namespace NewFeatures2
{
class Program
{
static void Main(string[] args)
{
Word.Application
wrdApp = new Microsoft.Office.Interop.Word.Application();
wrdApp.Visible
= true;
object fileNamePath
= @"C:\Yeni Ozellikler.docx";
object missingValue
= Missing.Value;
object onlyRead = true;
wrdApp
.Documents
.Open(ref fileNamePath, ref missingValue,
ref onlyRead, ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue);
Console.WriteLine("Kapatmak için bir tuşa basınız");
Console.ReadLine();
}
}
}
|
Oysaki C# 4.0 bu gibi durumlar için
isimlendirilmiş parametre(Named Parameters)
kullanımını olanaklı kılmaktadır. Aşağıdaki şekilde görüldüğü gibi,
intellisense' de bize yardımcı
olmaktadır.

Dolayısıyla yukarıdaki kod parçasını şu şekilde geliştirebiliriz;
Named Parameters kullanımı ile
using System;
using System.Reflection;
using Word=Microsoft.Office.Interop.Word;
namespace NewFeatures2
{
class Program
{
static void Main(string[] args)
{
Word.Application
wrdApp = new Microsoft.Office.Interop.Word.Application();
wrdApp.Visible
= true;
wrdApp.Documents.Open(@"c:\Yeni Ozellikler.docx",
ReadOnly: true);
Console.WriteLine("Kapatmak için bir tuşa basınız");
Console.ReadLine();
}
}
}
|
Son olarak
Platform
Interop Assembly(PIA) ile ilgili gelen yeniliklerden
birisine değinmek istiyorum. Normal şartlarda
Visual
Studio 2010 öncesinde bir COM API' sini uygulamaya
referans ettiğimizde, sarmalanan kütüphanenin özelliklerinde aşağıdaki şekilde görülen
Embed Interop Types
isimli bir kriter olmadığı bilinmektedir.

Oysaki Visual Studio 2010 ile birlikte bu
özellikte gelmektedir. Bu tabiki sadece C# 4.0
diline bağlanacak bir yetenek olarak düşünülmemelidir.
Peki ne işe yarar? Eğer yukarıda geliştirdiğimiz
C# 4.0
örneğinin ildasm(Intermediate Language
DisAsseMbler) çıktısına bakacak olursak
aşağıdaki durum ile karşılaşırız.
Göze çarpan özel bir nokta yer almamaktadır.
Ancak Microsoft.Office.Interop.Word
assembly' ının özelliklerinde yer alan Embed Interop
Types seçeneğini true
olarak değiştirir ve söz konusu uygulamanın
IL
çıktısına tekrardan bakarsak aşağıdak sonuçlarla karşılaşırız.

Görüldüğü gibi API içerisinde yer alan tipler,
.Net
programı içerisine birer tip olarak gömülmüştür. Aslında bu yenilik,
PIA' ların, geliştirilen
asıl uygulama içerisine tip bazında gömülerekten taşınabilmelerini kolaylaştırıcı
bir özellik olarak görülebilir. Bu konudaki araştırmalarıma devam ediyorum. Yeni
bilgiler kazandıkça sizlerle paylaşmaya devam ediyor olacağım. Böylece geldik bir
makalemizin daha sonuna. Bu makalemizde sizlere
C# 4.0 ile birlikte gelen bir kaç yeniliği aktarmaya çalıştım. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.
NOT: Örnekler 2008 PDC'de yayımlanmış olan Visual
Studio 2010 PreBeta sürümü üzerinden geliştirilmiştir. Yeni çıkacak sürümlerde
hem anlatılan konularda hemde Visual Studio ortamında farklılıklar olabilir.
Burak Selim ŞENYURT
MVP (Connected System Developer-2008,C# 2007,2006)
Makale:
C# 4.0 - Bizleri Bekleyen Yenilikler C#, Visual C# ve .NET Burak Selim Şenyurt
|
|
ŞUB 9
2010 |
Mükemmel bir anlatım.Tebrikler.Makaleleriniz sanki sınırsız gibi.Tekrar teşekkürler. |
|
AĞU 11
2009 |
Hocam,makaleleriniz sayesinde çok şey öğrendim. En büyük yardımcınsınız. Hiç görüşmedik ama bilmenizi isterim ki çok büyük emeğiniz var bende. Elinize sağlık. |
|
MAY 10
2009 |
Olayın özü bundan daha iyi anlatılamazdı bence. Teşekkürler |
|
|
Sayfalar :
|
|
|
|
|
|
|
-
-
Eklenen Son 10
-
Bu Konuda Geçmiş 10
Bu Konuda Yazılmış Yazılmış 10 Makale Yükleniyor
Son Eklenen 10 Makale Yükleniyor
Bu Konuda Yazılmış Geçmiş Makaleler Yükleniyor
|
|
|
|