C#-JAVA’ nın Sonu mu?

C#-JAVA’ nın Sonu mu?

JAVA’nın gün geçtikçe yaygınlaşan kullanım alanları, Microsoft firmasını gerçekten güç duruma düşürdü. 1995 yılında JAVA ufukta göründü ve geçtiğimiz 6 yıl içerisinde kendisi ile rekabet eden diğer bütün popüler diller üzerindeki üstünlüğünü gösterdi. Başlangıçta Microsoft bu duruma direndi ancak daha sonra, JAVA meşhur Visual Studio nun Visual J++ adı altında bir parçası oldu. JAVA’ yı çıkaran Sun Microsystems, Microsft aleyhine lisans anlaşmasını ihlal ettiği gerekçesiyle dava açtı ve Microsft buna karşılık bir bedel ödemek zorunda kaldı.

Sonunda ortalık yatıştı ve Microsft Visual J++ ‘ın yeni versiyonlarını çıkarmaya devam etti. Fakat Microsoft’un JAVA dilini Visual Studio paketinin içine koyma çabası başarıya ulaşamadı ve en sonunda başarız oldu. Elbette ki bu başarısızlık ta pazarlamanın iyi yapılamamasının da etkisi olmuştur. Sonuç olarak Microsoft 1998 yılından sonra Visual J++ için yeni bir versiyon çıkarmadı, ve yakın bir gelecek içinde de bu tür planlarını askıya aldı.

Sonunda Microsft anladı ki bir yerlerden ödünç alınan ürünler uzun vadede işe yaramayacak, bu yüzden Microsoft, daha çok JAVA’ya benzeyen ancak JAVA da olmayan bir takım yeni özellikleride ekleyerek C# (C Sharp) isimli yeni bir programlama dili tasarlamaya başladı. Bu makalenin amacı C# hakkındaki şüphelerinizi gidermek ve JAVA ile karşılaştırmaktr.

Peki, Microsoft ‘un ilgilendiği bu pakette önerdikleri nelerdi?

Bu pakette Microsoft’un önerdiği iki ana yapı vardı:

.NET Mimarisi
Yeni bir dil – C#

.NET Mimarisi

.NET mimarisinin 3 ana bileşeni vardır.

Herhangi bir dil tarafından kullanalabilecek Web Servisi denilen yeni bir ortak servisler kümesi. Bu servisler ara kod(intermediate kod) dediğimiz mimariden bağımsız bir yapı içerisinde çalıştırılır. Bu ara kod daha sonra çalışma zamanında CLR denilen birim tarafından çalıştırılır, ki bu bahsedilen CLR birimi programla ilgili kaynakları yöneten ve programın çalışmasını izleyen bir birimdir.

.NET’in birincil amacı geliştiriciler açısından Web Servislerini kullanarak mimariden bağımsız uygulamalar geliştirmeyi kolaylaştırmaktır. Örneğin bir servise hem PC hem PDA hemde Mobil cihazlardan erişebilme gibi.

Şimdi bu yapıları tek tek inceleyelim.

Web Servisleri

Web Servisleri internet standartları olan XML ve HTTP üzerinden hizmet verebilen yapılardır. Daha teknik bir dille söylemek gerekirse, web servisleri internet üzerinden erişilebilen bir kütüphane, bir fonksiyon yada bir veritabanı gibi düşünülebilir. Örneğin; bir web servisi herhangi bir şirkete ait bir yıllık ciroyu hesaplayıp veren bir fonksiyon içerebilir. Bu fonksiyon parametre olarak bir yıl değeri almaktadır. Dolayısıyla istemci uygulamamız geri dönüş değeri 2000 yılına ait ciroyu Yıllık_ciro(2000) gibi bir fonksiyonu çağırarak elde edebilir.

Aslına bakarsanız bir firmada farklı platformlara yönelik hazırlanmış uygulamalar arasında ki senkronizasyonu sağlamak amacıyla Web Servisleri kullanılabilir. Buna rağmen Web Servisi kelimesindeki “Web” sözcüğü ile internet ortamında web servislerinin kullanımını ön plana çıkarmak için kullanılmıştır. Fakat normal ugulamarda da çoğu durumda Web servsilerinden faydalanmamız mümkündür.

Web servislerinin en güzel yani servisin her yerden istenebilmesidir. Kısacası web servisi uygulama alanlarına Windows, Linux, Unix, Mac-Os gibi platformların yanısra komut satırından çalışan diğer ortamlar bile girebilir. Yani Web servisleri sayesinde platform bağımsız uygulamalar geliştirebiliriz. İnternet standartlarının platform bağımsız olması ve web servislerininde internet üzerinden çağrılması, web servislerinin herhangi bir işletim sisteminin yada programlama dilinin tekelinde olmamasını sağlamıştır.

Ara Kod(İntermediate Code)

Ara kod! Kulağa pek hoş gelmiyormu? Bu bana birşeyi hatırlatıyor….hımmm….. JAVA? Evet, elbette. Microsoft açık bir şekilde .NET ile JAVA’ nın çalışma platformundan esinlendiğini kabul etti böylece. .NET ortamında, programlar bir ara dil üzerinden ikili koda çevrilir. İşletim sisteminden ve donanımdan bağımsız olan bu dile MSIL(Microsft Intermediate Language) denir. (Çevirenin notu: MSIL artık IL olarak anılmaktadır.) IL kodu, JAVA platformundaki JVM de olduğu gibi CLR tarafından derlenip makina koduna çevrilir. IL kodunu makine koduna çeviren derleyicilere ise JIT(Just In Time) derleyicileri denilmektedir.

IL kodu şeklinde derlenmiş bir uygulamaya taşınabilen çalıştırılabilir(Portable Executables) uygulama denir. CLS nin şartı bütün dillerin ortak bir noktada toplanmasını sağlayacak kurallar oluşturmaktır. Ancak burada bir problemle karşılaşıldı, oda bütün dillerin farklı semantik yapıya sahip olmasıdır, örneğin her dilin farklı özellikleri olabilir, bazı diller prosedürel tekniği destekler, bazıları nesne yönelimi tekniğini destekler, bazılarında operatör yükleme varken bazılarında yok, ve diğerleri.

Bir dilin .NET uyumlu olması için CLS(Common Language Specification) standartlarına uyması gerekir. Dolayısıyla bir dilin .NET ortamına uyumlu olması için sağlaması gereken iki koşul vardır:

CLS ile uyumluluk
Kaynak kodu IL koduna çevirecek derleyici

Peki yukarıda bahsedilen komplek yapı bize ne kazandıracak? Diller arası çalışma? Bütün .NET uyumlu dillerde yazılmış kodlar ara koda çevrileceği için herhangi bir dil de yazılmış bir nesneyi başka bir dilde yazdığımız programda oluşturabiliriz. Örneğin .NET uyumlu C# dilinde yazılmış bir sınıf ile, VB programcısı bir nesne oluşturabilir.

Peki JAVA’nın bu durumdaki rolü ne? Yada Microsoft JAVA yı devre dışımı bıraktı? Tam olarak değil. JAVA nın .NET dilleri arasında olması düşünüldü. JAVA kodunu IL koduna çevirecek derleyici Rational firması(ünlü UML yazılım geliştirme aracını geliştiren firma) tarafından hazırlanmaktadır.(Not: bu yazının yaıldığı tarihte JAVA derleyicisi yeni yazılıyordu). Peki bu eskisiyle aynı JAVA mı olacak? Aslına bakarsanız hayır. JAVA kodları artık byte kod yerine IL koduna çevrilecek.Yeni JAVA, J2EE’nin sağladığı RMI, JMS, JDBC, JSP vs gibi API’ lerden faydalanamayacak.Yeni geliştirilen JAVA ile EJB mantığı yerini .NET’in dağıtık nensne modeline bırakmıştır.

CLR(Common Language Runtime)

Yukarıda da bahsettiğim gibi CLR, konsept olarak JVM(Java Virtual Machine)’ye çok benzemektedir. JVM bellek yönetimi için kaynakların düzenlenmesi, gereksiz bilgi(çöp bilgi) toplama ve işletim sistemi ile uygulama arasındaki iletişimi sağlayan bir birimdir.

CLR, dizilerin sınırlarının aşılması, tahsis edilmeyen bellek alanına ulaşlması, bellekteki gerekli alanların üzerine yazılması gibi programların çökmesine sebep olan geleneksel hataların durumunu izler. Bu izleme hızlılıktan fedakarlık sağlamak demektir. Microsoft bu yöntemle performansın %10 oranında düşeceğini kabul etmiştir, fakat bu sistemler devamlılık ve güvenilirlik açısından daha ön plandadır.

.NET Senaryoyu Sunuyor

Bu yılın ortalarına doğru .NET’in tam sürümü piyasada olacak(Çevirenin not: Yazının yazıldığı tarihe göre düşünmelisiniz) Microsoft mimarisinin kullanıldığı eski uygulamalar .NET uyumlu Windows 2000 sunucularında hala geçerliliğini sürdürecek. Eski mimarideki uygulamaların yeni gelecek bu mimariye taşınabilmesi için gerekli araçların olacağını Microsft garanti etmiştir, ancak bunun iyi bir çözüm olacağı görülmüyor. Halihazırdaki taşımak için geliştirilen araçların bu işlemi %100 yapamayacağı açıktır. Geliştiriciler bu taşıma işleminden dolayı epeyce bir zaman harcayacak.

.NET’in Geleceği

.NET’in gelişi ile birlikte, elektronik uygulamaların altyapısının değişmesi gerektiğini inkar edemeyiz. Her dil, .NET uyumlu olması için yapısal bir takım değişiklikler yapmalıdır. Java dilini bir düşünün. Hangi Java daha tercih edilir sizce, JVM ile JAVA mı yoksa .NET ile JAVA mı? Bu sorunun cevabını siz okurlarıma bırakıyorum.

Yeni Bir Dilin Doğuşu

Microsft .NET’in 27 farklı dil desteğinin olduğunu iddia ediyor.(Çevirenin not: Şu an bu dillrein sayısı daha fazla) Bir dili .NET uyumlu hale getirmek dilin varolan mimarisine yeni yamalar eklemek demektir. Microsft bu durumu değerlendirerek temel olarak .NET mimarisi hedef alınarak yeni bir dil tasarladı. Bu dilin adı C-Sharp yada C#. Bu dili daha çok JAVA yı andırıyor olmasına rağmen var olan diğer dillerden de güzel özellikler alınıp C# diline eklenmiş gibi gözüküyor.

C# nedir?

Geçen iki yüzyıl içerisinde C ve C++ dilleri ticari ve iş uygulamaları geliştirmek için en çok kullanılan diller olmuştur. Bu iki dil programcılara büyük miktarda kontrol sağlamasına rağmen, üretim açısından çok olanak sağlamıyordu.

Bu dilleri Visual Basic gibi dillerle karşılaştırdığımızda aynı uygulamyı geliştirmenin C ve C++ dillerinde daha fazla zaman aldığı bir gerçektir. Yıllardan beri istenilen tek şey güçlülük ve üretim hızının arasında bir denge kuracak bir dilin olmasıydı. Geçtiğimiz yıllarda bu problem Sun Microsystems tarafından anlaşıldı. Ve diğer dillerin çalışma mantığından tamamen farklı olan JAVA dilini geliştirdi. Bytecode konsepti, JVM ve açık kaynak kod vs ve sonuç olarak bedeva olması bu farklılıklardan bir kaçıdır.

Fakat unutulan bir şey vardı. JAVA ile yazılan kodlar başka dillerde kullanılamıyordu. Tekrar kullanılabilirlik denen bu özelliği JAVA desteklemiyordu. Böylece, tekrar kullanılabilirliği standartlaştıracak bir ortamın gerekliliği ortaya çıktı. Bu ortam .NET ile sağlandı, zira .NET ile uyumlu dillerin sağlaması gereken CLS standartları mevcuttur. Fakat daha öncede dediğim gibi .NET ile tam uyumlu çalışacak ve diğer dillerin güzel özelliklerini sağlayacak yeni bir dile ihtiyaç vardı.
Hakkında konuştuğum bu dil C-Sharp diye okunan C#’tan başka birşey değildir.

C#, modern, nesne yönelimli, tip güvenliğine büyük önem veren, temel hesaplama ve haberleşme gibi geniş bir yelpazedeki uygulamaları geliştirmek için .NET platformu için tasarlanmış yeni bir dildir.

C#, C++ programcılarınına üretkenliği ve C++ dilinin güçlülüğünün göstergesi olan kontrol mekanizmalarından taviz vermeden hızlı bir şekilde uygulama geliştirme olanağı tanımaktadır.

C# ve JAVA’nın Karşılaştırılması

İyi bir dilin sağlaması gereken şeyler nelerdir? Yada programcıların gönlünü kazanmak için bir dil yapısında neyi barındırmalıdır? Bu sorular on yıllardan beri dil tasarımcılarının üzerinde durduğu tartışmaya açık sorulardır. Bütün programcılar tarafından ittifakla kabul edilmiştir ki, bu soruların cevabını en iyi şekilde veren dil JAVA’dır. C#, sentaks olarak JAVA’ya çok benzemektedir, ancak derinlerine daldıkça Microsoftun bu dili tasarlamak için çok efor sarfettiğini ve bu yeni eklenen bu özellikler için Microsoft’a teşekkür etmemiz gerektiğini anlarız.

Şimdi isterseniz JAVA nın özelliklerini tek tek ele alıp bunları C#’ın ilgili özellikleriyle karşılaştıralım.

Intermediate Language(Ara dil)

JAVA kaynak kodu byte koduna çevirirken C# MSIL(IL) koduna çevirir. IL dili .NET uyumlu bütün dillerde yazılmış olan programların ortak olarak derlendiği dildir. Fakat IL ve Byte Kod’un çalışma mantığında ince bir farklılık vardır. JAVA daki bytecode’lar yorumlanırken IL kodu derlenerek makina koduna çevrilir.

Interoperability (Diller arası uyumluluk)

JAVA’nın gücü platform bağımsız olmasından kaynaklanmaktayken C# aynı zamanda diller arasındaki uyumluluğuda sağlar. Yani dil bağımsızlığı. Güzel!. Şimdi bunları biraz daha açalım: JAVA ile yazılmış bir program JVM’nin olduğu bütün sistemlerde çalışması sözkonusu iken, C# ile yazılan bir kod diğer .NET uyumlu diller tarafından tekrar kullanılabiliyor. Örneğin bir dilde yazılan sınıf, diğer .NET uyumlu diller ile rahatlıkla kullanılabilir.

Bellek Yönetimi

JAVA otomatik bellek yönetimi sağlamaktadır.(daha teknik bir deyimle gereksiz bilgi toplama mekanizması denir.) Bu özellik programcılar tarafından takdirle karşılanmıştır. Fakat eski C/C++ programcıları JAVA diline geçmeye çalışınca bu özellik onları rahatsız ediyordu. Bu tür programcıların problemlerini da göz ardı etmeden, C# otomatik bellek yönetiminin yanında programcının belleği kendisininde yönetmesini sağlayan sistem sunmuştur. Ne demek bu? Basit. C#’ ta hala pointer kullanabiliyoruz. (Çevirenin Notu: Müthiş!)

Harici Kodlara Referans

Harici kodlar C# ve JAVA’da benzer şekilde ele alınmıştır. JAVA dilinde import anahtar kelimesi kullanılırken C# ta using anahtar kelimesi kullanılmaktadır. JAVA paket(packages) kullanırken, C# isim uzayları(namespace) kullanır. Fakat bunların anlamları aşağı yukarı aynıdır.

Veri Tipleri

Bir dilin gücü, dilin desteklediği farklı veri türleri tarafından belirlenir. Veri tipleri programcılara güçlülüğü ve esnekliği sağlayan varlıklardır. C#, JAVA daki bütün veri tiplerini sağlamıştır, bunun yanısıra JAVA da olmayan bazı türler de eklenmiştir, örneğin bazı işaretsiz(unsigned) veri türleri JAVA da yoktur. Ayrıca C# ta kayan noktalı(floating point) bir veri türü olan 12 byte’lık decimal türü de mevcuttur.

Alan Düzenleyicileri (Field Modifiers)

C# taki Alan düzenleyicileri temel olarak JAVA dilindeki gibidir. Değiştirlemeyen yada sabit bir değişken tanımlamak için JAVA daki final dan farklı olarak read only ve const belirleyicileri kullanılır. const olan alan düzenleyiciler, ilgili değerin IL kodunun bir parçası olduğu ve sadece çalışma zamanında hesaplanacağı anlamına gelir.

Kontrol Mekanizması Oluşturma

if-else, switch, while, do-while, for, break, contine deyimleri her iki dilde aynıdır. Fakat C# ta yeni bir kontrol daha vardır. C# taki bu yeni yapı koleksiyonlar arasında dolaşmak için gerekli olan for each yapısıdır.

int slist(Arraylist alist)
……….
foreach (int j in alist)
{
………….
}

Yukarıdaki yapıda j döngü değişkeni olarak adlandırılır. Bu döngü değişkenine her iterasyonda alist dizisinin int türden olan elemanı atanır.

Arayüz ve Sınıf Bildirimi

JAVA daki extends ve implements anahtar sözcükleri C# te yerini iki nokta işaretine(:) bırakmıştır.

İstisnai Durumları Ele Alma(Exception Handling)

C# ta catch bloğundaki argüman isteğe bağlıdır. Eğer catch ile argüman belirtilmemişse, bu catch bloğu try bloğunda fırlatılacak herhangi bir hatayı yakalamak için kullanılır. Bütün catch bloğuda C# ta kullanılmayabilir. Ayrıca C# ta throws(çevirenin not: throw ile karıştırmayın) anahtar sözcüğü yoktur.

Arayüzler

C# ta bir sınıf isteğe bağlı olarak açıkca bir arayüzü uygulayabilir. Açıkca uygulanan metotlar arayüz ve sınıf arasındaki tür dönüşümü sayesinde çağrılabilir.

Kalıtım

JAVA ve C# ta sadece tekli türetme mevcuttur. Eğer çoklu türetme yapmak gerekiyorsa arayüzleri kullanmak tek yoldur.

Çok Biçimlilik(Polymorphism)

Sanal metotlar çok biçimliliği gerçekleştirmek için kullanılır. Bunun anlamı taban sınıfların, türemiş sınıflara ait aşırı yüklenmiş metotları çağırabilmesidir. JAVA da bütün metotlar sanaldır, fakat C# ta türemiş bir sınıftaki metodu taban sınıf ile çağırabilmek için metodun açıkca virtual anahtar kelimesi il işaretlenmesi gerekir. C# ta override anahtar kelimesi bir metodun türeyen sınıfta yeniden yazılacağını bildirmek için gereklidir. Sanal olmayan bir metodu yeniden uygulamaya çalışmak derleme zamanı hatasına yol açacaktır. Fakat eğer türemiş sınıftaki metot new anahar sözcüğü ile işaretlenirse program hata vermeden derlenecektir.

Pekala, sonuç nedir? C# te yeni bir şey bulabildinizmi, yoksa JAVA nın üvey kardeşi gibi mi duruyor? Microsoft bu uğraşının karşılığını ilerde alacakmı? Yeni bir dilin var olan uygulamalar üzerinde, değişik platformlarda hatta programcılar üzerindeki etkisi ne olacak? Bu can alıcı soruların cevabını ancak zaman verebilir. Fakat bu arada beyninizde bu soruları yavaş yavaş çözün ve düşünün. C# gerçekten JAVA nın sonumu?

—————————-

Bu makale http://www.csharphelp.com/archives/archive86.html adresindeki yazıdan Türkçeye tercüme edilmiştir.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C# ile Zamanlayıcı Kullanmak (System.Timers)

Windows programlama ile uğraşanlar Timer kontrolünü sık sık kullanmışlardır. Bu yazıda System.Timers isim alanında bulunan Timer sınıfı ile bir programdaki rutin olarak yapılması gereken işleri belirli bir zaman aralığı içinde ne şekilde yapabileceğimizi öğreneceğiz. Ancak alışalıgelmiş şekilde bir grafik arayüzü olan program yerine uygulamamızı komut satırından çalışacak şekilde geliştireceğiz. Burdaki amacımız zamanlayıcı dediğimiz timer nesnelerinin çalışma prensibini yakından görmektir.

Şöyle bir konsol uygulaması yapmanız gerektiğini düşünün. Her 5 dakikada bir, belirli bir kaynaktaki değişiklikler kontrol edilecek. Eğer kaynakta bir değişiklik varsa kullanıcıya uyarı verilecek. Bunu klasik yöntemlerle ne şekilde yapabilirdiniz bir düşünün? Yada ben söyliyeyim. Muhtemelen sonsuz bir döngü içerisinde sistem saatini kontrol edip belirli bir aralık geçmiş ise kaynağı kontrol eden işlevi çağıracaktınız. Eğer zamansal bir işlem yapıyorsak ki bir olayı bir zaman dilimi içinde sürekli gerçekleştirmek zamana bağlı bir olaydır; mutlaka sistem saatinden faydalanmamız gerekir. Sistem saatini makine düzeyinde işleyen bir birim olarak düşünebilirsiniz. Bilgisayar bilimlerinde bir çok algoritma sistem saati üzerine kurulmuştur. Zatan eğer zamanı ölçebilecek bir birim olmasaydı şu anki teknolojik imkanlara kavuşmamız mümkün olamazdı. Sonuç olarak zaman ne kadar değerliyse zamanı ölçebilmek te o derece önemlidir.

Bu yazıda bir metodu bir zaman dilimi içerisinde tekrar tekrar çağırmayı öğreneceğiz. Bunun için System.Timers isim alanında ki Timer isimli sınıfı kullanacağız. Timer sınıfında bulunan Elapsed olayı timer nesnesinin Interval özelliği ile belirlenen aralıkta çalıştırılacak metodun referansını tutmaktadır. Metodun referansı Timer sınıfının içindeki delegate(temsilci) ve olay(event) veri yapıları ile sağlanmaktadır. (Delegate ve Event veri yapıları ile ilgili ayrıntılı yazılar ileriki zamanlarda yayınlanacaktır.)

Şimdi isterseniz Timer sınıfının yapıcı metotlarını inceleyelim.Timer sınıfının iki tane yapıcı metodu vardır. Bu metotlardan birincisi herhangi bir parametre almamaktadır. İkincisi ise double türden bir parametre almaktadır. Double türden olan bu parametre Timer nesnesinin Elapsed olayının ne kadar sürede meydana geleceğini belirtmektedir. Bu metodun prototipi aşağıdaki gibidir.

public Timer(double TickSayısı)

TickSayisi milisaniye cinsindendir. Örneğin her 1 saniyede çalışmasını istediğimiz bir metot için TickSayisi 1000 olan bir Timer kullanmamız gerekir. Saniyede on defa çalışmasını istediğimiz bir metot için ise TickSayisi 100 olan bir Timer nesnesi kullanmalıyız. Eğer yapıcı metot herhangi bir parametre ile kullanılmaz ise varsayılan TickSayisi olan 100 ile Timer nesnesi oluşturulur.

Çalışma zamanında timer nesnelerinin TickSayisini değiştirmek yada Tick sayısını öğrenmek için Interval özelliği kullanılabilir. Timer nesneleri varsayılan olarak aktif değillerdir. Bu yüzden Timer nesnelerinin işleyebilmesi için bool türden olan Enabled özelliğinin true olarak ayarlanması gerekir. Yada Timer sınıfının Start() metodunu kullanarak zamanlayıcıyı başlatmanız gerekmektedir.

Timer sınıfı ile ilgili en önemli nokta Interval ile belirtilen süre dolduğunda hangi metodu çağıracağını nerden anlayacağıdır. Bunun için temsilciler ve olaylar kullanılır. Timer sınıfı tasarlanırken yapısında tanımlanan event ve delegate veri türleri ile bu mümkün olmaktadır. Timer sınıfının Elapsed olayı bu işe yaramaktadır. Elapsed olayına += operatörü ile olay yöneticisi yardımıyla(event handler) yeni metotlar ekleyebiliriz. Timer sınıfının Elapsed olayı için ElapsedEventHandler isminde özel bir temsilci tanımlanmıştır. Bu temsilci nesnesine parametre olarak verilecek metodun parametreik yapısı aşağıdaki gibi olmalıdır.

SaniyelikIs(object o, ElapsedEventArgs a)

ElapsedEventArgs sınıfı Timer nesnesi ile ilgili ayrıntılı bilgiler tutmaktadır. Örneğin ElapsedEventArgs sınıfının SignalTime özelliği ile Elapsed olayının meydana geldiği saati ve tarihi öğrenebiliriz. Örneğimizi verdikten sonra ElapsedEventArgs sınıfını daha iyi öğreneceksiniz.

Timer sınıfının diğer bir önemli elemanı ise Stop() metodudur. Bu metodu çağırdığımız anda Timer nesnesinin Elapsed olayı artık meydana gelmez. Stop() metodu yerine Timer nesnesnin Enabled özelliğini false yapmak ta diğer bir çözümdür.

Timer sınıfı ile ilgili bu bilgileri verikten sonra bir örnek ile bilgilerimizi pekiştirelim. Örneğimiz de her saniye de saati ekrana yazadıracak bir metot bildireceğiz. Bu metot bir Timer sayesinde her saniye çağrılacaktır. Örneğin kaynak kodu aşağıdaki gibidir.

using System;
using System.Timers;

class Zamanlayici
{
   static void Main()
   {
      Timer t = new Timer(1000);
      t.Elapsed += new ElapsedEventHandler(SaniyelikIs);
      t.Start();

      while(true)
      {
      
      }
   }

   static void SaniyelikIs(object o, ElapsedEventArgs a)
   {
      Console.WriteLine(DateTime.Now);
   }
}

Timer sınıfının işlevini görebilmek için programımızın en az beş on saniye çalışmasına devam etmesi gerekir. Bunun için while(true) şeklinde sonsuz bir döngü kurduk. Eğer bu sonsuz döngü olmasaydı programın icrası bir saniyeden kısa bir sürede biteceği için Elapsed olayı meydana gelmeyecekti. Programın çalışması sırasında elde edilmiş bir ekran görüntüsü aşağıdaki gibidir.

 

Ekran çıktısından da görüldüğü üzere her saniyede ekrana yeni saat değeri yazıldığı için programımız bir saat gibi çalışmaktadır.

SaniyelikIs() metodunu ElapsedEventArgs sınıfından dolayı aşağıdaki gibi de yazabilirdik. ElapsedEventArgs sınıfının SignalTime özelliği Elapsed olayının meydana geldiği zamanı vermektedir.

static void SaniyelikIs(object o, ElapsedEventArgs a)
{
  Console.WriteLine(a.SignalTime);
}

Timer sınıfını kullanırken karşılaşabileceğimiz en büyük sorunlardan biri Elapsed olayının meydana gelme aralığının olaydan sonra çalıştırılacak kodların icrası için geçen zamandan az olmasında görülür. Bu sorun, ikinci Elapsed olayı meydana geldiğinde birinci Elapsed olayına ilişkin kodlar halen çalışıyor durumda olmasından kaynaklanır. Örneğimizdeki Timer nesnesinin Tick sayısını 1000 yerine 100 yaptığımızda aynı saat değerinin 10 defa ekrana yazdırılması gerektiğini anlarız. Çünkü Elapsed olayı her saniyede 10 defa gerçekleşecektir. Her 10 Elapsed olayı gerçekleştiğine bir saniye geçeceği için ekrana aynı saat değeri 10 defa yazmalıdır diye düşünürüz. Ama bunun gerçekte böyle olmadığını görürüz. Tick sayısını 100 yaptığımızda programın ekran çıktısı aşağıdaki gibi olmaktadır.

Ekran çıktısında bazı saat değerlerinin daha fazla sayıda bazılarının ise daha az sayıda yazıldığını görüyorsunuz. Bu demek oluyorki programın bellek durumuna ve işlemcinin yoğunluğuna göre Elapsed olayının meydana gelme sayısı değişmektedir. Bunu önlemenin yolu tick sayısının, Elapsed olayından sonra çalışacak metot yada metotların toplam icra süresinde daha fazla olacak şekilde ayarlamaktır.

Elapsed olayına birden fazla metotodu ilişkilendirebiliriz. Örneğin Elapsed olayı her saniyede bir meydana geldiğinde ekrana ayrıca “Merhaba Elapsed olayı!” yazdırmak için aşağıdaki programı yazabilirsiniz.

using System;
using System.Timers;

class Zamanlayici
{
   static void Main()
   {
      Timer t = new Timer(1000);
      t.Elapsed += new ElapsedEventHandler(SaniyelikIs);
      t.Elapsed += new ElapsedEventHandler(Selam);
      t.Start();

      while(true)
      {
      
      }
   }

   static void SaniyelikIs(object o, ElapsedEventArgs a)
   {
      Console.WriteLine(DateTime.Now);
   }

   static void Selam(object o, ElapsedEventArgs a)
   {
      Console.WriteLine(“Merhaba Elapsed olayı!”);
   }
}

Bu programda Elapsed olayı meydana geldiğinde ilk önce SaniyelikIs() metodu ardından da Selam() metodu çağrılacaktır. Bunun sebebi olayla ilişkilendirilen metotların olay yöneticisine eklenme sırasıdır. İlk eklenen metot ilk çağrılır. Bu programın ekran çıktısı ise aşağıdaki gibi olacaktır.

 

Bu yazının sonuna geldik. Size Timer sınıfının kullanımını ve dikkat etmeniz gereken bazı noktaları aktarmaya çalıştım. Yazı ile ilgili düşünceleriniz elektronik posta adresime yazabilirsiniz.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C#’ta Gösterici(Pointer) Kullanmak – II

C#’ta göstericilerin kullanımına yönelik ilk yazıda göstericilere giriş yapmıştık, C#’ta göstericilerin kullanımını 3 yazılık bir seri halinde anlatmayı düşündüm. Bu yazıda gösterici aritmetiğini ve fixed anahtar sözcüğünün kullanımını öğreneceğiz.

Göstericilerin adres bilesenlerine sabit tamsayi degerleri ekleyebiliriz, ayni sekilde göstericilerin adres bileseninden sabit bir tamsayi degerini çikarabiliriz. Ancak göstericilere uygulanan bu toplama ve çikarma islemleri biraz farklidir. Göstericilere sabit degerlerin eklenmesi yada bir degerin göstericiden çikarilmasi göstericideki tür bileseni ile yakindan ilgilidir. Bir göstericinin degerini bir artirmak göstericinin adres bilesenini, göstericinin türünün içerdigi byte sayisi kadar artirmak demektir. Ayni kural çikarma islemi içinde geçerlidir. Örnegin int türünden bir gösterici ile 1 sayisini toplamak göstericinin adres bilesenini 4 artirmak anlamina gelir. Çünkü int türü 4 byte büyüklügündedir. Ayni sekilde int türden bir göstericiden 1 sayisini çikarmak göstericinin adres bilesenini 4 eksiltmek anlamina gelir. Göstericilerle yapilan bu tür aritmetik islemlerin tamamina gösterici aritmetigi denilmektedir.

Gösterici aritmetigini daha yakindan görmek için asagidaki programi yazin ve sonucunu inceleyin.

using System;

class Gosterici
{
   unsafe static void Main()
   {
      int* ptr1 = (int*)500;
      char* ptr2 = (char*)500;
      double* ptr3 = (double*)500;
      byte* ptr4 = (byte*)500;

      ptr1 += 2;
      ptr2 += 5;
      ptr3 += 2;
      ptr4 += 6;

      Console.WriteLine((uint)ptr1);
      Console.WriteLine((uint)ptr2);
      Console.WriteLine((uint)ptr3);
      Console.WriteLine((uint)ptr4);
   }
}

Programi /unsafe argümani ile derleyip çalistirdigimizda asagidaki ekran görüntüsünüz elde ederiz. Programda Main() metodunun unsafe olarak isaretlendigine dikkat edin.

508
510
516
506

Programin çiktisindan da görüldügü üzere int türden bir göstericiye 2 sayisini eklemek göstericinin adres bilesenini 2*4=8 kadar artirmistir. Ayni sekilde char türden bir göstericiye 5 degerini eklemek göstericinin adres bilesenini 5*2 =10 kadar artirmistir. Toplama yerine çikarma islemi yapilmis olsaydi bu sefer ayni oranda adres bileseni eksiltimis olacakti.

Göstericiler üzerinde sadece tamsayilarla aritmetik islemler yapilabilir. Göstericiler ile asagidaki aritmetik operatörleri kullanabiliriz.

+ , – , — , ++ , -=, +=

void göstericilerde herhangi bir tür bilgisi olmadigi için bu tür göstericiler üzerinde aritmetik islemler yapilamaz. Çünkü void türünden bir göstericiye örnegin 1 eklemek istedigimizde göstericinin adres bileseninin kaç byte ötelenecegi belli degildir.

Göstericiler üzerinde yapilabilecek diger önemli islemde iki göstericinin birbirinden çikarilmasidir. Gösterici türleri ayni olmak sartiyla iki göstericiyi birbirinden çikarabiliriz. Ancak iki göstericinin çikarilmasi sonucunda üretilen deger bir gösterici türü degildir. Iki gösterici arasindaki fark, adres bilesenlerinin sayisal farkinin gösterici türlerinin büyüklügünden kaç adet byte miktari edecegidir. Diger bir deyisle adres bilesenlerinin sayisal farki alinip gösterici türünün byte miktarina göre bir deger belirlenir. Iki göstericinin farki long türden bir deger üretir. Iki göstericinin farkina örnek verecek olursak, int türden 5008 adres ile int türden 5000 adresinin farki (5008-5000) % sizeof(int) tir. Yani sonuç long türden 2 dir. Asagidaki programi yazarak sonucu görebilirsiniz.

using System;

class Gosterici
{
   unsafe static void Main()
   {
      int* ptr1 = (int*)500;
      int* ptr2 = (int*)508;

      long fark=ptr2 – ptr1;

      Console.WriteLine(fark);
   }
}

Diger bir ilginç nokta iki göstericinin adres bilesenlerinin farki gösterici türlerinin büyüklügünün tam kati olmadiginda görülür. Örnegin ptr2 göstericisini tanimlanmasini

int * ptr2 = (int*)507;

seklinde degistirdiginizde bu sefer ekrana 1 yazdigini görürsünüz. Burdan çikarmamiz gereken sonuç iki göstericinin farki adres bilesenlerinin sayisal farkinin olmamasidir.

Dikkat: Iki göstericinin farki long türden bir deger üretir. Bu yüzden iki göstericinin farki açikca bir tür dönüsümü yapilmadikça long türünden küçük türden olan degiskenlere atanamaz.

Göstericiler ile kullanilabilecek diger operatörler ise ==, < ve > gibi karsilastirma operatörleridir. Bu operatörler iki göstericinin adres bilesenini karsilastirip ture yada false degeri üretirler. Karsilastirma operatörleri göstericiler için çok istisnai durumlar disinda anlamli degildir. Bu istisna durumlardan biri göstericileri kullanarak dizi islemleri yaptigimizda görülür.

fixed Anahtar Sözcügü

Bildiginiz gibi C#’ ta tanimladigimiz referans degiskenleri heap bellek bölgesindeki adresler temsil ederler. Ancak biz adresler yerine nesnenin ismini kullaniriz. Gereksiz bilgi toplayicis(garbage collector) bellek optimizasyonui açisindan heap bellek bölgesindeki nesnelerin yerlerini her an degistirebilir. Bu yer degisiminden bizim haberimiz olmaz, çünkü nesnenin yeri degistigi anda bu nesneye referans olan stack bellek bölgesindeki degiskenin adres bileseni de degistirilir. Dolayisiyla biz ayni referans ile farkli bellek bölgesini istegimiz disinda kullanmis oluruz. Ancak bazi durumlarda gereksiz nesne toplayicisina bir nesnenin adresini degistirmemesi için ikna etmek durumunda kaliriz. Bu, özellikle sinif nesnelerinin üye elemanlarindan birinin adresi ile islem yapmamiz gerektigi durumlarda karsimiza çikar. Bir degiskenin adresinin belirlenen bir faaliyet alani boyuncu degismeden kalmasi için bunu gereksiz nesne toplayicisina bildirmemiz gerekir. Bunun için fixed anahtar sözcügü kullanilir.
Zaten fixed anahtar sözcügünü kullanmadan referans türünden nesnelerin üye elemanlarinin adreslerini elde etmemiz mümkün degildir. Üye elemanlarinin adreslerini elde edemedigimiz bu tür nesnelere managed type(yönetilen tip) denilmektedir. Buna göre siniflar managed type kapsamina girmektedir.

Asagidaki programda ManagedType isimli sinifin int türden olan x elemaninin adresi bir göstericiye atanmak isteniyor.

using System;

class ManagedType
{
   public int x;
   public ManagedType(int x)
   {
   this.x = x;
   }
}

class Gosterici
{
   unsafe static void Main()
   {
      ManagedType mt = new ManagedType(5);

      int* ptr1 = &(mt.x);
   }
}

ManagedType sinifinin x elemani deger tipi olmasina ragmen mt nesnesi üzerinden x degiskeninin adresi elde edilememektedir. Çünkü x degiskeninin adresi gereksiz nesne toplayicisi tarafindan her an degistirilebilir. Eger yukaridaki kod geçerli olmus olsaydi x degiskeninin adresi degistigi anda ptr1 göstericisi nereye ait oldugu bilinmeyen bir adres bilgisi tasiyor olacakti. x degiskeninin bir blok içerisinde sabit adreste olmasini istiyorsak asagidaki gibi fixed anahtar sözcügünü kullanmaliyiz.

using System;

class ManagedType
{
   public int x;
   public ManagedType(int x)
   {
   this.x = x;
   }
}

class Gosterici
{
   unsafe static void Main()
   {
      ManagedType mt = new ManagedType(5);

      fixed(int* ptr1 = &(mt.x))
      {
            //x’in adresi bu blokta asla degismez.
      }
   }
}

Yukaridaki fixed ile isaretlenmis blokta x’in adresinin degismeyecegi garanti altina alinmistir. Birden fazla degiskeni fixed olarak isaretlemek için asagidaki gibi bir kullanim geçerli kilinmistir.

ManagedType mt1 = new ManagedType(5);
ManagedType mt2 = new ManagedType(5);

fixed(int* ptr1 = &(mt1.x))
fixed(int* ptr2 = &(mt2.x))
{
    //x’in adresi bu blokta asla degismez.
}

Öte yandan bir fixed bildirimi içinde adreslerinin degismesini istemedigimiz elemanlari virgül ile ayirarak asagidaki gibi de bildirebiliriz.

ManagedType mt1 = new ManagedType(5);
ManagedType mt2 = new ManagedType(5);

fixed(int* ptr1 = &(mt2.x), ptr2 = &(mt2.x))
{
   //x’in adresi bu blokta asla degismez.
}

Göstericilerle ilgili son yazıda, yapı göstericileri, göstericiler ile dizi işlemleri ve stackalloc ile dinamik alan tahsisatı yapma gibi konuları inceleyeceğiz.

tuna

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir