C# VE OOP KILAVUZU EK B

EK B C# VE OOP KILAVUZU Bu kısım, T-SQL'i bir programlama dili olarak bilenler için C# diline hızlı bir başlangıç maksatlı eklenmiştir. Ünitenin sonl...
Author: Emel Cumali
5 downloads 0 Views 1MB Size
EK B

C# VE OOP KILAVUZU Bu kısım, T-SQL'i bir programlama dili olarak bilenler için C# diline hızlı bir başlangıç maksatlı eklenmiştir. Ünitenin sonlarına doğru nesneye dayalı programlamanın genel bir özetini ve takip eden sayfalarda da 3N katman mimarisi için örnek bir uygulama bulabilirsiniz.

C# İLE PROGRAMLAMAYA GİRİŞ KILAVUZU Bu bölüm, daha önceden herhangi bir dilde (ki buraya kadar okudunuzsa en azından TSQL diye bir dili kullanmayı biliyor olmalısınız) kodlar yazmış ama C# ile henüz tanışmamış programcılar için hızlı bir başlangıç kılavuzu olarak hazırlanmıştır. Bu bölüm C# dili gibi geniş bir kavramın sadece kitap içeriğindeki program parçacıklarını kodlayabilecek kadarını kapsamaktadır. Kitap içeriği C#'ı sadece yönetilmiş olarak (.NET Framework dahilinde) ele almaktadır. C# ile dört farklı çalışabilir program yazılabilir: Komut satırı uygulaması, ASP.NET uygulaması Windows Formları Uygulaması. ve WPF uygulamaları Bunun dışında, C# ile .Net platformu için kendiliğinden çalışamayacak ama bir başka program tarafından yüklenebilecek programlar (dll uzantılı assembly'ler) kodlamak mümkündür. SQL Server içerisindeki CLR bileşeni bu türden kodları çalıştırabilen ortamlardan biridir. C# ile Merhaba Dünya CLR ortamı, C# dili ile de programlanabilen bir yönetilmiş (managed) ortamdır. Managed ortam, sistem kaynaklarını programcı yerine kontrol eden ortam demektir. .NET CLR ortamını kullanarak çalışacak en basit komut satırı C# programı aşağıdaki şekilde kodlanabilir: using System; public class MerhabaCSharp{ public static void Main(){ Console.WriteLine("Merhaba C#"); } }

İlk örneği yazıp derleyip çalıştırabilmek, bir dili öğrenmenin yarısıdır denir. İlk C# örneğinizi test etmek için, Visual Studio.NET ile bir komut satırı uygulaması başlatabileceğiniz gibi, .NET Framework SDK kurulumuyla birlikte gelen csc.exe C# derleyicisini kullanabilirsiniz.

Şekil B.1: MerhabaCsharp projesinin başlatılması

Şekil B.2: İlk C# programının çalıştırılması.

Bir dili güçlü kılan noktalardan biri de kendisi ile birlikte gelen hazır kütüphanenin genişliğidir. Çünkü bu kütüphane sayesinde programcı daha az kod yazarak daha fazla iş çıkartabilir. Bu şekilde bir dil ile gelen hazır kütüphaneye temel kütüphane (base library) denir. C#, .NET Framework ile gelen C# kütüphanesini temel kütüphane olarak kullanır. Bir dil ile programlar geliştirirken, dil içerisinde yer alan sınıfların hiyerarşik olarak düzenlenmesi gerekebilir. Bu tür durumlar için C# isim uzayı (namespace) deyimini destekler. Mesela System.Data isim uzayı içerisinde veri yönetimi ile ilgili sınıflar yer alır. Bir sistem kütüphanesini, sadece kod yazarken kendi programımızın parçasıymış gibi kullanabilmek için, using deyimi ile ihtiyaç duyduğumuz kütüphaneyi içeren isim uzayına atıfta bulunmak yeterlidir. C# nesneye dayalı bir dildir. Bu nedenle, C# içerisinde yazılan bütün programların bir tür içerisinde bulunması zorunluluktur. Bizim örneğimizde bu nedenle MerhabaCSharp sınıfı yer almakta. Sınıflar Yazmak Bir sınıfın dışarıdan bakıldığında iki temel türden elemanı olabilir: alan ve metot. Alan, bir değer atanabilen ve değeri okunabilen sınıf bileşenidir. Sınıf içerisinde fonksiyonellikleri yerine getirecek bileşene de metot denir. Nesneye dayalı programlamada metot, bir sınıf içerisinde yazılmış stored procedure'ler gibi düşünülebilir. Özellikler için örnek vermek gerekirse, bir nesnenin Boy, En, Renk gibi parametrik değerler alacak bileşenleridir. Metodlar bir işlevi yerine getirirler. Yazdır, Topla, Çarp, Çıkart, Birlestir… gibi C#'da bir programın çalıştırılabilir olması için bir sınıfının Main() adlı metodu olması gerekir. Yukarıdaki örnekte bu türden bir metot yer almaktadır. Örnek 1: En basit olarak marka bilgilerini saklayacak bir Class şu şekilde tanımlanabilir:

//Marka.cs public class Marka{ public int markaKod; public string markaAd; public MarkaBilgileriniYaz(){ Console.WriteLine("MarkaKodu:{0} \t MarkaAdı:{1}", markaKod, markaAd); } }

Örneğimizde, markaKod ve markaAd birer özellik iken MarkaBilgileriniYaz() bir metoddur. Yazmış olduğunuz sınıflar Int gibi yeni bir türdür. Bu türlerde değişkenler tanımlayabilirsiniz. Herhangi bir sınıf türünden tanımlanan değişkene nesne denir. İpucu: C#'da açıklama tek satır eklemek için // ve çok satır açıklama eklemek çin /*…..*/ işaretlerini kullanabilirsiniz. Bloklar tanımlamak için {} şeklinde açılır kapanır kıvırcıklar kullanılır. Örnek 2: Çalıştırabildiğimiz ilk nesne içeren örneğimizi şu şekilde değiştirelim: using System; public class MerhabaCSharp{ public static void Main() { Marka yeniMarka = new Marka(); yeniMarka.markaKod=500; yeniMarka.markaAd="Verivizyon"; yeniMarka.MarkaBilgileriniYaz(); } } public class Marka{ public int markaKod; public string markaAd; public void MarkaBilgileriniYaz(){ Console.WriteLine("MarkaKodu:{0} \t MarkaAdı:{1}", markaKod, markaAd); } }

Örneğimizde, Marka sınıfının özelliklerinin ve metodunun kullanılmasını görebilirsiniz. Şekil B.3: Marka sınıfının kullanılması

Değişkenler Değerler ve Veri Tipleri

Sınıflarda özellik tanımlarken ve kod yazarken, değişkenler kullanılır. Değişkenlerin ne amaçla kullanıdlığını T-SQL ile Programlamak adlı bölümde öğrenmiştik; kendilerine aktarılan değeri tutup kendi adı ile bu değere erişim tanımlamak. Değişkenin adı değişmez ama değerine her zaman atama yapılabilir. T-SQL'de olduğu gibi, C# için de değişkenlerin tipi vardır. C#'ta veri tipleri iki farklı grupta ele alınır: Değer tipler ve Referans tipler.

Değer tiplerin değerleri de kendileri ile birlikte Stack denilen bölgede tutulur. Bu bölge hızlı erişim sağlar ancak, bu bölgede saklanacak bir tipin ne kadar alan gerektirdiğini tam olarak bilmek gerekir. Tablo B.1: C#'da yer alan değer tipleri

Değişken Türü

Açıklama

sbyte

8-bit işaretli tam sayı

short

16-bit işaretli tam sayı

int

32-bit işaretli tam sayı

long

64-bit işaretli tam sayı

byte

8-bit işaretsiz tam sayı

ushort

16-bit işaretsiz tam sayı

uint

32- bit işaretsiz tam sayı

ulong

64-bit işaretsiz tam sayı

float

Tek duyarlıklı kayan noktalı sayı (yaklaşık değer ondalık)

double

Çift duyarlıklı kayan noktalı sayı (daha büyük yaklaşık değer ondalık)

char

2-bytelık Unicode temelli tek harf saklayabilen değişken

decimal

28 basamak duyarlıklı kesin değer kaydedebilen ondalık sayı.

bool

true veya false değeri alabilir.

Değer tipli bir değişkene doğrudan değer ataması yapılabilir. Örnek 3: a değişkenine a=0 değeri atanabilir. int a; a=0;

Dikkat: C#'da bir değişken, ilk olarak değer atanmadan, başka bir işlemde kullanılamaz, sadece tanımlanabilir. Referans tipler, sınıf gibi boyutu her atamada farklı değerler alabilecek tipler için idealdir. Referans tiplere doğrudan atama yapılamaz. Çünkü referans tipler, değerlerini Heap denilen özel bir bölgede tutarak o bölgedeki değere referans içerirler. new operatörü, heap bölgesinden yeni bir adres tahsis ederek bu bölgenin adresini, atıfta bulunulan değişkenin değeri ile eşleştirir. Şekil B.4: CLR ortamında bir değer tip değişken stack bölgesinde tutulurken, referans tip değişkenin sadece kendisi stack bölgesinde tutulur. Değeri Heap bölgesinde tutulur ve değişken buraya bir refarans içerir.

Örnek 4:

yeniMarka adında bir değişkenin stack bölgesinde tanımlanması ve heap bölgesinden yeni bir adres alınması. int x, y; int z=0; Marka yeniMarka ; // marka türünde bir değişkeni stack bölgesine at yeniMarka = new Marka(); // heap bölgesinden bir adresi bu değişkene tahsis et. Şekil B.5: Stack ve Heap bölgelerindeki değişken ve değerlerin davranışları

Stack bölgesindeki değikenlere doğrudan null değeri atanabilir veya saha dışına çıkıldığında bu değişkenler silinebilir. Bu tür durumlarda, Heap bölgesindeki ayrılmış alan öksüz kalacaktır. Heap bölgesindeki öksüz alanlar, hafıza limitleri zorlanmaya başlandığında Garbage Collector adı verilen özel .NET Framework bileşeni tarafından otomatik olarak temizlenirler. Temizlenme esnasında nesnelerin Finalize() metodları çalıştırılır. İpucu: String tipi, C#'ta referans tipli olmasına rağmen, değer tipli gibi davranır. Bunun arka planda yansıması olarak sık değişen string tipteki veriler fazla hafıza tüketirler. Bu nedenle, fazla değişen string değişkenler yerine System.Text sınıfı içerisinde yer alan StringBuilder sınıfını kullanabilirsiniz. String tiplerin başlangıç ve bitişleri "" ile belirtilir. Ancak string ifadenin kendi içerisinde de bir " geçerse, bu derleyicinin sentaksı anlamasını imkansız kılar. Bu nedenle bazı durumları uç durumlar olarak ele almak gerekir. Bu türden karakterler ve işlevlerini aşağıdaki tabloda bulabilirsiniz. Tablo B.2: Uç (escape) karakter ve karşılıkları.

Uç Karakter

Karşılığı

\'

Tek tırnak

\"

Çift Tırnak

\\

Ters bölü

\0

Null

\a

Alert (bip sesi)

\b

Backspace

\f

form besleme

\n

Alt satır

r

Satır başı

t

Yatay tab (3 karkater boşluk)

v

Düşey tab (3 satır alta geçme)

Uç karakterleri doğrudan bir string ifade içerisinde vermek istiyorsanız string ifadenin başına@ işareti koymak gerekir. Örnek 5: string ucKarakterler = @"\, ' ve \t gibi karakterler her zaman bir metin içinde geçmeyebilir" Tür Dönüşümleri

C# açısından tür dönüşümlerini iki ana başlık altında ele alabiliriz: Bilinçsiz Tür Dönüşümü ve Bilinçli Tür Dönüşümleri. Bilinçsiz tür dönüşümü, doğrudan bir atama işlemi ile dönüşümün yapılabilmesidir. Örnek 6: Bilinçsiz dönüşüm, sadece uygun tiplerden büyük olan küçük olana atandığında yapılabilir. int i =0; sbyte j=1; i=j; // j sbyte değeri bir int türden değişkene atandığı için kendiliğinden dönüştü.

Bu türden dönüşümü büyük türün küçük türe atanması halinde problem çıkartacaktır. Uyumlu tipleri birbirine doğrudan tür belirterek de dönüştürmek mümkündür: Örnek 7: int i =5000; sbyte j=1; j=(sbyte)i; // i önce sbyte türüne dönüştürüldü. Arkasından j sbyte tipli değişkene aktarıldı.

Ancak bu türden dönüşümler yaparken, veri kaybına özellikle dikkat etmek gerekir. C#'da uyumsuz türleri birbirine dönüştürmek için Convert sınıfını kullanabilirsiniz. Convert sınıfının hemen her türü bir diğer türe dönüştürecek uygun metodları vardır. Örnek 8: String türden, sayı içeren bir değeri Int türüne dönüştürebiliriz. string sYas="23"; int iYas; iYas = Convert.ToInt32(sYas); // veya Int32.Parse(sYas) da eşdeğer bir yöntemdir. Değer ve Referans Tipler Arası Dönüşüm: Boxing ve Unboxing

Bazen, adres temelli bir değişkeni değer tipli gibi veya tam tersi değer tipli bir değişkeni adres temelli gibi kullanmak gerekebilir. Mesela bir metot, dışarıdan nesne bekliyordur ama elimizdeki değer int (Stack değişken)dir veya bir nesne int bekliyordur ama elimizdeki int değeri bir nesnede tutuluyordur. Değer tipli bir değişkenin adres tipe (Object) çevrilmesine Boxing denir. Örnek 9: int x=3; object o = x; // artık 3 değeri heap'te tutulmuyor

Adres temelli değer taşıyan bir değişkeni, değer türüne döndürmeye de Unboxing denir.

Örnek 10: int y= (int) o; Döngüler ve Karar Yapıları

T-SQL'de gördüğümüz döngü ve karar yapılarının çok daha gelişmişleri C# dilinde de vardır. Kitap içerisindeki örnekleri anlayabilmeniz için, sadece if-else, for ve while yapılarının kullanımını bilmeniz yeterli olacaktır. if-else Karar Yapısı

Belli şartlarda çalışacak program parçacıkları oluşturmak için şu şekilde kullanabilirsiniz. int x=20 if (x == 20) { Console.WriteLine("x == 20 imiş"); } else { Console.WriteLine("x != 20 imiş"); } for Döngü Yapısı

Bazı ifadelerin n kere çalışması gerekiyorsa for yapısı kullanılır. Örnek 11: Ekrana 1-20 arasındaki sayıları yazdıracak şekilde bir C# programı şu şekilde yazılabilir: for (int i = 0; i < 20; i++) { System.Console.WriteLine (i); } while Yapısı

Bazen, belli şartların hâsıl olması için kaç döngü atılacağı belli olmayan ifadeler olabilir. Bu tür ifadeleri while yapısı ile şartlar oluşuncaya kadar tekrarlatabiliriz. int x = 99; while (x > 36) { System.Console.WriteLine( "Henüz x değeri 36dan büyük.. "); x--; }

İpucu: Döngüleri bir defaya mahsus yarıda kesmek için continue ve geri kalan tüm döngüleri bırakıp döngüyü kesmek için break ifadelerini kullanabilirsiniz. Nesnelerle Çalışmak Yazmış olduğunuz sınıflar Int gibi yeni bir türdür. Bu türlerde değişkenler tanımlayabilirsiniz. Herhangi bir sınıf türünden tanımlanan değişkene nesne denir. Programlamada Nesnel Yaklaşım, günlük hayatta etrafımızdaki araçları kullanmaya bakış açımızı yansıtmak üzere geliştirilmiştir.



Günlük hayatta, belli amaç için kullandığımız her şey nesnedir.



Program geliştirirken, aslında gerçek dünyadan problemleri modelleriz.

Nesne, "kapalı bir kutudur". Belli bir işlevi yerine getirmek için tasarlanmıştır. Nesneyi kullanabilmek için girdiler veririz. Nesne işleyişi, bizim değil tasarlayanların bilgisi dahilindedir. Biz iç işeyişini bilmeden ne verdiğimizde ne alacağımızı bilerek kullanırız. Yani nesne, bilmediğimiz işlemler yapar ve bize bir çıktı verir. Sınıflarda Erişim Seviyelerini Anlamak

Nesneler sınıflar halinde programlanırlar. Yani nesnelerin tanımları, sınıflarda saklıdır. Ya da tersten bakacak olursak, bir sınıftan nüsha çıkartıldığında bunun adı nesne olur. 

Bir "kapalı kutunun" iç işleyişini problemsiz olarak yerine getirebilmesi, dış dünyadan soyutlanmış olmasına bağlıdır.

Mesela bir cep telefonunu düşünün. Kablolarına erişip kısa devreler yaptırarak da pekâlâ bir şeyler yaptırabilirsiniz. Ama üretici firması, bu 'kapalı kutu' yani telefon'un işlevini istikrarlı olarak yerine getirebilmesi için, bir çok iç işleyişini bir kılıfın arkasında saklamış ve bize sadece tutarlı, amaca uygun kullanım girdilerinde bulunabileceğimiz tuşlar tasarlayıp bu tuşlara erişmemize olanak sağlamıştır. İşte programlama dünyasındaki nesneler de kullanılmadan önce tanımlanırlar. Yani sınıf halinde kodlanırlar. Sınıf halinde kodlama aşamasında, hangi metotların, hangi özelliklerin dışarıdan erişilebilir olacağı, hangi elemanların sadece iç işleyişle ilgili olduğundan dış dünyadan erişilemez ve bilinemez olması gerektiğine karar veririz. Bu türden karar verme işlemini belirleyebileceğimiz anahtar kelimeleri ve işlevlerini aşağıdaki tabloda bulabilirsiniz. Tablo B.3: nesnelerin elemanlarına erişimi belirleyen kelimelerden sık kullanılanları.

Erişim Belirleyici

Kullanım Yeri

İşlevi

public

Sınıf, özellik, metot

Dışarıdan erişilebilir bir eleman belirlemede kullanılır

private

Sınıf, özellik, metot

Elemanın sadece nesnenin iç işleyişi için gereksinim duyulduğunu, dışarıdan erişilmemsi gerektiğini anlatır

Örnek 12: Daha önceki örneklerde şu şekilde kodladığımız marka sınıfında, markaKod ve markaAd alanlarına doğrudan erişimi kapatalım: public class Marka{ private int markaKod; private string markaAd; private void MarkaBilgileriniYaz(){ Console.WriteLine("MarkaKodu:{0} \t MarkaAdı:{1}", markaKod, markaAd); } }

Artık şu ifade düzgün olarak çalışamayacaktır: using System; public class MerhabaCSharp{ public static void Main() { Marka yeniMarka = new Marka(); yeniMarka.markaKod=500;

yeniMarka.markaAd="Verivizyon"; yeniMarka.MarkaBilgileriniYaz(); } } Şekil B.6: Erişim seviyeleri, sınıf dışarısından, sınıf elemanlarına erişimleri belirler.

Çünkü markaKod ve markaAd sütunlarına doğrudan erişim kapatılmıştır. Bu sınıfın ilgili alanlarına kontrollü bir erişim sağlamak için, property adı verilen yarı metod-yarı değişken yapılar kullanabiliriz. Örnek 13: Nesne değişkenlerine erişimlerini düzenlemek üzere property'leri kullanabilirsiniz. public class Marka{ private int markaKod; private string markaAd; public int MarkaKod { get { return markaKod; } set { if (value > 0) markaKod = value; } } public string MarkaAd { get { return markaAd; } set { markaAd = value; } } public void MarkaBilgileriniYaz() { Console.WriteLine("MarkaKodu:{0} \t MarkaAdı:{1}", MarkaKod,MarkaAd); } }

Dikkat edilirse, artık markaKod ve markaAd sütunlarına atanan değerler belli bir kontrole tabi tutulabilecek durumdadır. (mesela markaKod değerinin sıfırdan küçük olmaması gerektiği kontrol edilmekte) Şekil B.7: Marka Sınıfı

Bu tür bir sınıf şu şekilde nesneleştirip kullanılabilir: using System; public class MerhabaCSharp {

public static void Main() { Marka verivizyon = new Marka(); verivizyon.MarkaKod = 500; verivizyon.MarkaAd = "Verivizyon"; verivizyon.MarkaBilgileriniYaz(); Marka seckin = new Marka(); // kurala uymayan bir atamayı, // nesne kabul etmeyecektir seckin.MarkaKod = -1; seckin.MarkaAd = "Seçkin Yayın A.Ş."; seckin.MarkaBilgileriniYaz(); } } Şekil B.8: Programın çıktısı

Şekil B.9: Sınıflardan nesne türetilmesi

Yapıcı (Constructor) Kavramını Anlamak

Bir sınıftan nesne nüshalandırılması aşamasında, sınıfa ait bir metodun devreye girmesi gerekir. Bu metod, 

Sınıf ile aynı adı taşır.



Dönen değer almaz.



Genel olarak public'tir.



En genel manada, bir nesnenin hazırlanması (initialisation) maksadıyla kullanılır

Örnek 14: Yeni bir marka oluşturulurken, bu markaya ait başlangıç verilerinin otomatik olarak atanabilir olmasını istersek, public class Marka { private int markaKod; private string markaAd; public Marka(int markaKod, string markaAd) { MarkaKod = markaKod; MarkaAd = markaAd; } public int MarkaKod { get { return markaKod; } set { if (value > 0) markaKod = value;

} } public string MarkaAd { get { return markaAd; } set { markaAd = value; } } public void MarkaBilgileriniYaz() { Console.WriteLine("MarkaKodu:{0} \t MarkaAdı:{1}", MarkaKod, MarkaAd); } }

Bu türden bir sınıfı kolayca şu şekilde kullanabiliriz: using System; public class MerhabaCSharp{ public static void Main() { Marka verivizyon = new Marka(500,"Verivizyon"); verivizyon.MarkaBilgileriniYaz(); Marka seckin = new Marka(501,"Seçkin Yayın A.Ş"); seckin.MarkaBilgileriniYaz(); } } Şekil B.10: Programın çalışması

Herhangi bir anda, sınıftaki elemanların 'o' anda üstünde çalışılırkenki hali ile nesne için nüshalanmış kopyalarını kastetmek üzere this anahtar sözcüğünü kullanabilirsiniz. Örnek 15: Marka sınıfının, MarkaBilgileriniAl adında aşağıdaki gibi bir metodu olsun: public void MarkaBilgileriniAl(int MarkaKod, int MarkaAd) { this.MarkaKod=MarkaKod; this.MarkaAd=MarkaAd; }

Metodları Aşırı Yüklemek

T-SQL ile Stored Procedure'ler gibi prosedürel rutinler kodlarken, isteğe bağlı parametreler için default değerler verebiliyorduk. C# bu türden bir seçenek sunmaz. Bunun yerine aynı adda farklı sayıda veya tipte parametreler alan metodlar tanımlayabiliriz. Bu işleme Metot Aşırı Yükleme (Method Overloading) denir. 

Aynı adda ama farklı imzada iki method aşırı yüklenmiştir denir.



Bir durum için method adı çağrıldığında, parametre olarak verilen değer ve değişkenlerin tip ve sayısına göre uygun olan method otomatik olarak devreye girer.

Constructor metotlar da neticede bir metot olduğundan aşırı yüklenebilirler.

Örnek 16: Daha önceden, marka sınıfının oluşturulabilmesi için boş bir yapıcı metot derleyici tarafından otomatik olarak ekleniyordu. Ancak artık biz kendimiz bir yapıcı metot ekledik. Halen Marka sınıfının hiçbir özelliği verilmeden de nesne türetiminde kullanılması isteniyorsa, ikinci bir constructor metodu boş olarak bile olsa tanımlanabilir. Aksi halde derleyici tarafından böyle bir metod otomatik olarak oluşturulamaz. public class Marka{ private int markaKod; private string markaAd; public Marka(){ // boş yapıcı metot } public Marka(int markaKod, string markaAd) { MarkaKod=markaKod; MarkaAd=markaAd; } public int MarkaKod{ get{return markaKod;} set { if (value > 0) markaKod = value; } } public string MarkaAd { get { return markaAd; } set { markaAd = value; } } public void MarkaBilgileriniYaz(){ Console.WriteLine("MarkaKodu:{0} \t MarkaAdı:{1}", MarkaKod, MarkaAd); } }

Static Kavramını Anlamak

Mademki nesneleri, sınıflardan çoğaltarak kullanıyoruz, bu durumda her nesne için sınıfın içerisindeki her şeyi kopyalatmaya gerek var mıdır? Gereksiz şeyleri hafızada gereksiz yere çoğaltmasak da olur mu? Gibi soruları kendinize sormuş olabilirsiniz. Nesneye dayalı programlama, her şeyin illa da bir nesne üstünden erişilebilir olmasını zorunlu kılmaz. Bazı elemanlara sınıf üstünden erişilebilir. Mesela, markaların marka kodlarının belli bir değerin altında kalmaması gerekiyor diyelim. Bu türden bir parametrenin her bir sınıf ile çoğaltılması yerine sadece sınıfın bir parçası olarak kalması daha akıllıca olacaktır. Örnek 17: Marka sınıfına, minimum marka kodunu işaretlemek üzere static bir alan ekleyelim. public class Marka { public static int minimumMarkaKod = 0; private int markaKod; private string markaAd; public Marka(){ // boş yapıcı metot }

public Marka(int markaKod, string markaAd) { MarkaKod=markaKod; MarkaAd=markaAd; } public int MarkaKod{ get{return markaKod;} set { if (value > 0) markaKod = value; } } public string MarkaAd { get { return markaAd; } set { markaAd = value; } } public void MarkaBilgileriniYaz(){ Console.WriteLine("MarkaKodu:{0} \t MarkaAdı:{1}", MarkaKod, MarkaAd); } }

Örneğimizde sadece bir değişkeni static olarak tanımlandık. Bir metodu, bir özelliği de static olarak tanımlamak mümkündür. Ancak unutulmaması gereken nokta, statik tanımlanan elemanlara nesne üstünden ulaşılamaz, doğrudan sınıf üstünden ulaşılabilir. using System; public class MerhabaCSharp{ public static void Main() { Marka verivizyon = new Marka(); verivizyon.MarkaKod=500; verivizyon.MarkaAd="Verivizyon"; verivizyon.MarkaBilgileriniYaz(); Marka seckin = new Marka(); // kurala uymayan bir atamayı, // nesne kabul etmeyecektir seckin.MarkaKod = 501; seckin.MarkaAd = "Seçkin Yayın A.Ş."; Console.WriteLine("bir marka kodu {0} değerinden büyük olmalıdır", Marka.minimumMarkaKod); } } Şekil B.11: Static bir alana sınıf üstünden erişilmesi.

Şekil B.12: Static sınıf elemanları, nesnelerle birlikte çoğaltılmaz, sınıf üstünden erişilebilir.

Dikkat: Static üyeler, nesneye erişemezler. Nesneler, statik üyelere erişebilirler. Elbette ne nesneye dayalı programlama, ne de C# bu kadarlık bir bilgi ile tam olarak kullanılamaz ancak burada verilenler, size sadece kitap içeriğini anlayabilecek seviyeye getirme amaçlıdır. Özetle Nesneye Dayalı Programlama

Nesne yönelimli programlamanın temelini Class'lar oluşturur. Bir class, günlük anlamda bildiğimiz nesne'nin kodlanarak tanımlamasıdır. Nesnelerle çalışma teorisinin temelinde, içeriğinin nasıl çalıştığını bilmeden onu kullanabilme mantığı yatar. Araba, DVD Player gibi... nasıl yaptıklarını bilmeyiz ama ne yaptıklarını biliriz. 1.

Bir class genel olarak şu yapıdan ibarettir (örnek için bölümün ilerleyen kısmında yer alan sınıflara bakabilirsiniz. )



Method'lar, property'ler veya değişkenler (field) içerebilir (bunların her birine member denir)



Method, prosedürel programlamada bildiğimiz fonksiyon'un işlevini class'larda yerine getirir.



Property prosedürel programlamada bildiğimiz değişken'in işlevini class'larda yerine getirir. Bu işlev için, değişken de kullanılabilir.



Property'ler yazılabilir ve okunabilir olabilir. Sadece okunabilir veya sadece yazılabilir tanılanması mümkündür. Ancak değişkenleri okunabilir yapmak için, Method'lar yerine, property (özellik) adı verilen yarı method yarı değişken yapı kullanılabilir.



Method'lar ve değişkenler, sadece sınıf içerisinden erişilebilir veya bütün sınıflardan yahutta bütün assemly'lerden erişilebilir olabilir veya erişilemez olarak kodlanabilir. Bunlar için, Public, Private, Internal, External... gibi deyimler (Erişim Tanımlayıcıları) kullanılır.



Methodlar, ayı adda ama farklı girdi veya çıktı parametrede üst üste tanımlanabililir. (method overloading) Bu durumda, class'ın kullanımı aşamasında, çağrılış şekline en uygun method otomatik olarak algılanır ve devreye girer.

2.

Class'lar yazıldıktan sonra, kütüphanelerde saklanırlar. MFC, .NET gibi belli bir geliştirme ortamı ile birlikte gelen kütüphanelere, temel kütüphane (base librarybase class) denir. Bu ve kullanıcı tarafından geliştirilmiş kütüphaneler, isim uzayı (namespace) denilen nokta notasyonlu yapılarla düzenlenir. Namespace kavramı, fiziksel değil mantıksal bir kavramdır. Yani, farklı dokümanlarda birbiri ile aynı seviyede class'lar olabilir.



Düzenlenmiş Class'lara erişim, isim uzayından sonra sınıf adı gelecek şekilde bir notasyon ile sağlanır. (System.Data gibi)



C# gibi dillerde, kütüphane sınıflarını, uygulamanın içerisindeymiş gibi doğrudan nesne adıyla çağırmak mümkündür. Ancak bunun için uygulamanın en başında, isim uzayları ve alt isim uzayları nokta notasyonu ile sınıf adına kadar veya belli bir

seviyeye kadar belirtilebilir. Geri kalanlar, (sınıf adı veya isim uzayının tamamı belirtilmedi ise, belirtilmeyen kısmı) kod içerisinde belirtilerek, sınıflara erişilebilir. (using System.Data gibi) 3.

Class'lar yeni class'lar türetilirken şu şekilde kullanılarak kolaylıklar sağlarlar:



Yeni bir class'ın çekirdeğini oluşturabilirler. (Implementation Inheritance) ve



Bazı class'lar miras almaya karşı korumaya alınabilir (Sealed Class)



Bazı class'lar miras alınmadan kullanılamaz tanımlanabilir (Abstract Class)



Bazı class'lar, miras alındıktan sonra, methodlarını geçersiz kılacak aynı adda yeni bir metod tanımlanabilir (method overriding)



Kendisinden türeyecek class'ların yapıları hakkında söz sahibi olabilirler (Interface Inheritance)

4.

Class'lar Bir Uygulama geliştirirken, şu şekilde kolaylıklar sağlarlar:



Bazı class elemanları, static deyimi ile tanımlanmamışlardır ve nüshalandırma (instantiating) ile yeni class nüshası çıkartılarak, kullanılırlar. ASP biliyorsanız veya yukarıdaki ASP'li örneğe bakarsanız, ADODB.Connection'ı düşünün. Hep nüshalandırarak kullanılır.



Bazı methodlar ve propertiy'ler, değişkenler çoğaltılamaz olabilir (static) Bu tür method'lar kullanılırken, class'lar nüshalandırmaksızın, orijinal namespace ve sınıfın adı ile kullanılırlar. Yukarıdaki örnekte, Response.Write'i hatırlayın... hep aynı isim ile kullandık. Aşağıdaki örnekte, güvenlik işlemleri için geliştireceğimiz, MD5 ile özet alan Class da bu türdendir.



Class'lar, ebebeynleri ile aynı türdenmiş gibi davranabilirler. Böylece, temelde aynı class'tan türetildikleri halde farklı türlerde olup aynı türmüş gibi davranan class'lar elde edilebilir. (Polimorfizm)

3n Katmanlı Mimari ve Sınıfların Rolü Üç-n Katman Mimarisi, özellikle nesneye dayalı programlama ile birlikte güç bulmuş, veritabanı programlamanın esas dinamiklerini oluşturan bir programlama eğilimi hatta trendidir. Bu nedenle, tam manasıyla bilinmesinde yarar vardır. Burada, Nesneye dayalı programlamada kullanılan teriminolojinin anlaşılır bir özetini yapmaya çalıştım. Geniş bilgi için, bu konuda bir kitap okumanızda yarar olabilir. Üç Katmanı Tasarlamak

Üç katman mimarisinin en alt katmanını, buraya kadar öğrendiğimiz tekniklerle geliştirdiğimiz, Veri katmanı oluşturur. Veri katmanı ile temasta bulunan, uygulamanın en alt katmanına iş katmanı denir ve prensip olarak class'lardan oluşur. Class'lar, meseleye çok iyi vakıf kişiler tarafından tasarlanabilirler. Class'ları tasarlamak için geliştirilmiş standart yollar var. Bunlardan en genel olanı UML (Unified Modeling Language)'dir. Bu konuda yeni iseniz, nesneye dayalı bir dil ile kodlama işinin temellerini kavradıktan sonra UML ile de haşir neşir olmanız, yararlı olacaktır. Arkasından, genel olarak karşılaşılan sorunlara yaklaşım tarzları hakkında bilgi edinmek için Tasarım Desenleri (Design Pattern) öğrenmeniz, bu yoldaki son nokta olacaktır. Tasarım Deseni, sadece nesneye dayalı programlamaya uygulanmasa da, nesneye dayalı programlama için, bir çok tasarım desenini bir çok nesneye dayalı dilde bulmak mümkündür (C++, Java, C#). Bütün bu detayları, size bırakıp projemizin genel olarak, üç katmana dağılımını inceleyelim:

Tablo B.4: En üst katmanı oluştururan sunum katmanının görevi, iş katmanı ile kullanıcıyı etki-leştirmektir. Su-num katmanı doğrudan veritabanına erişmez! Bu işlemler için, iş katmanında geliştirilmiş Class'ları nüshalandırarak kullanabilir.

Katman

Sunum

İş

Veri

İşlevi

Projemizde Yer Alacak Dosya- Bileşen Adları

Kullanıcı ile etkileşim, iş katmanında kodlanmış Login.aspx, Login.aspx.cs nesneleri nüshalandırıp kullanmak. Sunum ve Veritabanı katmanları ile etkileşim. İş kurallarının sağlanması

KullaniciVT.cs Guvenlik.cs

Tablolar, Trigger'lar, Stored Procedure'ler, tblKullanici, Constraintler ve diğer veritabanı bileşenleri ile SP$yetkilendir kurgulanmış veri mantığının sağlanması

Buraya kadar öğrendiklerimiz çerçevesinde bir üç katman mimari yaklaşımı ile, daha önce, ASP-ADO ile gerçeklediğimiz örneğimizi yeniden ele alalım: Veri katmanını biliyoruz. tblKullanici adında bir tablomuz var. Bu tabloya erişip verilerin doğruluğunu onaylayan, SP$yetkilendir adında stored procedure kodladık, bu nedenle. Üç katman ile çalışırken, bilmemiz gereken şey, SP$yetkilendir'dir. Öteki kısımlar artık veri katmanında kalmıştır. Neticede, veri katmanı ile ilgili işlemleri bitirdiğimizi varsayabiliriz. İş Katmanını Tasarlamak

Bir sonraki katman olan iş katmanını tasarlamaya geçelim. Bu iş için, nesneye dayalı programlamaya tam destek sağlayan, Managed C# dilini kullanacağız. Managed ve Unmanaged kavramları kafanızı karıştırabilir. Aralarında ufak bir fark vardır: Managed ortamda, sistemden hafıza gibi kaynak alma ve sisteme bu kaynağı, işi bittiğinde iade etme işini, çalıştırma ortamı yerine getirir. JVM ve .NET Framework bu türden ortamlardır. Ama ortam, her zaman böyle olmayabilir. Şayet işletim sistemi ile kaynak alış-verişini programcının kendisi yürütüyorsa, (ki bu durumda programcıya çok fazla iş düşer ve sistemde daha fazla hata ihtimali vardır!) bu türden çalıştırma ortamına da Unmanaged ortam denir. C# bir dil olarak her iki ortamı da destekler. Ancak .NET ortamı, C#'ın bütün meziyetlerini gösterebileceği bir ortam değildir. Buna rağmen, biz .NET ortamında çalışacağız. Veritabanına erişip kullanıcı ile ilgili işlemleri yerine getirecek bir sınıfa ihtiyacımız var. Bu sınıfa KullaniciVT adını verelim. Class adlarının ilk harfi büyük harfle başlatılır. Bu bir zorunluluk değildir ama töre olmuş bir kodlama tarzıdır. Şimdi ilk sınıfımızı kodlayalım: Bu sınıfın amacı, Web'den gelen verileri her seferinde özetlemek (Digest)tir. Bir üyemiz ilk kaydolduğunda, seçtiği şifresi veritabanımızda saklanmaz. Bunun yerine, özeti saklanır. Daha sonra, yetkilendirmek için geldiğinde de şifresinin özeti alınır. Böylelikle, her seferinde gelen veririn daha önceki veri olup olmadığı bilinir, ama en önemli tarafı, ne olduğu asla bilinemez. Çünkü özet alma ile şifreleme yapan, SHA, MD5 gibi algoritamalar, tek yönlü veri dönüşümünü esas alan, ters yönde bir dönüşüme asla müsaade etmeyen algoritmalardır. ///Guvenlik.cs using System; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; namespace Verivizyon.Bilesenler {

public class Guvenlik { public static string OzetCek(string gercekDuzMetin) { Byte[] gercekByteMetin = new UnicodeEncoding().GetBytes(gercekDuzMetin); Byte[] ozetByteMetin = ((HashAlgorithm) CryptoConfig.CreateFromName("MD5")).ComputeHash(gercekByteMetin); return BitConverter.ToString(ozetByteMetin); } } }

İpucu: C#'ta fonksiyon yoktur. Bunun yerine, class'ların method'larını kullanacağız. Ancak bir methodu, gerçek manada sabit bir fonksiyon gibi her yerde nüshalandırmadan kullanmak istiyorsanız, static deyimi ile tanımlayabilirsiniz. Takip eden sınıfları, aşağıdaki gibi kodlayalım ///dosya:KullaniciVT.cs using System; using System.Configuration; using System.Data; using System.Data.SqlClient; namespace Verivizyon { //KullaniciVT, kullanicilarla veritabani // arasinda iliskiyi kuran, //is mantigi saglayicisidir. public class KullaniciDetay { public string kullaniciAd; /*Kullanicida bulunan ozellikler *.. * */ } public class YetkilendirmeBilgi { /* * Yetkilendirilen kisiye ait, kullanici kodu, isim, soyad * son giris tarihi gibi bilgileri encapsulate etmede kullanilir. * */ public string kullaniciKod; public string isim; public string sonGirisTarih; } public class KullaniciVT { public KullaniciDetay KullanicininBilgileri(int kullaniciKod)

{ /* kullanici bilgilerini veritabanindan cek * kullaniciDetay class'indan bir nusha al * vertabanindan gelen verileri bu nushaya yukle * neticeyi, methodu cagirana dondur * */ KullaniciDetay KullaniciBilgileri= new KullaniciDetay(); return KullaniciBilgileri; } public YetkilendirmeBilgi KullaniciYetkilendir(string kullaniciAd, string sifre) { // Bir adet connection ve bir adet command nesnesi olusturalim SqlConnection Conn = new SqlConnection(ConfigurationSettings.AppSettings["DSN"]); // DSN parametresi, web.config dosyasinda, // sekildeki gibi tutulmaktadir. SqlCommand Cmd = new SqlCommand("SP$yetkilendir", Conn); Cmd.CommandType = System.Data.CommandType.StoredProcedure; Cmd.Connection = Conn; Cmd.CommandType = System.Data.CommandType.StoredProcedure; Cmd.Connection = Conn; // Parameter : @KullaniciKod Cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("@KullaniciKod", System.Data.SqlDbType.Int, 4)); Cmd.Parameters["@KullaniciKod"].Direction = System.Data.ParameterDirection.ReturnValue; // Parameter : @kullaniciAd Cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("@kullaniciAd", System.Data.SqlDbType.VarChar, 50)); Cmd.Parameters["@kullaniciAd"].Direction = System.Data.ParameterDirection.Input; Cmd.Parameters["@kullaniciAd"].Value = kullaniciAd; // Parameter : @sifre Cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sifre", System.Data.SqlDbType.VarChar, 50)); Cmd.Parameters["@sifre"].Direction = System.Data.ParameterDirection.Input; Cmd.Parameters["@sifre"].Value = sifre; // Parameter : @yetkiSeviye Cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("@yetkiSeviye", System.Data.SqlDbType.Int, 4)); Cmd.Parameters["@yetkiSeviye"].Direction = System.Data.ParameterDirection.Output; // Parameter : @sonGirisTarih Cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("@sonGirisTarih",

System.Data.SqlDbType.VarChar, 12)); Cmd.Parameters["@sonGirisTarih"].Direction = System.Data.ParameterDirection.Output; // Parameter : @isim Cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("@isim", System.Data.SqlDbType.VarChar, 30)); Cmd.Parameters["@isim"].Direction = System.Data.ParameterDirection.Output; Conn.Open(); Cmd.ExecuteNonQuery(); string KullaniciKod= Cmd.Parameters["@KullaniciKod"].Value.ToString(); if (KullaniciKod=="0") { Conn.Close(); return null; } else { YetkilendirmeBilgi YetkiBilgileri= new YetkilendirmeBilgi(); YetkiBilgileri.kullaniciKod = Cmd.Parameters["@KullaniciKod"].Value.ToString(); YetkiBilgileri.isim=Cmd.Parameters["@isim"].Value.ToString(); YetkiBilgileri.sonGirisTarih = Cmd.Parameters["@sonGirisTarih"].Value.ToString(); Conn.Close(); return YetkiBilgileri; } } public string KullaniciEkle(KullaniciDetay yeniKullanici) { /* Kullanici bilgilerini yeniKullanici nüshasindan al * Command nesnesine aktar * ifadeyi calistir. * neticesini cagrildigi yere dondur. * */ return null; } } }

KullaniciDetay ve YetkilendirmeBilgi sınıfları, veri kılıflaması (encapsulation) için kullanılmaktadır. Amaç, verilerin belli bir formatta bütün olarak aktarılabilmesini sağlamaktır. Bu amaç için, C#'ta struct tipi değişkenler de kullanılabilirdi ancak, biz class'larla bu işi gerçekleştirdik. İpucu: struct, küçük veri yapılarının aktarılmasında daha başarılıdır. Class ise referans bazlı çalıştığından, büyük verileri aktarırken tercih edilebilir bir kılıflama (encapsulation) yöntemidir.

KullaniciVT sınıfının birçok metodu yeralmakta. Çünkü, ele aldığımız örnekte, bütün işlemler bu sınıfa düşmekte. Ancak sınıfın sadece örnekte gerekli olan kısımlarını gerçekledik, diğer taraflarını basitliği korumak için gerçeklemedik. Bu metodlardan, KullaniciYetkilendir, dışarıdan aldığı iki parametreyi kullanarak, SP$yetkilendir stored procedure'üne erişip veri katmanı ile bilgileri kontrol edebilmektedir. Sonuçta, yetkilendirme işlemi olursa, YetkilendirmeBilgi sınıfından bir nüsha alınmakta ve bu nüshaya doldurulan veriler (encapsulation) çağrıldığı yere, gönderilmektedir. Yetkilendirme olmazsa, null değeri gönderilmektedir. Peki bu sınıf veritabanına hangi bağlantı ifadesini kullanarak erişmektedir? web.config dosyasına DSN adı ile bir bağlantı ifadesi şu şekilde eklenebilir: Şekil B.13: Veritabanı erişimi ile ilgili ifadelerin, XML formatında uygulamanın konfigürasyon bilgilerini tutan web.config dosyasına eklenmesi

web.config' de yer alan bu bilgiler, aşağıdaki şekilde erişilip kullanılmaktadır. SqlConnection Conn = new SqlConnection(ConfigurationSettings.AppSettings["DSN"]);

Ancak, bu ifadeleri kullanabilmek için, ilgili isimuzaylarını (namespace) yüklemeniz gerekir. Bu işten sorumlu içerik, using System.Configuration; deyimi ile, ilgili temel sınıfların (base class) bulunduğu kütüphaneyi projemizin parçası haline getirdik. Büyük uygulamalarda, iş katmanı, içerisinde yer verilen bütün sınıflar ve methodlarla kendi başına bir proje olarak ele alınır ve bir dll haline getirilerek kullanılır. Bütün iş katmanını içine alan bu yapıya, Application Framework (Uygulama Omurgası) adı verilir. Sunum Katmanını Tasarlamak

Bu sınıfları gerçekledikten sonra, iş katmanındaki işlemlerimizi bitirmiş olduk. Sıra geldi, sunum katmanına. Sunum katmanında, bu iki dosyada yer alan sınıfları türetip kullanacağız. Bu türetme işleminin, bir önceki ASP örneğinde Connection nesnesini kullanmaktan bir farkı bulunmamakta. Büyük uygulamalarda, iş katmanı, içerisinde yer verilen bütün sınıflar ve methodlarla, namespace yardımıyla da düzenlenerek, kendi başına bir proje olarak ele alınır ve bir dll haline getirilerek kullanılır. Bütün iş katmanını içine alan bu yapıya, Application Framework (Uygulama Omurgası) adı verilir. Ardından, kullanıcıdan verileri alabilecek ve verilerinin doğru olup olmadığını ona aktarabilecek bir aspx dosyasını aşağıdaki şekilde tasarlayalım.

Şekil B.14: Yeni bir Form başlatıp adını, login.aspx olarak değiştirelim ve bir Label, iki Textbox ve bir adet de Button nesnesi ekleyelim.

Üye Giriş Sayfası
Kullanici Adiniz
Sifreniz

Codebehind dosyası, Visual Studio tarafından otomatik oluşturulmakta. Bizim tüm yapmamız gereken, butonu çift tıklamak ve içerisine şu kodları eklemek: // yetkilendirmeyi yapacak sinifi olusturalim Verivizyon.KullaniciVT kullanici = new KullaniciVT(); //yetkilendirme sonucunu encapsulate edecek sinifi //olusturalim Verivizyon.YetkilendirmeBilgi yetkilendirmeBilgi= new YetkilendirmeBilgi(); //YetkilendirmeBilgi.KullaniciYetkilendir() metodunu // nasıl çağırdığımıza ve // Şifrenin nasıl özetinin çekildiğine dikkat edin... yetkilendirmeBilgi = kullanici.KullaniciYetkilendir(TxtKullanici.Text, Verivizyon.Bilesenler.Guvenlik.OzetCek(TxtSifre.Text)); if(yetkilendirmeBilgi != null){ LblSonuc.Text = "Merhaba " + yetkilendirmeBilgi.isim +

" sitemize en son " + yetkilendirmeBilgi.sonGirisTarih + " tarihinde giris yaptiniz" ; }

Görüldüğü üzere, veritabanına doğrudan bir erişim yer almamaktadır. Sadece, bir alt katmanda oluşturulan sınıflar nüshalandırıldı (instantaiting) ve bu nesneler kullanıldı. Uygulama ilk başladığında, ilgili mesajı kullanıcılarımıza göstermek için, şu kodu da ekleyebiliriz: private void Page_Load(object sender, System.EventArgs e) { if ( !IsPostBack) LblSonuc.Text = "Lütfen Kullanici adi ve sifrenizi giriniz."; } Şekil B.15: Projenin çalışan hali