1 Ocak 2018 Pazartesi

C#'ın Tarihçesi, .NET Framework ve Common Language Runtime (CLS)


Merhaba, bu yazıda C, C++ ve C#'ın kısa bir tarihçesinden bahsedeceğim. Ardından .NET Framework ve derleme zamanında neler yaşandığından, Common Language Runtime (CLS)'in görevinden kısaca bahsedeceğim.

C#'a geçmeden önce günümüzün nesneye yönelik programlama dilleri C# ve Java'nın ortaya çıkmasında büyük rolü olan C ve C++ dillerinin tarihçesinden de kısaca bahsetmek istiyorum.

C Dili: Modern Programlama Çağının Başlangıcı

Daha önceleri Pascal Programlama Dili kayda değer bir başarı elde etmiş olsa da günümüzün programlama dillerinin şekillenmesinde ana model C Programlama Dili olmuştur.  

C dili, 1960'ların yapısal programlama (structured programming) devrimi sonucu ortaya çıkmıştır. 1970 yılında ABD'li ünlü bilgisayar mühendisi Dennis Ritchie (1941 - 2011) tarafından icat edilmiştir.

Yapısal programlama dillerinden önce program yazmak oldukça zordu. Kodlar "spagetti kod" denilen atlamalar (go to ifadesini çoğu programcı hatırlar), çağrılar ve döngülerden oluşan, takibi zor program yapılarından ibaretti. Yapısal programlama ise bu karışıklıklardan (spagetti kod vb) kurtulmak için ortaya atıldı. C ile beraber birçok yapısal dil mevcuttu fakat C'nin estetik, esnek, sade ve performanslı olması bu dillerin önüne geçti ve böylece 1980'lerin en yaygın kullanılan yapısal dili oldu. 

C dilinin bütün esnekliğine rağmen kısıtlayıcı yönleri de mevcuttu. C'nin en büyük problemlerinden birisi de programlar büyüdükçe kontrol edilmesi zor bir hal almasıydı. Programlar belli bir büyüklükten sonra esneklikten çıkıp, karmaşık ve yönetilemez bir hal alıyordu. Her ne kadar iyi programlanıyor olursa olsun C dili, belli bir büyüklüğe ulaştıktan sonra mutlaka bu engele çarpacaktı. İşte bu noktada C++ dili imdada yetişmiştir.

C++'ın Doğuşu ve Nesneye Yönelik Programlama

Yukarda bahsettiğimiz program büyüklüğü ve karmaşıklığı sonrasında çözüm olarak Object Oriented Programming (OOP) denilen programlama yapısı ihtiyacı ortaya çıktı. 

C++ ilk olarak 1979 yılında Bjarne Stroustrup tarafından ortaya kondu. C++, C'nin üzerine inşa edilerek nesne yönelimli özelliği eklenmiştir. Dolayısıyla C++, C'nin bütününü içermektedir. Bahsettiğimiz karmaşıklığı önlemek ve nesneye yönelik programlama dili olması itibariyle 10 yıllık bir süreçten sonra 1990 yılında C++ en yaygın kullanılan dil haline gelmiştir. Günümüzde bile hala bazı yüksek performas gerektiren konularda C++ kullanılmaktadır. 

C++ tamamiyle yeni bir dil ortaya atmak için çıkarılmasa da C# ve Java gibi dillerin ortaya çıkmasında büyük bir atılım olmuştur. 

C#'ın Doğuşu

Bilindiği üzere Java, platform bağımsızlık ilkesine dayanarak C ve C++ dillerinden türemiştir. Java her ne kadar platform bağımsız bir dil olsa da bazı özellikleri açısından yetersiz kaldığı durumlar olmuştur. Bunlardan bir tanesi; karışık dillerle birlikte çalışabilme becerisidir. Bir diğeri de, Windows platformları entegrasyonudur. Her ne kadar JVM yüklü bir makinede Java sorunsuz çalışabiliyor olsa da Java Windows'a tam hakim değildir. Dünyada en çok kullanılan işletim sistemi olan Windows için doğrudan destekten yoksun olması Java'nın bir dezavantajı olarak karşımıza çıkmaktadır. 

Yukarda bahsettiğim nedenlerden dolayı Microsoft, kendi yağında kavrulmak istedi ve C#'ı geliştirdi. 1990'ların başında .NET temelleriyle birlikte C# şekillenmiş olsa da, Microsoft ilk kez 2000'li yılların ortalarında C# alfa versiyonunu piyasaya sürmüştür. İlk sürümünden sonra çok hızlı gelişti ve Microsoft'un vazgeçilmez programlama dili haline geldi. Çıktığı ilk günden günümüze kadar her dönemin ihtiyaçlarına yönelik sayısız çözümler ve tasarımlar sundu. Bu sayede hiçbir zaman popülaritesi düşmedi.

C#'ın baş mimarı Anders Hejlsberg'tir, Bill Gates değildir :) Genelde Microsoft'un bütün araçlarının yazarı Bill Gates olduğu kanısı vardır ya onun için ayrıca buna dikkat çekmek istedim. Anders Hejlsberg aynı zamanda Turbo Pascal'ın da mimarıdır. 

C# öğrenilmesi kolay, sade ve çok esnek bir dildir. Aynı zamanda .NET'in gözbebeği ve çok şık bir dildir. Oldukça güçlüdür. OOP'un bütün güçlü nimetlerini kapsamlı bir şekilde ve eksiksiz olarak kullanır. .NET'in en önemli silahıdır. Microsoft, en büyük yatırımı C# için yapmıştır. 

C# ve Java'nın ataları C ve C++'tır. C# ve Java'nın temelleri bu iki dil üzerine kurulmuş olduğundan C# ve Java birer kuzen gibidir. Bu nedenle ortak yönleri ve benzerlikleri oldukça fazladır. Bu vesileyle C#'ı OOP yönüyle öğrenen birisi rahatlıkla Java'yı da öğrenebilir.

C#'ın Gelişimi ve Sürümleri

Kısaca bahsetmek gerekirse;

C# 1.0 ilk sürümdür. 1.0'dan kısa bir süre sonra 1.1 sürümünü yayınlamıştır. Bu iki sürüm arasında küçük farklılıklar bulunmaktadır fakat 2.0 için durum tamamen farklıdır. 

C# 2.0 önemli yenilikler eklemiştir. Bunlardan bazıları: jenerikler, anonim metotlar ve kısmi sınıf tanımlamaları (partial class) 

C# 3.0 da büyük yenilikleri getirdi. Bunlardan bazıları: Lambda deyimleri, LINQ (Language Integrated Query), extension metotları ve belirtilmemiş tipte değişkenlerdir. (Bu kavramlara ayrıca başka yazılarda değineceğim)

C# 4.0 ile gelen en önemli özelliklerden biri de dinamik tiplerdir (daha sonra değineceğim). Bir diğer önemli özellik de Task Parallel Library (TPL) ve Parallel LINQ (PLINQ) şeklinde özetlenebilir.

.NET Framework Nedir?

.NET Framework farklı dillerin (örneğin; C#, VB.NET gibi) birlikte çalışmasına olanak tanır ve Windows platformu için program taşınılabilirliği sağlar. Örneğin; C# ve VB.NET ile ayrı ayrı yazılmış uygulamaları uygun .NET Framework sürümü kurulu bir makinede rahatlıkla çalıştırabilirsiniz. .NET Framework'un sürümleri arasında bir hiyerarşi söz konusudur. En tepedeki sürüm bütün sürümleri çalıştırabilmektedir. .NET Framework, Windows kurulumu ile birlikte gelmekte, ayrıca sonradan da yüklenebilmekte veya güncellenebilmektedir. .NET Framework'u (Java Virtual Machine) JVM'ye benzetebiliriz. 

Common Language Runtime (CLR) Nedir?

Common Language Runtime (Ortak Dil Çalışma Zamanı) .NET kodunun çalışmasını kontrol eder. CLR'nin çalışma yapısı şu şekildedir: Bir C# kodu derlendiğinde, elde edilen çıktı, direk çalıştırılabilir kod olmayıp MSIL (Microsoft Intermediate Language) denilen bir çıktı üretir. MSIL bir sözde kodtan (pseudo code) ibarettir. Bu kod, CPU'dan bağımsız bir Assembly dili demektir.  CLR ise bu elde edilen çıktının çalıştırılabilmesini sağlar. Bu şu demek oluyor: MSIL çıktısı elde edilecek şekilde derlenen her program CLR'ın uygulandığı her ortamda çalışabilir demektir. Bu da .NET Framework'un taşınılabilirliğini sağlamaktadır. 

Bir sonraki makalede görüşünceye dek hoşçakalın...



Paylaş:

22 Aralık 2017 Cuma

Visual Studio "Create Unit Tests" Menüsü


Visual Studio'da Unit Test yapmak istiyorsunuz fakat "Create Unit Tests..." menüsü gelmiyorsa ya da disable geliyorsa bunun çözümü için doğru yerdesiniz.

Eğer kodunuza sağ tıkladığınızda "Create Unit Tests" gelmiyorsa öncelikle bunun gelmesini sağlamalıyız.

1) Visual Studio > TOOLS > Customize

2) Açılan Customize ekranında Commands sekmesine gelin. Context menu radio butonunu tıklayın. Combodan "Editor Context Menus | Code Window" seçiniz. Gelen seçeneklerden "Create Unit Tests..." menüsünü bulun ve seçin. Sonra Close deyin.



Bu durumda kodunuzun her hangi bir yerinde sağ tıkladığınızda "Create Unit Tests..." menüsü gelecektir ama henüz test projeniz olmadığından aşağıdaki gibi disabled durumda gelebilir.


Yukardaki gibi disabled durumda geliyorsa test projesi olmadığındandır. Bu proje için bir test projesi oluşturmanız gerekmektedir.

1) FILE > Add > New Project deyip çıkan ekranda Installed > Visual C# > Test menüsünden yeni bir test projesi ekliyoruz.



2) Eklenen test projesine sağ tıklayıp Add > Unit Test diyoruz. 


Unit test yapmak istediğiniz kod sayfasına gelip sağ tıklayınca artık "Create Unit Tests..." menüsü aşağıdaki gibi enable olacaktır.



Şimdilik bu kısmını anlattık. Unit Test nedir nasıl yapılır konusunu ise başka bir yazıda ele alacağız.

Herkese iyi kodlamalar...


Paylaş:

28 Kasım 2017 Salı

Javascript'teki "===" Operatörü


Merhaba,

Javascript'te bazen şu "===" operatörü görmüşsünüzdür. Bu da ne ola ki diye merak ediyorsanız cevabı burada :)

Bildiğiniz üzere javascript esnek bir dile sahiptir. C# dili gibi katı tip denetimli bir dil değildir:

Lafı uzatmadan, aşağıdaki örneği inceleyelim. var x = 1; var y = "1"; if(x == y){ alert("Değişkenler aynı"); }else{ alert("Değişkenler aynı değil"); }

Yukardaki kod, "Değişkenler aynı" uyarısını verecektir.

Fakat bir adım daha ileriye gidip hem değişken değerleri hem de tip kontrolü yapmak istersek "===" operatörünü kullanmalıyız.


var x = 1; var y = "1"; if(x === y){ alert("Değişkenler aynı"); }else{ alert("Değişkenler aynı değil"); }

Bu sefer bu kod "Değişkenler aynı değil" uyarısını verecektir. Çünkü biri sayı değerinde (1) ötekisi de string ("1") değerindedir. Bir nevi C#'taki tip denetimi gibi.

Kodu şöyle  düzeltirsek,

var x = 1; var y = 1; if(x === y){ alert("Değişkenler aynı"); }else{ alert("Değişkenler aynı değil"); }

Bu sefer çıktı "Değişkenler aynı" olacaktır.

Benzer şekilde "!==" operatörü de tersini kontrol etmek amacıyla kullanılabilir. Değişken değeriyle birlikte tip kontrolü farklı olması kontrolü sağlanabilir.

Hepinize iyi kodlamalar...


Paylaş:

25 Kasım 2017 Cumartesi

C# interface Kavramı (Arabirimler)


Merhaba,

Size bu yazıda C#'ta önemli bir konu olan interface yapısından bahsedeceğim. Türkçe'deki diğer adı arabirimler. Bu yazıda interface terimini kullanacağım.

interface nedir?

interface, işlevselliği olmayan ama işlevselliği tarif eden, bir işin nasıl yapılacağını değil de ne yapılacağını tarif eden mantıksal bir özelliktir. Türkçe adından da anlaşılacağı üzere bir arabirimdir. abstract classların bir adım ileriye götürülmüş halidir. abstract classları hatırlayalım (abstract classların detayı için tıklayın) bazı metotların  gövdesi olmakla birlikte abstract metotların gövdesi yoktu. kalıtım yoluyla aktarıldığı sınıflarda uygulanmak zorundaydı. interface'de ise bütün metotlar gövdesizdir. Yani bir interfacei kalıtım yoluyla devralan bir sınıf interfacein bütün metotlarını uygulamak zorundadır. Bu, hem kod garantisi sağlar hem de hataları minimize eder. Hem de büyük projelerde bir prototip ortaya koymamızı sağlar. 

Bazen bir uygulamanın ana şablonlarını ortaya koymak istersiniz. Neler yapılacağını belirlersiniz fakat detaylarına girmezsiniz. Detayları size bağlı çalışanlarınız yapacaktır. İşte bu tam olarak interface tanımına uygundur. Alın size gerçek hayattan interfacelere örnek.. Diyelim ki siz bir işletmenin patronusunuz ve kakaolu kek yapacaksınız. Kakaolu kek için ne lazım? Kakao, un, şeker.. Bu üç malzeme olmazsa olmazdır. O zaman projenizde ne olmalı? KakaoGetir(), UnGetir(), SekerGetir()... Siz patsonsunuz ya, tabiki de siz markete gidip bunları temin etmeyeceksiniz. Hepsi de farklı marketlerde bulunabilir. Nihayetinde bu görevleri siz değil, size bağlı kişiler yapacaktır. Ahmet! Sen kakao getir. Mehmet! Sen de un getir. Veli e oğlum sen de şeker getir. Ahmet der ki: patron kakaoyu nerden alayım nasıl getireyim? Patron der ki ben karışmam getir ama nerden getirirsen getir, ister arabayla git ister yürüyerek ama istersen şu yandaki X marketinin ürünleri güzeldir ordan alabilirsin ama yeter ki bana getir der. Ahmet de allem eder kallem eder gider kakaoyu getirir. X marketinde bulamayıp Y marketine gider getirir ama getirir. Mehmet de yürüyerek yandaki bakkaldan un alır gelir. Veli de motora atlayıp kestirme yolunu en iyi bildiği marketten şeker alır gelir. Sonuç olarak kakao, un ve şeker, kekin olmazsa olmazıdır bu üç malzeme tamamlanmazsa kek ortaya çıkmayacaktır. Ahmet, Mehmet ve Veli üçü de görevini layıkıyla yerine getirip kekin yapılmasında büyük katkıları olmuştur.

Gelelim, kod olarak interfacelerin tanımına.. Genel olarak interface tanımı şu şekilde yapılır

interface ad {

          tip1 metot1 (parametre listesi);
          tip2 metot2 (parametre listesi);
          .
          .
          .
}

Dikkat edin, metotların gövdesi yoktur. Sadece dönüş tipi, metot adı ve metot parametrelerine sahiptir. Asıl iş, bu interfacei devralacak classlarda yapılacaktır. metotlar kapalı olarak publictir. Mantıklı olarak düşünüldüğünde, bir interfacei devralacak herhangi bir class içindeki tüm metotları uygulamak zorunda olduğuna göre bütün metotlara erişmek zorundadır. Dolayısıyla metotların neden kapalı olarak public olduğu anlaşılabilir.

interfaceler veri üyeleri içermezler, yapılandırıcı ve yok edicilere de (constructor, destructor) sahip olamazlar, ayrıca hiçbir üyesi static olamaz. Çünkü interfaceler classlar gibi nesne üretmezler sadece arabirimlerdir. interfaceleri uygulayan classların uygulayacağı interface'e ait metotlar da public olmak zorundadır. Aynı zamanda interfacei uygulayan metotların dönüş tipi, parametreleri birebir aynı olmak zorundadır.

interfaceler tanımlanırken, başına "I"  harfi konulması kodun okunulabilirliğini oldukça arttırır. Bu bir zorunluluk olmamakla birlikte, son derece yaygın bir kullanımdır. Böylece kalıtım aldığımız yapının bir interface olduğunu kolayca anlarız.

Aşağıdaki örneği inceleyelim:

interface ISayiSerileri {
        int SonrakiSayi();
        void Sifirla();
    }

    class CiftSayilar : ISayiSerileri {
                
        int val = -2;

        public int SonrakiSayi() {
            val += 2;
            return val;
        }

        public void Sifirla() {
            val = -2;
            Console.WriteLine("Sayaç sıfırlandı...");
        }

        public void CiftSayilariGetir(int adet) {
            StringBuilder sb = new StringBuilder ();
            for (int i = 0; i <= adet; i++)
            {
                sb.Append(SonrakiSayi() + ", ");
            }
            Console.WriteLine(adet + " adet çift sayı serisi" );
            Console.WriteLine(sb.ToString());
        }
    }

static void Main(string[] args)
        {
            CiftSayilar cs = new CiftSayilar();
            cs.CiftSayilariGetir(5);
            cs.Sifirla();
            cs.CiftSayilariGetir(7);
            Console.ReadLine();
        }

Bu programın çıktısı aşağıdaki gibi olacaktır:

5 adet çift sayı serisi
0, 2, 4, 6, 8, 10,
Sayaç sıfırlandı...
7 adet çift sayı serisi
0, 2, 4, 6, 8, 10, 12, 14,

Bu programı biraz daha inceleyelim: 

ISayiSerileri diye bir interface tanımladık. Bu interface iki tane metotda sahip:

int SonrakiSayi();
void Sifirla();

ISayiSerileri'deki tanımlara dikkat ediniz.. Herhangi bir erişim belirteci yok. Çünkü kapalı olarak publictir. Fakat bunu uygulayan CiftSayilar classındaki bu metotların tanımı publictir. public olmak zorundadır. CiftSayilar classında bu iki metottan birini çıkardığınızda derleme hatası alacaksınız. Uygulayıcı sınıflar interfacelerin bütün metotlarını uygulamak zorunda olmakla birlikte, ayrıca kendi üyelerine ve kendi metotlarına (CiftSayilariGetir(int adet)) sahip olabilirler.


Not: Daha önce kalıtım derslerinde yazdığım gibi, bir class yalnızca bir tane classı kalıtım yoluyla devralabiliyorken bir class birçok interfacei devralabilir. Bir tane class olacak şekilde, hem class hem de birden çok interfacei aynı anda uygulayabilir.

class A{....}
interface I {....}
interface IB {....}

class B : A, IA, IB {.....} 

Yukardaki B classı derlenecektir. Aynı anda hem bir tane class hem de birden çok interfacei uygulamak mümkündür.

Bununla birlikte interfaceler arasında da bir kalıtım hiyerarşisi oluşturmak da mümkündür.

Aşağıdaki örneği inceleyelim:

interface IA {
        void MethodA();
    }

    interface IB : IA {
        //Burada void MethodA()'yı tanımlamak gerekmez.
        void MethodB();
    }

    class A : IB {
        public void MethodA() { } //IA'dan gelen
        public void MethodB() { } //IB'den gelen
    }

Yukardaki örnekte, A sınıfı IB interfaceinden türemiştir, IB de IA interfaceinden türemişti. Dolayısıyla A sınıfı IA'dan gelen MethodA() ve IB'den gelen MethodB()'yi uygulamak zorundadır.

interfaceler sadece metot tanımlamaz. Aynı zamanda bir properties de tanımlayabilir. Fakat tahmin edileceği üzere get ve set belirteçleri belli bir değer gösteremez.

interfacede bir properties aşağıdaki gibi tanımlanmalıdır:

interface IA {
        int MyProperties { get; set; }
    }



Açık Uygulamalar

Bazen bir interface i uygularken bazı üyelerin private kalmasını isteyebiliriz. Bu durumda interface tanımındaki üye adını interface adı ile birlikte tanımlayabiliriz. Üyenin bu şekilde açık olarak uygulanmasına neden olur (explicit interface member implementation). Bu tanıma bir örnek aşağıdaki gibidir:

interface IA {
        int MethodA(int i);
    }

 class A : IA {
        int IA.MethodA(int i) { //Bu kısmın private olabileceğine dikkat edin. public de olabilir
            return i / 2;
        }
    }

Daha ilginç bir örnek aşağıdaki gibidir.

  interface IEven {
        bool IsOdd(int x); //Tek mi?
        bool IsEven(int x); //Çift mi?
    }

    class MyClass : IEven {

        bool IEven.IsOdd(int x) { //private olduğuna ve tanım şekline dikkat ediniz (interface adı ile birlikte)
            if ((x % 2) != 0) return true;
            return false;
        }

        public bool IsEven(int x) { //Burası normal tanımlama
            IEven e = this;
            return !e.IsOdd(x);
        }
    }

static void Main(string[] args)
        {
            MyClass ob = new MyClass();

            bool res;
            res = ob.IsEven(4);
            if (res) Console.WriteLine("4 çift sayıdır");

            //res = ob.IsOdd(3); //Hata!! IsOdd() private. erişilemez!

            //Yalnız aşağıdaki kod doğrudur ve derlenecektir. IsOdd() metoduna IEven türünden bir referans ile erişilebilir.
            IEven ieven = (IEven)ob;
            res = ieven.IsOdd(3);
            if (res) Console.WriteLine("3 tek sayıdır");

        }

Bu programın çıktısı tahmin edileceği gibi aşağıdaki gibidir:

4 çift sayıdır
3 tek sayıdır

Yukarda gerekli açıklamaları yaptığım için ayrıca açıklama yapmaya gerek görmüyorum şimdilik.

interfacelerin referans olarak kullanımı, bağımlılıkların kaldırılması kapsamında yaygın bir kullanımı vardır. (IoC, Dependency Injection.. gibi) Fakat bunu daha sonra ele alacağız.

interface konusunu şimdilik burda noktalıyoruz.

Bir sonraki makalede buluşuncaya dek hoşçakalın, sağlıcaklakalın...




Paylaş:

20 Kasım 2017 Pazartesi

C#'ta Kalıtım (Inheritance) - Kalıtımda Sanal Metotlar, Overriding ve Bazı Hususlar - Part 4

Merhaba,

Bir önceki yazıda Kalıtımda Yapılandırıcılar ve Ad Gizlemeden bahsetmiştik. Bu yazıda ise kalıtımda metotlara daha yakından bir bakış atacağız.

Bir sınıf, başka bir sınıfın üyelerini miras alabildiği gibi public metotları da miras alabilir. Fakat bazı durumlarda bu metotların türetilmiş sınıfa ait specific özellikler yerine getirmesi istenebilir. Bu durumda metot adını değiştirip yeni bir metot yazmak mantıklı gelse de, OOP açısından zayıf bir yöntem olacaktır. Bunun yerine temel sınıftan alınan bir metodu özelleştirmek için temel sınıfta sanal metot (virtual) tanımlayıp türetilmiş sınıfta bu metodu devre dışı bırakmak (overriding) gerekir. Bu durumda artık temel sınıftan kalıtım ile gelen metot devre dışı kalacak olup türetilmiş sınıftaki metot geçerli olacaktır. OOP'un çok biçimlilik (polymorphism) özelliği bunu gerektirir. OOP'un nimetlerinden mümkün olduğunca yararlanmak, boyutu büyüyen projeler için daha sağlıklı olacaktır.

Aşağıdaki örneği inceleyelim:

class A
    {
        public virtual void MyVirtualMethod() {
            Console.WriteLine("Bu temel sınıfın sanal metodu");
        }
    }
    
    class B : A {
        public override void MyVirtualMethod()
        {
            Console.WriteLine("Bu da B'nin override ettiği metod");
        }
    }
static void Main(string[] args)
        {
            A a = new A();
            a.MyVirtualMethod();
            B b = new B();
            b.MyVirtualMethod();
}

Bu programın çıktısı aşağıdaki gibi olur:

Bu temel sınıfın sanal metodu
Bu da B'nin override ettiği metod

Öncelikle; A'da bir sanal metot (public virtual) tanımlandığına dikkat edin. Bu metot A sınıfında belli bir işi yerine getirmekte. Fakat B, ekstra işler daha yapmak istemektedir. Bu durumda A'nın metodunu devre dışı bırakıp (public override) kendi işini yapabilmektedir. Eğer; B sınıfı A sınıfındaki metodun da yaptığı işleri yapmak isterse ama ayrıca kendi işini de ilave etmek isterse yine A'daki metodu base anahtar sözcüğü ile çağırabilir. Yukarda B'nin içindeki metot aşağıdaki gibi olabilir:

 class B : A
    {
        public override void MyVirtualMethod()
        {
            base.MyVirtualMethod();
            Console.WriteLine("Bu da B'nin override ettiği metod");
        }
    }

Bu durumda, A'nın metodunun yaptığı işlere ilaveten kendi işlerini de yapabilecektir.

Sanal metotların devre dışı bırakılma gibi bir zorunluluğu yoktur. Devre dışı bırakılmazsa temel sınıftan olduğu gibi devralınabilir.

Sanal metotlar çok yerde işe yarar. Bunun için güzel örneklerden biri de; iki boyutlu şekil sınıfından türeyen üçgen, kare ve dikdörtgen sınıflarının kendi AlanHesapla() metotlarının olabilmesidir. Her geometrik şeklin alan hesaplaması farklı olacağından her bir alt sınıf bu metodu override ederek uygulayacaktır.

Ufak bir not: sanal metotlar public olmak zorundadır. private, abstract ya da static olamazlar. 


Kalıtıma Dair Diğer Hususlar

Daha önce abstract classlar ve metotlardan bu linkte bahsetmiştik. Detaylar için bu linke bakabilirsiniz. Kısaca bahsetmek gerekirse; abstract classlar mutlaka kalıtım yoluyla aktarılabilen özet classlardır. abstract classlar bir nevi yarım classlardır tam bir iş tanımlamamaktadır. Dolayısıyla new anahtar sözcüğüyle oluşturulamazlar. Bir işi yapabilen metotlara sahip olabilmekle birlikte, abstract metotlara da sahip olabilmektedir. Abstract metotlar, abstract classları devralan sınıflarda mutlaka uygulanmak zorundadır.

abstract classlar mutlaka kalıtım ile kullanılabilmelerine karşın, kalıtımı önleyen sealed yapısı da mevcuttur. Bir sınıf neden kalıtım ile aktarılamasın ki buna ne gerek var diye düşünebilirsiniz. Bazı özel durumlarda bu işe yarar. Örneğin; tıbbi, elektronik vb gibi bir cihazınızın ilk kullanıma hazırlanmasını ayarlayan bir sınıfınız olsun. Böylece bunu kullanan kullanıcıların bu ilk hazırlanma şeklini değiştirebilmelerini istemeyebilirsiniz. Bu durumda sealed yapısını kullanmak işinizi görecektir.

sealed class A {........}

class B : A {......} //Hata!!! A sınıfından sınıf türetemezsiniz.


Bu yazıda kalıtım konusunu noktalıyorum.

Faydalı olabildiysem ne mutlu bana.. Anlaşılmayan bir şey olursa yorum kısmına yazabilirsiniz. Dilim döndüğünce anlatmaya çalışırım.

Bir sonraki yazıda buluşuncaya dek sağlıcaklakalın...
Paylaş:

19 Kasım 2017 Pazar

C#'ta Kalıtım (Inheritance) - Kalıtımda Yapılandırıcılar ve Ad Gizleme - Part 3

Merhaba,

Bir önceki yazıda kalıtımda üye erişimi konusunu ele almıştık. Bu yazıda ise kalıtımda yapılandırıcılardan ve ardından ad gizleme kavramından bahsedeceğim.

Bildiğiniz üzere her sınıfın bir yapılandırıcısı vardır. Siz tanımlamasanız bile default bir yapılandırıcı vardır. Bir kalıtım hiyerarşisinde hem temel sınıf hem de türetilmiş sınıfların kendi yapılandırıcılarını çağırmaları mümkündür. Peki bu durumda türetilmiş sınıf hangi yapılandırıcıyı çağıracak gibi bir soru akla gelebilir ki gelmelidir. Cevap: her iki sınıf da kendi yapılandırısını çağırır. Bu akla yatkındır çünkü temel sınıf, türetilmiş sınıfın ne yaptığından habersizdir ve kendi işini kendisi görecektir.

Aşağıdaki örnek bunu ifade eder:

 class A
    {
        public A() {
            Console.WriteLine("Bu temel sınıfın yapılandırıcısı");
        }
    }
    class B : A {
        public B() {
            Console.WriteLine("Bu da türetilmiş sınıfın yapılandırıcısı");
        }
    }
static void Main(string[] args)
        {
            B b = new B();
        }

Bu programın çıktısı, tahmin edeceğiniz üzere aşağıdaki gibi olur (çıktı sırasına dikkat ediniz. önce temel sınıfın yapılandırıcısı çağrılmakta):

Bu temel sınıfın yapılandırıcısı
Bu da türetilmiş sınıfın yapılandırıcısı

Peki kalıtım hiyerarşisi daha çok seviyeli olduğu zaman ne olur? Örneğin; class C : B {} olursa? Bu durumda da yine en tepedeki hiyerarşiden başlayarak aşağı doğru yapılandırıcılar çağrılır.

Yukardaki örneğe ekleme yapalım.

 class A{
        public A() {
            Console.WriteLine("A'nın yapılandırıcısı");
        }
    }

    class B : A {
        public B() {
            Console.WriteLine("B'nin yapılandırıcısı");
        }
    }

    class C : B {
        public C() {
            Console.WriteLine("C'nin yapılandırıcısı");
        }
    }

static void Main(string[] args){
            C c = new C();
}

Tahmin edeceğiniz üzere çıktı aşağıdaki gibi olur:

A'nın yapılandırıcısı
B'nin yapılandırıcısı
C'nin yapılandırıcısı


Peki daha karmaşık yapılandırcılarımızın olduğunu varsayalım. Bu durumda türetilmiş sınıf, temel sınıfın yapılandırıcısını çağırabilir. Zaten OOP'un kurallarından biri de mümkün mertebe bir kodu bir yerde yazmak ve defalarca çağırabilmek, dolayısıyla hataları mimimuma indirgemektir. Türetilmiş sınıf, temel sınıfın yapılandırıcısını çağırırsa temel sınıfın yaptığı ekstra işlerden kurtulmuş olur. Ancak türetilmiş sınıf kendine özgü işleri yerine getirecekse, bunu ayrıca deklare etmelidir. Peki temel sınıfın yapılandırıcısını nasıl çağıracağız? Bunu base anahtar sözcüğü ile halledeceğiz. Daha fazla laf kalabalığı yapmadan sözü aşağıdaki örneğe bırakalım:

    class IkiBoyutluSekil
    {
        public IkiBoyutluSekil(double _genislik, double _yukseklik) {
            this.Genislik = _genislik;
            this.Yukseklik = _yukseklik;
        }

        double genislik;
        double yukseklik;

        public double Genislik
        {
            get { return genislik; }
            set { genislik = value; }
        }
        public double Yukseklik
        {
            get { return yukseklik; }
            set { yukseklik = value; }
        }
    }

    class Ucgen : IkiBoyutluSekil {

        string isim;
        public string Isim
        {
            get { return isim; }
            set { isim = value; }
        }

        public Ucgen(double _genislik, double _yukseklik, string _isim) : base(_genislik, _yukseklik) {
            this.Isim = _isim;
        }

        public double Alan() {
            return Genislik * Yukseklik / 2;
        }     
    } 

static void Main(string[] args)
        {
            Ucgen ucgen = new Ucgen(10, 20, "üçgen");
            Console.WriteLine("Şeklin adı: " + ucgen.Isim);
            Console.WriteLine("Şeklin alanı: " + ucgen.Alan());
        }

Bu kodun çıktısı aşağıdaki gibi olacaktır:

Şeklin adı: üçgen
Şeklin alanı: 100

Bu kodu incelemek gerekirse; Ucgen sınıfı IkiBoyutluSekil sınıfından türemiştir (class Ucgen : IkiBoyutluSekil {....}). IkiBoyutluSekil temel sınıf olup kendi yapılandırıcısına sahiptir. Ucgen sınıfı ise IkiBoyutluSekil sınıfının yapılandırıcısını kullanmaktadır. Burada base anahtar sözcüğüne, konumuna ve kullanım şekline dikkat ediniz. IkiBoyutluSekil sınıfı Genislik ve Yukseklik propertieslerine ilk değer ataması yapıyor. Ucgen sınıfı ise base(_genislik, _yukseklik) ile temel sınıfın yapılandırıcısını çağırarak tekrar tekrar ilk değer ataması yapmıyor. Zaten IkiBoyutluSekil sınıfı bunu yapıyordu. Böylece Ucgen sınıfı bu yükten kurtulmuş oldu.

Bir başka konuya değinmek gerekirse;

Kalıtımda Ad Gizleme

Kalıtım hiyerarşisinde temel sınıf ile aynı ada sahip değişkenler tanımlamak mümkündür. Bu durumda temel sınıftaki değişken gizlenmiş olup, türetilmiş sınıftaki değişken geçerli olacaktır. Ad gizleme işlemini yapmak istediğimiz değişkenin başına new anahtar sözcüğünü eklemeliyiz.

Aşağıdaki basit örnek ad gizleme kavramını anlatmaktadır.

  class A
    {    
        public int x = 10;
    }

    class B : A {

        new int x = 11; //A'daki x'i gizledik

        public B() {
            Console.WriteLine(x);
        }
    }

B b = new B(); dediğimizde çıktımız "11" olacaktır. Tanımlara dikkat ediniz.

Bu işlemin tam tersi de mümkündür. Bir değişken türetilmiş sınıfta gizlenebildiği gibi, temel sınıf içindeki değişken de açığa çıkarılabilir. Bunun için base anahtar sözcüğünü kullanacağız.

Aşağıdaki örneği inceleyelim:

class A
    {    
        public int x = 10;
    }

    class B : A {

        new int x = 11;

        public B() {

            int a = base.x; //bu A'nın içindeki x
            int b = x; //bu B'nın içindeki x

            Console.WriteLine("A'nın içindeki x: " + a);
            Console.WriteLine("B'nın içindeki x: " + b);
        }
    }


B b = new B(); dediğimizde çıktımız

A'nın içindeki x: 10
B'nın içindeki x: 11

olacaktır. base'in kullanımına dikkat ediniz.

Bu yazıyı şimdilik burda noktalıyoruz. Anlaşılmayan bir konu varsa yorumlar kısmında yazabilirsiniz.

Bir sonraki yazıda Kalıtımda Overriding konusunu ele alacağız.

Bir sonraki yazıda buluşuncaya dek sağlıcaklakalın...

Paylaş:

17 Kasım 2017 Cuma

C#'ta Kalıtım (Inheritance) - Kalıtımda Üye Erişimi - Part 2

Merhaba,

Bir önceki makalemizde kalıtıma giriş yapmıştık. Bu makalede ise üye erişimi konusunu inceleyeceğiz.

Bildiğiniz üzere OOP mimarisinde bir sınıfın private üyelerine dışardan erişim söz konusu değildir. Bu kural kalıtımda da geçerlidir. Yani bir A sınıfı, B sınıfını kalıtım yoluyla devraldığı zaman B'nin private üyelerine erişemez. Bu ilk bakışta ciddi bir kısıt gibi gözükebilir ama private üyelere dolayı yollardan erişim sağlanabilir. Örneğin; propertiesler sayesinde dolaylı erişim sağlanabilir.

 class A{
        private int x;
        public int X
        {
            get { return x; }
            set { x = value; }
        }
    }

    class B : A {   }

     static void Main(string[] args) {
            B b = new B();
            b.x = 10; //Hata!! private üyeye erişim yok
            b.X = 10; //Hata yok. public üye            
        }

Peki bazı özel durumlarda temel sınıfın private üyesine erişmemiz gerektiği durumda ne yapacağız? Üyeyi public hale getirirsek bu üyeye dışardan her türlü erişim mümkün olabilecek. Merak etmeyin, dil mühendisleri buna da çare bulmuşlar: protected üye. protected üyeler yalnızca kalıtım yoluyla aktarılan sınıflarca erişilebilmektedir. Dışardan erişim yine sözkonusu değildir. protected üye, türetilmiş sınıf için public özelliği gösterir. Fakat dışardan erişimler için yine privatedir.

Aşağıdaki örneği inceleyelim:

 class A{
        protected int x;
    }

    class B : A {
        public B(int _x) {
            this.x = _x; //Buraya dikkat! this.x ifadesi A'daki protected int x değişkenidir
            Console.WriteLine(x.ToString());
        }
    }

 static void Main(string[] args){
            A a = new A();
            a.x = 10;//Hata!! protected üyeye erişim yok
            
            B b = new B(10); //B classına göz atın
        }

Yukardaki örneği incelersek; A sınıfından bir nesne oluşturduğumuz zaman (A a = new A()) pretected int x; üyesine dışardan erişim sağlayamayız ama A'yı devralan B sınıfı this.x ile A'nın protected üyesine erişim sağlayabilmektedir. Bu kısma dikkat etmelisiniz.

Kalıtımda üye erişimiyle ilgili kısa bir bilgi vermiş olduk. Bir sonraki makalemizde Kalıtımda Yapılandırıcılar ve Ad Gizleme konusunu ele alacağız.

Sağlıcaklakalın...
Paylaş:

Bu Blogda Ara