Arşiv

Aralık 2009 ayı için arşiv

Sql Server İstatistiklerin Son Güncellenme Zamanını Bulma

İstatistikler, verinin veritabanı nesnelerinde (tablo, index, vs) nasıl dağıldığı bilgisini içerdiği için, veritabanında en önemli nesnelerden biridir.

Veritabanı Yöneticilerinin (Database Administrators) eskimiş istatistiklerden şikayetlerini çok duydum. Yapılması gereken istatistikleri güncellemektir.

Eğer bir tablodaki indexlere ait istatistiklerin son güncelleme zamanını öğrenmek istiyorsanız, aşağıdaki sql sorgusunu çalıştırabilirsiniz;

SELECT
	NAME AS INDEX_ADI,
	STATS_DATE(OBJECT_ID, INDEX_ID) AS SON_GUNCELLEME
FROM
	SYS.INDEXES
WHERE
	OBJECT_ID = OBJECT_ID('HumanResources.Department')

Eğer tüm indexler için aynı bilgiyi almak istiyorsanız, WHERE filtresini kaldırmanız yeterli.

Benim test bilgisayarımda aldığım sonuç;

PK_Department_DepartmenID 2010-01-20 15:30:17:200
AK_Department_Name 2010-01-20 15:30:26:053

Eğer istatistiklerinizin zamanı geçmiş olduğunu düşünüyorsanız ve güncellemek istiyorsanız, aşağıdaki sql komutunu çalıştırabilirsiniz;

UPDATE STATISTICS HumanResources.Department

Bu komutu çalıştırdığınızda ilgili tablonun istatistik verilerinin güncellendiğini göreceksiniz. Eğer komutu WITH FULLSCAN komutu ile birlikte çalıştırırsanız, tablodaki tüm kayıtlar baştan taranacak ve istatistik baştan oluşturulacaktır.

UPDATE STATISTICS HumanResources.Department
WITH FULLSCAN

Dikkat : İstatistikleri güncellemek için yukarıdaki komutları, sadece istatistiklerinizin gerçekten eskimiş olduğuna inandığınızda, sorgularınız için oluşturulan query plan’ların hatalı olduğunu gördüğünüzde ve sql server’ın yoğun olmadığı zamanlarda çalıştırmanızı tavsiye ederim.

Kendi test bilgisayarımda, istatistik güncelleme komutunu çalıştırdıktan sonra, aşağıdaki sonuçları elde ettim;

PK_Department_DepartmenID 2010-01-30 21:10:06:560
AK_Department_Name 2010-01-30 21:10:06:594

Gördüğünüz gibi, ilgili tablodaki indexlerin istatistikleri güncellendi.

LINQ ile Dosya Sisteminde Sorgulama Yapmak

Yazdığımız uygulamalarda dosya sisteminde arama yapmaya ihtiyaç duyabiliriz. Bu durumda genelde döngüler yazarız. Peki LINQ kullanamaz mıyız? Üzerinde biraz çalıştıktan ve araştırma yaptıktan sonra, aşağıdaki kodu elde ettim;

private IEnumerable<FileInfo> DosyaListesi(DirectoryInfo KokDizin, string Uzanti, string DosyaAdiParcasi)
{
	foreach (var f in KokDizin.GetFiles().Where(Dosya => Dosya.Extension == Uzanti && Dosya.Name.Contains(DosyaAdiParcasi)))
		yield return f;

	foreach (DirectoryInfo d in KokDizin.GetDirectories())
		foreach (var f in DosyaListesi(d, Uzanti, DosyaAdiParcasi))
			yield return f;
}

Gördüğünüz gibi fonksiyon, parametre olarak aramaya başlanacak kök dizini, arama yapılacak dosyanın uzantısını ve dosya isminde bir parçayı alıyor, geriye FileInfo sınıfında liste döndürüyor.

İlk foreach döngüsünde kök dizinde parametre ile alınmış kriterlere göre arama yapılıyor ve yield anahtar kelimesi ile, bulunan her sonuç dönüş kümesine ekleniyor.

İkinci (ve üçüncü) foreach döngülerinde ise, recursive (kendini tekrarlamalı) olarak DosyaListesi() fonksiyonu kendi kendisini çağırarak, arama işlemini alt klasörlerde de yapıyor.

Dosyanın UTF8 Formatında Olduğunu Belirleme

BilgeAdam‘da bir öğrencimin sorusu üzerine, bir dosyanın UTF8 formatında olup olmadığını belirlemek için programatik yollar düşünmeye başlamıştım.

System.Text namespace’inde bulunan Encoding class‘ını incelerken şöyle bir yol buldum;

public static bool IsUtf8(string DosyaAdi)
{
	using (var Dosya = File.Open(DosyaAdi, FileMode.Open))
	{
		byte[] pre = Encoding.UTF8.GetPreamble();
		var b = new byte[pre.Length];
		Dosya.Read(b, 0, b.Length);
		return b.SequenceEqual(pre);
	}
}

Sadece dosyanın UTF8 olduğunu kontrol etmek için biraz hantal bir yol gibi gözüküyor değil mi?

Daha iyi bir yöntem öneriniz varsa, yorumlarınızı bekliyorum.

Sql Server SET NOCOUNT komutu

Sql Server‘da her sorgu çalıştırdığımızda, sorgu sonucu, etkilenen satır sayısı ile birlikte, sorguyu çalıştıran uygulamaya geri gönderilir.

Bazı durumlarda bu bilgi işimize yarasa bile, genellikle kullanmayız.

Sql Server‘ın bu bilgiyi hesaplamasını ve uygulamaya geri göndermesini engelleyerek, çok ufakta olsa kazanç sağlayabiliriz.

Yapmamız gereken, sorgudan önce aşağıdaki komutu çalıştırmak olacaktır;

SET NOCOUNT ON

Sql Server sadece ilgili sorgu için, etkilenen satır sayısını hesaplama işlemini yapmayacaktır.

Birkaç Faydalı Extension Method

C# 3.0 ile gelen Extension Method desteğini çok kullanışlı bulduğum için, kendi uygulamalarımda kullanmaya çalışıyorum. Bu yazımda faydalı bulduğum birkaç Extension Method‘u toplamaya çalıştım.

Metni Encrypt – Decrypt Eden Extension Method

namespace ExtensionManager
{
	public static class Extensions
	{
		public static string Encrypt(this string stringToEncrypt, string key)
		{
			if (string.IsNullOrEmpty(stringToEncrypt))
				throw new ArgumentException("Boş metin şifrelenemez");
			if (string.IsNullOrEmpty(key))
				throw new ArgumentException("Şifreleme için anahtar vermelisiniz");

			CspParameters cspp = new CspParameters();
			cspp.KeyContainerName = key;

			RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cspp);
			rsa.PersistKeyInCsp = true;

			byte[] bytes = rsa.Encrypt(UTF8Encoding.UTF8.GetBytes(stringToEncrypt), true);

			return BitConverter.ToString(bytes);
		}

		public static string Decrypt(this string stringToDecrypt, string key)
		{
			if (string.IsNullOrEmpty(stringToDecrypt))
				throw new ArgumentException("Beş metnin şifresi çözülemez");
			if (string.IsNullOrEmpty(key))
				throw new ArgumentException("Şifre çözme için anahtar vermelisiniz");
			string result = null;
			try
			{
				CspParameters cspp = new CspParameters();
				cspp.KeyContainerName = key;

				RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cspp);
				rsa.PersistKeyInCsp = true;

				string[] decryptArray = stringToDecrypt.Split("-");
				byte[] decryptByteArray = Array.ConvertAll<string, byte>(decryptArray, (s => Convert.ToByte(byte.Parse(s, NumberStyles.HexNumber))));
				byte[] bytes = rsa.Decrypt(decryptByteArray, true);

				result = UTF8Encoding.UTF8.GetString(bytes);
			}
			catch()
			{
			}
			return result;
		}
	}
}

Örnek kullanım;

string metin = "My Secret";
string sifrelenmis = metin.Encrypt("sifreleyici");
string sifresicozulmus = sifrelenmis.Decrypt("sifreleyici");

Listeler üzerinde Foreach Yapan Extension Method

namespace ExtensionManager
{
	public static class Extensions
	{
		public static void ForEach<T>(this IEnumerable<T> kaynak, Action<T> islem)
		{
			foreach (var item in kaynak)
				islem(item);
		}
	}
}

Örnek kullanım;

List<string> isimler = new List<string> { "engin", "polat", "ahmet", "mehmet", "murat" };
isimler.ForEach(isim => MessageBox.Show(isim));

İnternet Adresi Doğrulayan Extension Method

namespace ExtensionManager
{
	public static class Extensions
	{
		public static bool IsValidUrl(this string text)
		{
			Regex r = new Regex(@"http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?");
			return r.IsMatch(text);
		}
	}
}

Örnek kullanım;

string Adres = "http://www.enginpolat.com";
bool AdresDogru = Adres.IsValidUrl();

Email Adresi Doğrulayan Extension Method

namespace ExtensionManager
{
	public static class Extensions
	{
		public static bool IsValidEmail(this string text)
		{
			Regex r = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");
			return r.IsMatch(text);
		}
	}
}

Örnek kullanım;

string Adres = "test@test.com";
bool AdresDogru = Adres.IsValidEmail();

Klasör Oluşturan Extension Method

namespace ExtensionManager
{
	public static class Extensions
	{
		public static void CreateDirectory(this DirectoryInfo dirInfo)
		{
			if (dirInfo.Parent != null)
				CreateDirectory(dirInfo.Parent);
			if (!dirInfo.Exists)
				dirInfo.Create();
		}
	}
}

Örnek kullanım;

var dir = new DirectoryInfo(@"C:\temp\bir\iki\uc");
dir.CreateDirectory();

Tarihin Haftasonuna Geldiğini Doğrulayan Extension Method

namespace ExtensionManager
{
	public static class Extensions
	{
		public static bool IsWeekend(this DateTime value)
		{
			return (value.DayOfWeek == DayOfWeek.Sunday || value.DayOfWeek == DayOfWeek.Saturday);
		}
	}
}

Örnek kullanım;

for (DateTime date = BaslangicTarih; date <= BitisTarih; date = date.AddDays(1))
{
	if (date.IsWeekend())
		continue;
	/// Tatil değil, yapılacak işler yapılır.
}

Object türünden JSON’a, JSON’dan Object Türüne Çeviren Extension Method

namespace ExtensionManager
{
	public static class Extensions
	{
		public static string ToJson(this object obj)
		{
			JavaScriptSerializer serializer = new JavaScriptSerializer();
			return serializer.Serialize(obj);
		}

		public static T FromJson<T>(this object obj)
		{
			JavaScriptSerializer serializer = new JavaScriptSerializer();
			return serializer.Deserialize<T>(obj as string);
		}
	}
}

Örnek kullanım;

Personel Engin = new Personel("Engin Polat", 5000);
string json = Engin.ToJson();
Engin = null;
Engin = s.FromJson<Personel>();

Tarihten Yaş Hesaplayan Extension Method

namespace ExtensionManager
{
	public static class Extensions
	{
		public static int Age(this DateTime tarih)
		{
			DateTime now = DateTime.Now;
			int yas = now.Year - tarih.Year;
			if (now < tarih.AddYears(yas))
				yas--;
			return yas;
		}
	}
}

Örnek kullanım;

DateTime AhmetDogumGunu = new DateTime(1979, 05, 07);
int Yas = AhmetDogumGunu.Age();

Ayın İlk Gününü – Ayın Son Gününü Bulan Extension Method

namespace ExtensionManager
{
	public static class Extensions
	{
		public static DateTime FirstDayOfMonth(this DateTime date)
		{
			return new DateTime(date.Year, date.Month, 1);
		}

		public static DateTime LastDayOfMonth(this DateTime date)
		{
			return new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1).AddMonths(1).AddDays(-1);
		}
	}
}

Örnek kullanım;

DateTime Simdi = DateTime.Now;
MessageBox.Show("Ayın ilk günü: " + Simdi.FirstDayOfMonth().ToShortDateString());
MessageBox.Show("Ayın son günü: " + Simdi.LastDayOfMonth().ToShortDateString());

Dosyanın MD5 Değerini Hesaplayan Extension Method

namespace ExtensionManager
{
	public static class Extensions
	{
		public static string GetMD5(this string filename)
		{
			string result = string.Empty;

			try
			{
				MD5CryptoServiceProvider md5Provider = new MD5CryptoServiceProvider();
				FileStream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
				byte[] arrByteHashValue = md5Provider.ComputeHash(fileStream);
				fileStream.Close();

				string hashData = BitConverter.ToString(arrByteHashValue).Replace("-", "");
				result = hashData;
			}
			catch()
			{
			}
			return result.ToLower();
		}
	}
}

Örnek kullanım;

string Dosya = @"C:\Temp\DosyaAdi.txt";
MessageBox.Show(Dosya + " dosyasının MD5 değeri: " + Dosya.GetMD5());

LINQ ile Bilgisayara Kurulu Programları Sorgulamak

30 Ocak 2010 1 yorum

Bu yazımda, bilgisayarda kurulu programları, LINQ kullanarak nasıl sorgulayacağımızı inceleyeceğim.

Bilgisayara kurduğumuz her program bilgisayarın Registry‘sine kurulum ile ilgili bir kayıt bırakır.

Bilmeyenler için not: Registry’yi açmak için komut satırına regedit yazmanız yeterli.

Registry ile ilgili daha detaylı bilgi için MSDN: Registry sayfasına bakmanızı tavsiye ederim.

Registry‘nin LocalMachine\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall dalında, bilgisayara kurulu olan uygulamalara ve bu uygulamalar ile ilgili bilgilere ulaşılabilir.

Uygulamamızda, Registry class’ını kullanabilmek için, kodumuza aşağıdaki namespace’i eklememiz gerekiyor.

using Microsoft.Win32;

Hemen form tasarlamakla başlayalım.

Bilgisayarda Kurulu Programlar Ana Form Ekran Görüntüsü

Yukarıdaki formu oluşturduktan sonra btnGetir butonunun Click olayına aşağıdaki kodu yazalım;

using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"))
{
	var programlar = from k in rk.GetSubKeyNames()
					 let r = rk.OpenSubKey(k)
					 select r.GetValue("DisplayName");

	foreach (string program in programlar)
		if (!string.IsNullOrWhiteSpace(program))
			lbProgramlar.Items.Add(program);
}

Registry sınıfının static LocalMachine üyesi üzerinden OpenSubKey() method’unu çağırıyoruz.

Daha sonra yaptığımız;

from k in rk.GetSubKeyNames()
let r = rk.OpenSubKey(k)
select r.GetValue("DisplayName");

LINQ sorgusunu yazmak oluyor. Bu sorgu, ilgili Registry dalının altında bulunan tüm dalları açar ve içlerindeki DisplayName anahtarının değerini getirir.

Son olarak basit bir foreach döngüsü ile bu değerleri lbProgramlar Listbox kontrolüne dolduruyoruz.

İşte benim bilgisayarımdaki sonuç;

Bilgisayarda Kurulu Programlar Çalıştıktan Sonraki Ekran Görüntüsü

Sql Server Where ve Join Filtreleri Karşılaştırma

26 Ocak 2010 1 yorum

Bilgisayar Programcılığı dünyasında genellikle bir işi yapmanın birden fazla yolu vardır. Özellikle veritabanı tarafında bu yollar daha da fazla olabilir. Uygulama Geliştiriciler olarak yeni bir kod parçası yazacağımız zaman, genellikle bu yollardan en çok kullandığımız bir tanesini seçeriz ve onu kullanırız.

Ne var ki, seçtiğimiz yol herzaman en doğru yol olmayabilir. Sql‘de join‘li sorgulama yaparken, filtreleme için kullandığımız yöntem de bunlardan biri olabilir.

Bu yazımda join‘li sorgularda filtreleme için seçebileceğimiz iki yolu karşılaştıracağım. Karşılaştırma yapmak için kendi bilgisayarımda kurulu olan Sql Server 2008 üzerinde AdventureWorkd2008 R2 veritabanını kullandım.

Eğer birden fazla tablonun join‘ler ile ilişkilendirildiği bir sorgu yazıyorsak, sonuç kümesini filtreleme için kullanabileceğimiz iki yöntem vardır;

WHERE Filtreleme

Sonuç kümesinde olmasını/olmamasını istediğimiz kayıtları sorgunun WHERE cümlesinde tanımlarız. Şablon;

SELECT
	[TABLE1.ALANADLARI],
	[TABLE2.ALANADLARI]
FROM
	[TABLE1]
	JOIN [TABLE2] ON [TABLE1.ALAN1] = [TABLE2.ALAN1]
WHERE
	[TABLE1.ALAN2] = DEGER AND
	[TABLE2.ALAN2] = DEGER

JOIN Filtreleme

Sonuç kümesinde olmasını/olmamasını istediğimiz kayıtları sorgunun JOIN cümlelerinde tanımlarız. Şablon;

SELECT
	[TABLE1.ALANADLARI],
	[TABLE2.ALANADLARI]
FROM
	[TABLE1]
	JOIN [TABLE2] ON [TABLE1.ALAN1] = [TABLE2.ALAN1] AND [TABLE1.ALAN2] = DEGER AND [TABLE2.ALAN2] = DEGER

AdventureWorks2008 R2 veritabanında aşağıdaki iki sorguyu çalıştırdıktan sonra, performans analizini yapalım;

WHERE Filtreleme

SELECT
	*
FROM
	Sales.SalesOrderHeader AS SOH WITH (NOLOCK)
	JOIN Sales.SalesOrderDetail AS SOD WITH (NOLOCK) ON SOH.SalesOrderID = SOD.SalesOrderID
	JOIN Sales.SalesOrderHeaderSalesReason AS SOHSR WITH (NOLOCK) ON SOHSR.SalesOrderID = SOH.SalesOrderID
	JOIN Sales.SalesReason AS SR WITH (NOLOCK) ON SOHSR.SalesReasonID = SR.SalesReasonID
WHERE
	SOH.CustomerID > 15000 AND
	SOD.LineTotal > 2000 AND
	SR.SalesReasonID > 5

JOIN Filtreleme

SELECT
	*
FROM
	Sales.SalesOrderHeader AS SOH WITH (NOLOCK)
	JOIN Sales.SalesOrderDetail AS SOD WITH (NOLOCK) ON SOH.SalesOrderID = SOD.SalesOrderID AND SOH.CustomerID > 15000 AND SOD.LineTotal > 2000
	JOIN Sales.SalesOrderHeaderSalesReason AS SOHSR WITH (NOLOCK) ON SOHSR.SalesOrderID = SOH.SalesOrderID
	JOIN Sales.SalesReason AS SR WITH (NOLOCK) ON SOHSR.SalesReasonID = SR.SalesReasonID AND SR.SalesReasonID > 5

Her iki sorgu da çalıştıktan sonra 1321 satır geri döndürdü. Performans incelemesini üç alanda yapacağız;

  • CPU Kullanımı
  • Disk Kullanımı
  • Hafıza Kullanımı

Bu üç performans kriteri için verileri  SYS.SYSPROCESSES DMV‘sinden alabiliriz. SYS.SYSPROCESSES için detaylı bilgiye MSDN‘deki şu makaleden ulaşabilirsiniz. Aşağıda ilgili üç performans kriteri için verileri elde edebileceğimiz sorgu var;

SELECT
	DB_NAME(SP.DBID) AS VERITABANI,
	EST.TEXT AS SORGU,
	CPU,
	PHYSICAL_IO AS DISK_OKUMA,
	MEMUSAGE AS HAFIZA_KULLANIM
FROM
	SYS.SYSPROCESSES AS SP
	CROSS APPLY SYS.DM_EXEC_SQL_TEXT(SP.SQL_HANDLE) AS EST

Bu sorguyu çalıştırdıktan sonra benim test bilgisayarımda şu verileri elde ettim;

  • VERITABANI : AdventureWorks2008R2
  • CPU : 1965
  • DISK_OKUMA : 211
  • HAFIZA_KULLANIM : 2
  • VERITABANI : AdventureWorks2008R2
  • CPU : 156
  • DISK_OKUMA : 0
  • HAFIZA_KULLANIM : 2

Gördüğünüz gibi kaynak kullanımı açısından (özellikle Disk Okuma ve CPU Kullanımı açısından) JOIN Filtreleme, WHERE Filtrelemeden daha avantajlı.

JOIN Filtreleme tabloları eşlerken filtrelenmiş verileri kullanıyor, WHERE Filtreleme ise, önce tabloları eşleştiriyor sonra filtreliyor. Özellikle çok kayıt bulunan/bulunacak olan tablolarınıza sorgu yazarken, JOIN Filtreleme’yi kullanmanızı tavsiye ederim.

C# Speech Synthesis – Konuşma Sentezleyici

Eğer uygulamanızda, Text-to-Speech (TTS) yeteneği olmasını istiyorsanız, .Net Framework içerisinde bunu yapmanızı sağlayacak sınıflar mevcut.

Projenizde Text-To-Speech sınıflarını kullanabilmek için, System.Speech.Synthesis namespace’indeki sınıflara erişebilmeniz gereklidir.

System.Speech.Synthesis namespace’ine, projenize System.Speech.dll assembly’yi referans olarak ekleyerek ulaşabilirsiniz.

Text-to-Speech kullanmak istediğiniz sınıfın bulunduğu kod dosyasının en üstünde bulunan using‘leri yazdığınız kısma;

using System.Speech.Synthesis;

satırını ekleyiniz. Daha sonra yapmanız gereken, kodunuzun ilgili yerine aşağıdaki kod parçasını eklemek;

SpeechSynthesizer ss = new SpeechSynthesizer();
ss.Speak("Konuşmaya çevrilecek ve hoparlörden duyulacak metin");

Uygulama çalışırken, ilgili satıra geldiğinde, önce Speak() methoduna parametre olarak verdiğiniz string hoparlörden duyulur, sonra bir sonraki satırdan çalışmaya devam eder.

Eğer metnin konuşmaya çevrilme işleminin uzun sürdüğünü ve uygulamanıza “yavaşlık” kattığını düşünüyorsanız, aynı kodu aşağıdaki gibi yazabilirsiniz;

SpeechSynthesizer ss = new SpeechSynthesizer();
ss.SpeakAsync("Konuşmaya çevrilecek ve hoparlörden duyulacak metin");

SpeakAsync() method’u sayesinde, uygulama metnin konuşmaya çevrilmesi ve ses kartı aracılığıyla yayınlanması işlemini ayrı bir iş parçacığında gerçekleştirir.

Not : Speak() method’u ingilizce kelimeleri doğru olarak okuyor, fakat türkçe kelimeleri okuyamıyor.

C# String’i Title Case’e (Kelimelerin Baş Harfleri Büyük Gerisi Küçük) Çevirme

25 Ocak 2010 6 yorum

.Net Framework içerisinde string.ToUpper() ve string.ToLower() fonksiyonlarını kullanarak string değişkenin içeriğini BÜYÜK ve küçük harfe çevirebiliyoruz.

Metni Upper Case (Tümü Büyük Harfler) ve Lower Case (Tümü Küçük Harfler) formatlamak haricinde Title Case (Kelimelerin Baş Harfleri Büyük Gerisi Küçük) çevirmek yaygın kullanılan başka bir formattır.

string sınıfına bir Extension Method yazarak bu özelliğe sahip olmasını sağlayabiliriz. Extension Method‘u yazarken bilmemiz gereken ilk şey, ToTitleCase() method’unun TextInfo sınıfında yer aldığıdır. TextInfo sınıfına CultureInfo sınıfının CurrentCulture özelliğinden ulaşabiliriz.

public static class ExtensionManager
{
	public static string ToTitleCase(this string Text)
	{
		return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(Text);
	}
}

Artık projemiz içerisinde herhangi bir yerde string değişkenimizin ToTitleCase() method’unu kullanabiliriz.

private void frmMain_Load(object sender, EventArgs e)
{
	string AdSoyad = "engin polat".ToTitleCase();
}

Yukarıdaki kod parçası çalıştığı zaman, AdSoyad değişkeninin içeriği Engin Polat olacaktır.

Not : CultureInfo sınıfının CurrentCulture özelliğinden TextInfo sınıfının özelliklerine eriştiğimiz için, bilgisayarınızda kullandığınız dile göre sonuç farklılık gösterebilir.

C# Try-Catch-Finally Kod Bloğu Hakkında

24 Ocak 2010 2 yorum

Geçen aylarda BilgeAdam‘da bir öğrencimin sorduğu soru geldi aklıma. Soru aslında çok basit, fakat farkettim ki, internette bu konu ile ilgili pek kaynak yok. Bari kendim yazayım dedim. Soru şu;

Bir method'dan geriye true/false değer döndürmem lazım.

Method içerisine yazdığım kodları try-catch-finally blokları içerisine almam gerekiyor.

Eğer try içerisinde hata oluşmazsa, geriye true değer döndüreceğim. Hata oluşur ve kod catch bloğuna girerse false değer döndüreceğim.

Ama finally kod bloğunda yapmam gereken işler var. (Açık bağlantıları kapatmak, vs.)

Eğer try-catch içerisinde return ifadesini kullanırsam, finally bloğundaki kod çalışır mı?

Sorunun doğru cevabı için; Devamını oku…

Sql Server Query Plan Cache

Sql Server‘da çalıştırılan her sorgu, aslında çalıştırılmadan önce derleme (compile) işlemine tabi tutulur. Bu derleme işlemi sonucunda Sql Server query plan denilen çıktıyı üretir.

Query Plan, query processor‘e (sorguyu işleyen birim), sorgunun ihtiyaç duyduğu veriler için veritabanında bulunan tablo ve index‘lere fiziksel olarak nasıl erişebileceğini söyler.

Fakat, query plan elde etmek için yapılan bu derleme işlemi, bazı sorgular için çok pahalı olabilir.

Sql Server, aynı sql sorgusunun defalarca çalıştırıldığı durumlarda, derleme işleminin yükünü azaltmak için, query plan cache denilen hafıza bölgesinde query plan‘ları önbellekler.

Query plan cache, önbellekleyeceği sorguları basit bir hash tablo‘da saklar. Hash tablo’nun iki alanı vardır, birinde sql sorgusunun kendisini, diğerinde derleme sonucu ortaya çıkan query plan‘ı saklar.

Sql Server, yeni bir sorgu çalıştıracağı zaman, ilk önce query plan cache‘te sorgunun query plan‘ı var mı diye bakar. Eğer bulursa, daha önce önbelleklenmiş bu query plan‘ı kullanır. Bulamazsa, ilk önce sorguyu yazım denetimine tabi tutar, sonra sorguyu derler ve oluşan query plan‘ı bu listeye ekler.

Query plan cache‘in getirdiği performans artışını ölçmek için, öncelikle Sql Server’daki query plan cache’i boşaltacağız;

DBCC FREEPROCCACHE

Dikkat : Bu komutu kullandığınızda, Sql Server üzerinde bulunan tüm query plan cache silinir. Veritabanı veya belli bir sorgu için temizleme mümkün değildir. Bu komutun Canlı Veritabanında (Production Database) kullanılmaması önerilir.

Şimdi Sql Server’ın sorguyu inceleme ve derleme işlemi için ne kadar vakit harcadığını bulmamız lazım. Yapmamız gereken, sorguyu çalıştırmadan önce aşağıdaki komutu çalıştırmak;

SET STATISTICS TIME ON

Şimdi sorgumuzu çalıştırabiliriz. Ben Sql Server 2008 için hazırlanmış örnek veritabanında (AdventureWorks 2008R2) aşağıdaki sorguyu çalıştırıyorum;

SELECT * FROM HumanResources.Employee WHERE BusinessEntityId IN (1, 2);

Sorgu sonucu;

SQL Server parse and compile time:
	CPU time = 0 ms, elapsed time = 12 ms.

(2 row(s) affected)

SQL Server Execution Times:
	CPU time = 0 ms,  elapsed time = 1 ms.

Gördüğünüz gibi, sorgunun parse ve compile işlemine tabi tutulması 12 ms. sürdü. Sorgunun çalıştırılması ise 1 ms. sürdü.

Aynı sorguyu tekrar çalıştırırsak, elde edeceğimiz sonuç;

SQL Server parse and compile time:
	CPU time = 0 ms, elapsed time = 1 ms.

(2 row(s) affected)

SQL Server Execution Times:
	CPU time = 0 ms,  elapsed time = 1 ms.

Query plan cache sayesinde, parse ve compile işlemi 12 ms. yerine 1 ms. sürdü.

Not : Sql Server’ın istatistik toplarken ulaşabileceği en düşük kesinlik süresi 1 ms.’dir. 1 ms.’den kısa süren işler için bile Sql Server 1 ms. raporlar.

C# İsimsiz Tipler – Anonymous Types

Sadece bir method içerisinde kullanacağınız basit class‘lara kaç defa ihtiyaç duydunuz? Böyle bir class’a her ihtiyaç duyduğunuzda yapmanız gereken, yeni bir class oluşturup, bütün tanımlama kodlarını (private alanlar, public alanlar, vs.) yazmaktır.

C# dili, İsimsiz Tip (Anonymous Type) tanımlamaya izin veriyor. Üstelik, private ve public öğelerin oluşturulmasını ve yönetilmesini C# dilinin kendisi üstleniyor.

public class HataLogla()
{
	var Hata = new { Mesaj = "Hata Mesajının Kendisi", Tarih = DateTime.Now, Yer = "XClass.YMethod" };
	var Kullanici = new { Id = 42, AdSoyad = "Engin Polat", Yonetici = "Ali Veli" };

	string Loglanacak = string.Format("Hata Mesajı: {0}\nTarih: {1} {2}\nHatanın Oluştuğu Yer: {3}\n\nKullanıcıId: {4}\nKullanıcı Adı: {5}\nYöneticisi: {6}", Hata.Mesaj, Hata.ToShortDateString(), Hata.Tarih.ToLongTimeString(), Hata.Yer, Kullanici.Id, Kullanici.AdSoyad, Kullanici.Yonetici);

	File.WriteAllText(Loglanacak, @"C:\Log\Log.txt");

	MessageBox.Show(Hata.Mesaj, Hata.Tarih);
}

Yukarıdaki kodda yer alan Hata ve Kullanici değişkenlerinin tipleri, derleme zamanında derleyici tarafından otomatik oluşturulur. Eğer method‘un içerisine aşağıdaki kodları eklersek;

MessageBox.Show("Hata değişkeninin tipi : " + Hata.GetType().ToString());
MessageBox.Show("Kullanici değişkeninin tipi : " + Kullanici.GetType().ToString());

Mesaj kutularında şu değerleri görürüz;

Hata değişkeninin tipi : <>f_AnonymousType0’3[System.String,System.DateTime,System.String]
Kullanici değişkeninin tipi : <>f_AnonymousType0’3[System.Int32,System.String,System.String]

Farkettiğiniz gibi, böyle bir tip oluşturmaya çalışırsak, derleme zamanında hata alırız, ama C# derleyicisi bizim için bu tipleri oluşturuyor.

Değişkenin tipini veremeyeceğimiz için, C# diline var anahtar kelimesi eklenmiştir.

var anahtar kelimesi sayesinde, değişkenin tipi atandığı değerden otomatik olarak çözümleniyor.

İsimsiz Tipler (Anonymous Types) IDisposable interface’ini uygulamadığı için, Disposable olamazlar.

Eğer yukarıdaki örneği geleneksel kodlama teknikleri ile yazacak olsaydık;

public class HataBilgi
{
	private string _Mesaj = string.Empty;
	private DateTime _Tarih = DateTime.Now;
	private string _Yer = string.Empty;

	public string Mesaj
	{
		get
		{
			return _Mesaj;
		}
		set
		{
			_Mesaj = value;
		}
	}

	public DateTime Tarih
	{
		get
		{
			return _Tarih;
		}
		set
		{
			_Tarih = value;
		}
	}

	public string Yer
	{
		get
		{
			return _Yer;
		}
		set
		{
			_Yer = value;
		}
	}

	public HataBilgi(string Mesaj, DateTime Tarih, string Yer)
	{
		this.Mesaj = Mesaj;
		this.Tarih = Tarih;
		this.Yer = Yer;
	}
}

public class KullaniciBilgi
{
	private int _Id = 0;
	private string _AdSoyad = string.Empty;
	private string _Yonetici = string.Empty;

	public int Id
	{
		get
		{
			return _Id;
		}
		set
		{
			_Id = value;
		}
	}

	public string AdSoyad
	{
		get
		{
			return _AdSoyad;
		}
		set
		{
			_AdSoyad = value;
		}
	}

	public string Yonetici
	{
		get
		{
			return _Yonetici;
		}
		set
		{
			_Yonetici = value;
		}
	}

	public KullaniciBilgi(int Id, string AdSoyad, string Yonetici)
	{
		this.Id = Id;
		this.AdSoyad = AdSoyad;
		this.Yonetici = Yonetici;
	}
}

public class HataLoglama
{
	HataBilgi Hata = new HataBilgi("Hata Mesajının Kendisi", DateTime.Now, "XClass.YMethod");
	KullaniciBilgi Kullanici = new KullaniciBilgi(42, "Engin Polat", "Ali Veli");

	string Loglanacak = string.Format("Hata Mesajı: {0}\nTarih: {1} {2}\nHatanın Oluştuğu Yer: {3}\n\nKullanıcıId: {4}\nKullanıcı Adı: {5}\nYöneticisi: {6}", Hata.Mesaj, Hata.ToShortDateString(), Hata.Tarih.ToLongTimeString(), Hata.Yer, Kullanici.Id, Kullanici.AdSoyad, Kullanici.Yonetici);

	File.WriteAllText(Loglanacak, @"C:\Log\Log.txt");

	MessageBox.Show(Hata.Mesaj, Hata.Tarih);
}

İsimsiz Tip yeteneklerini kullanarak yazdığımızda ise;

public class HataLogla()
{
	var Hata = new { Mesaj = "Hata Mesajının Kendisi", Tarih = DateTime.Now, Yer = "XClass.YMethod" };
	var Kullanici = new { Id = 42, AdSoyad = "Engin Polat", Yonetici = "Ali Veli" };

	string Loglanacak = string.Format("Hata Mesajı: {0}\nTarih: {1} {2}\nHatanın Oluştuğu Yer: {3}\n\nKullanıcıId: {4}\nKullanıcı Adı: {5}\nYöneticisi: {6}", Hata.Mesaj, Hata.ToShortDateString(), Hata.Tarih.ToLongTimeString(), Hata.Yer, Kullanici.Id, Kullanici.AdSoyad, Kullanici.Yonetici);

	File.WriteAllText(Loglanacak, @"C:\Log\Log.txt");

	MessageBox.Show(Hata.Mesaj, Hata.Tarih);
}

Aradaki fark açıkça ortada!

C# 3.0 Object Initializer Özelliği

C# 3.0 object initialize etmek için kodda kısaltma sağlayacak bir yenilik sunuyor. Hemen hergün aşağıdaki gibi kod yazmamız gerekmez mi?

Personel UygulamaGelistirici = new Personel();
UygulamaGelistirici.Id = 42;
UygulamaGelistirici.AdSoyad = "Engin Polat";
UygulamaGelistirici.Birim = "NTV - Yeni Medya";

Personel sınıfından yeni bir örnek oluşturduktan sonra, sınıfın özelliklerinden ihtiyacımız olanlara atamalar yapmaya başlarız.

Eğer Personel sınıfının constructor‘ı olsaydı ve bu özelliklerin atamalarını yapsaydı, tek bir satırda, hem Personel sınıfının yeni bir örneğini UygulamaGelistirici değişkenine oluşturmuş, hem de bazı özelliklerin değerlerini atamış olabilirdik.

Fakat, Personel sınıfının constructor‘ını yazamadığımız durumlarda veya constructor‘da parametreler ile almak için çok fazla özellik olduğu durumlarda bu yöntemi kullanamayız.

C# 3.0 ile birlikte Object Initializer denilen yeni bir kodlama tekniği tanıtıldı.

Hemen yukarıdaki örneği bu teknik ile yazalım;

Personel UygulamaGelistirici = new Personel() { Id = 42, AdSoyad = "Engin Polat", Birim = "NTV - Yeni Medya" };

Gördüğünüz gibi, kodlama bizim için kolaylaşıyor.

Object Initializer içerisinde sadece özelliklere atama yapabiliyoruz, method çağrımı vs. yapılamıyor.

Eğer sınıfın özellikleri başka sınıflardan örnekler istiyorsa, Object Initializer nasıl yazabiliriz? Aşağıdaki gibi yazabiliriz;

VeritabaniSorgulayici dbSorgu = new VeritabaniSorgulayici() {
	Connection = new SqlConnection("..ConnectionString.."),
	Command = new SqlCommand("..Sorgu..")
};

C# Obsolete Attribute

Yaklaşık olarak .Net Framework içerisinde binlerce class ve binlerce class içerisinde milyonlarca method vardır.

Her yeni .Net Framework versiyonunda bu class‘ların ve method‘ların bir kısmı yeni yöntemler / yeni teknikler ile baştan yazılıyor.

Bazen bu değişiklikler o kadar büyük oluyor ki, ilgili class veya method yeni bir isimle baştan yazılıyor ve eskisinin artık kullanılmaması öneriliyor.

Önceki Framework versiyonlarında yanlış konumlandırdıkları class veya method‘ları taşıdıkları durumlarda da aynı durum geçerli.

Geriye yönelik uyumluluktan dolayı, kullanılmaması önerilen class veya method‘lar üç versiyon daha yerinde durmaya devam eder.

Ama derleme zamanında uygulama geliştiriciye, ilgili class veya method‘un Obsolete olduğunu bildirir ve kodunu güncellemesini ister.

Bu özellik Obsolete Attribute sayesinde sağlanır.

Eğer biz de yazdığımız projelerimizde çeşitli class veya method’ları Obsolete yapmak istersek, aşağıdaki kodda gözüktüğü gibi yapabiliriz;

[Obsolete]
public class EskimisClass
{
	///
}

veya

[Obsolete("Bu method v4.5 versiyonundan itibaren kaldırılacak, lütfen yerine Ntv.YeniMedya.Veritabani sınıfında bulunan Baglan() methodunu kullanın. Ayrıntılı bilgi için http://www.xyz.com/Baglan sayfasına bakabilirsiniz")]
public void VeritabaniBaglan()
{
	///
}

Yukarıdaki kodda bulunan VeritabaniBaglan() method’unu kullanırken ve projemizi derlerken aşağıdaki ekranlar ile karşılaşırız;

Obsolete Method

Obsolete Method Error

C# Rezerve Methodlar – Reserved Methods

C# dilinin bazı yetenekleri, aslında uygulama geliştiricilerin göremedikleri method’lardan gelmektedir.

Eğer geliştirdiğiniz uygulamanın assembly’lerini ILDASM tool’u ile açıp incelerseniz, bu method çağrılarını görebilirsiniz.

Eğer bilerek veya yanlışlıkla bu method’lardan birisi ile aynı isme sahip method oluşturmaya çalışırsanız, derleme anında (compile-time) hata alırsınız.

Bu rezerve method isimlerini inceleyelim;

Özellikler (Properties) için Rezerve Methodlar;

Class veya Struct‘larınıza eklediğiniz özellikler, derleme zamanında

T add_{OzellikAdi}
void set_{OzellikAdi}(T value)

şablonunda iki method’a dönüştürülür. Class veya Struct‘ınıza bu şablona uyan method ekleyemezsiniz.

Indexer’lar için Rezerve Methodlar;

Indexer’larınız derleme zamanında

T get_Item(Parametre)
void set_Item(Parametre, T value)

şablonunda iki method’a dönüştürülür.

Yıkıcılar (Destructors) için Rezerve Methodlar;

Class veya Struct‘ınıza eklediğiniz Destructor kodu derleme zamanında

void Finalize()

method’una dönüştürülür. Class veya Struct‘ınıza Finalize() method’u eklemeye çalıştığınızda derleme zamanında uyarı alırsınız.

Olaylar (Events) için Rezerve Methodlar;

Olaylarınız için derleme zamanında kodunuza

void add_{EventAdi}(T callback)
void remove_{EventAdi}(T callback)

methodları eklenir.

C# Partial Method Desteği

C# 3.0 ile gelen partial keyword’u sayesinde partial class tanımlayabiliyoruz.

partial keyword’unun pek bilinmeyen bir kullanım alanı ise method‘lardır.

partial class tanımlayabildiğimiz gibi partial method‘da tanımlayabiliyoruz.

partial class‘ları tanımlamamızdaki en büyük fayda, kod üreticiler olmuştur. partial method tanımlamanın güzelliği de işte burada. Bir kod üreticisi ile otomatik olarak kod ürettirirken, partial method‘lar ürettirebiliriz.

Eğer üretilmiş partial method‘u uygulamazsanız, ilgili partial method‘u çağıran kod parçaları hata vermiyor.

Basitçe, uygulanmamış partial method çağrıları derleme (compile) sırasında yok sayılıyor.

Ama partial method‘lar için bazı kısıtlamalar ve kurallar var;

  • partial method’ların geri dönüş tipi herzaman void olmalıdır
  • partial method’lar out parametreler alamazlar, ama ref parametreler alabilirler
  • partial method’lar extern olamazlar
  • partial method’lar erişim belirleyici alamazlar, çünkü herzaman private erişim belirleyicisine sahiptirler
  • partial method’lar virtual olamazlar
  • partial method’lar static olabilir
  • partial method’lar generic olabilir
  • partial method’lara delegate bağlanamaz, çünkü partial method’un çalışma zamanında (runtime) var olacağı garanti değildir

Bu bilgilerden sonra örnek bir class yazalım;

public partial class DataAccessLayer
{
	partial void ResetConnection();
}

public partial class DataAccessLayer
{
	partial void ResetConnection()
	{
		///Connection'ı resetleyen kod
	}

	public void Reset()
	{
		ResetConnection();
	}
}

Gördüğünüz gibi, Reset() isminde public bir method yazmam gerekti. Çünkü, ResetConnection() method‘u partial olduğu için private erişim belirleyicisine sahip.

Eğer yukarıdaki kodun exe/dll dosyasına ILDASM tool’u bakarsak, DataAccessLayer.ResetConnection() private method’unu ve Reset() method‘u içerisinde bu method’u çağıran kod parçasını görürüz.

Eğer ResetConnection() method’unu uyguladığımız kod parçasını sildikten sonra, ILDASM ile bakarsak, DataAccessLayer.ResetConnection() private method’unun bulunmadığını ve Reset() method’unda bu method’u çağıran kod parçası olmadığını görürüz.

Böylece çalışma zamanı hatası almayız.

C# Erişim Belirleyiciler – Access Modifiers

Öncelikle erişim belirleyicilerin (access modifiers) nerelere uygulandığını bilmemiz lazım; C# projesinde tanımlanmış tüm varlıklara uygulanabilir, buna class, struct, function, method, property ve class seviyesindeki tüm değişkenler dahildir.

Peki, neden erişim belirleyicilere ihtiyaç duyarız?

Çünkü, uygulamamızda kullandığımız varlıkların bulundukları kod bloğunun dışından erişilip/erişilemeyeceğini belirlemek isteriz.

C# dilinde tanımlı erişim belirleyiciler (access modifiers);

public : public olarak tanımlanan öğe, kod bloğunun içinde ve dışında tamamen erişilebilirdir. Yani, hiçbir kısıtlama yoktur.

protected : protected olarak tanımlanan öğe, sadece tanımlandığı class’ın içinde ve o class’tan türetilmiş diğer class’ların içinde erişilebilirdir.

internal : internal olarak tanımlanan öğe, bulunduğu assembly’nin (Dll veya Exe dosyası) içinde erişilebilirdir. Dll veya Exe dosyasının içerisinde erişim için kısıtlama yoktur, ama dışarıdan erişilemez.

protected internal : protected internal erişim belirleyicisi, protected ve internal erişim belirleyicilerinin VEYA (OR) işlemiyle birleştirilmiş halidir. protected internal olarak tanımlanmış öğe, tanımlandığı class’ın içinde ve o class’tan türetilmiş diğer class’ların içinde erişilebilir. Ayrıca, aynı assembly içinde olmasalar dahi, tanımlandığı class’tan türetilmiş diğer class’ların içinde de erişilebilirdir.

private : private olarak tanımlanan öğe, sadece tanımlandığı class’ın içerisinde erişilebilirdir. En katı erişim belirleyicidir.

Aslında CLR içinde tanımlı bir erişim belirleyici daha vardır. FamilyAndAssembly olarak bilinir. C# kelimeleri ile benzetmeye çalışırsak, protected VE (AND) internal erişim belirleyicisidir. C# dili bu erişim belirleyicisini desteklemez, eğer kullanmanız gerekiyorsa, C++ ve direk IL yazmanız gerekmektedir.

class veya struct‘lar ya public, ya da internal olabilir, varsayılan olarak internal erişim belirleyicisine sahiptirler.

Varsayılan olarak class içerisinde tanımlı öğeler private erişim belirleyicisine sahiptirler.

struct içerisinde tanımlı öğeler public, internal veya private olabilirler. struct‘lar türetmeyi desteklemediği için protected ve protected internal erişim belirleyicisine zaten ihtiyaçları yoktur.

C# Auto-Implemented Properties Özelliği

Uygulamalarımızı geliştirirken birçok class veya struct yazmamız gerekir.

Bu class veya struct‘ların hemen hepsinde özelliklere (properties) ihtiyaç duyarız.

Çünkü, local değişkenlerin public değil, private olmasını isteriz. Bu durumda yapmamız gereken public olmasını istediğimiz local değişkenlere birer tane özellik (property) yazmaktır.

Örnek Personel Class‘ı aşağıdaki gibi olacaktır;

public class Personel
{
	private int PersonelId;
	private string AdSoyad;
}
public class Personel
{
	private int _PersonelId;
	private string _AdSoyad;

	public int PersonelId
	{
		get { return _PersonelId; }
		set { _PersonelId = value; }
	}

	public string AdSoyad
	{
		get { return _AdSoyad; }
		set { _AdSoyad = value; }
	}
}

Sadece okuma/yazma yapan iki özellik için ne faz kod yazdık, değil mi?

Eğer get/set blokları arasına özel iş yapan kod yazmamız gerekiyorsa, yukarıdaki gibi yapmaya devam etmeliyiz.

Fakat sadece okuma/yazma yapan özellikleri C# 3.0‘dan itibaren daha kısa yazabiliyoruz;

public class Personel
{
	public int PersonelId { get; set; }

	public string AdSoyad { get; set; }
}

Gördüğünüz gibi local değişkenlerin oluşturulması ve yönetilmesi işini yapmakla uğraşmıyoruz.

Eğer local değişkenlerin nasıl oluşturulduğunu merak ediyorsanız, projenizi compile ettikten sonra, ortaya çıkan exe/dll dosyasını ILDASM (Intermediate Language Disassembler) tool’u ile açıp bakabilirsiniz.

PersonelId özelliği için <>k__AutomaticallyGeneratedPropertyField0 isimli bir değişkenin oluşturulduğu ve tipininde int olduğunu göreceksiniz.

Otomatik oluşturulan değişken’in ismini herhangi bir kod parçasında kullanmaya çalışırsak, derleme (compile) zamanında yazım hatası (syntax error) alırız, yani local değişkenleri kullanamayız.

Eski stil kodlamada sadece okunabilir (readonly) veya sadece yazılabilir (writeonly) property tanımlayabiliyorduk. Bunu yeni stil kodlamada da yapabilir miyiz?

Cevap Evet, aşağıdaki kodu inceleyin;

public class Personel
{
	public int PersonelId { get; private set; }
	public string AdSoyad { private get; set; }
}

C# ile Tarihin Gününü Bulma

Uygulamanızda tarih göstereceğiniz ekranlarda, tarihin gününü de göstermek istersiniz. Hatta gün isimlerinin, kullanıcının dilinde olmasını istersiniz.

Öncelikle ilgili tarihin hangi güne geldiğini bulalım;

int gun1 = (int)DateTime.Now.DayOfWeek;
int gun2 = (int)DateTime.Now.AddMonth(-5).DayOfWeek;
int gun3 = (int)DateTime.Parse("2009-01-05").DayOfWeek;

Dikkat etmemiz gereken şey; DayOfWeek ilgili tarihin hangi güne denk geldiğini bulurken, Pazar gününü baz alır.

Uygulamayı kullanan kullanıcının kendi dilinde gün ismini bulmak için;

string gun1 = DateTime.Now.ToString("dddd");
string gun2 = DateTime.Now.DayOfWeek;
string gun3 = DateTime.Parse("2009-01-05").DayOfWeek;
string gun4 = CultureInfo.CurrentCulture.DateTimeFormat.DayNames[(int)DateTime.Now.DayOfWeek];

İlgili tarihin hangi güne geldiğini başka bir dilde bulmak için;

string gun5 = CultureInfo.GetCultureInfo("tr-TR").DateTimeFormat.DayNames[(int)DateTime.Now.DayOfWeek];
string gun6 = CultureInfo.GetCultureInfo("de-DE").DateTimeFormat.DayNames[(int)DateTime.Now.DayOfWeek];

C# Null Coalescing (??) Operatörü

Null Coalescing Operatörü (??) C# dilinin gözden kaçan, ama çok kullanışlı bir operatörüdür.

?? operatörü sayesinde bir değişkenin değerinin null olduğu durumda alternatif değer döndürebiliriz.

string mesaj = "merhaba dünya!";
string sonuc = mesaj ?? "mesaj yok";

Eğer mesaj değişkeni null değer içeriyor olsaydı, sonuc değişkenine “mesaj yok” değeri atanacaktı.

string mesaj = null;
string sonuc = mesaj ?? "mesaj yok";
int? yas = 30;
int sonuc = yas ?? -1;

Eğer yas değişkeni null ise, sonuc değişkenine -1 değeri ata.

int? yas = null;
int sonuc = yas ?? -1;