Extension Method | Engin Polat\'ın Windows 8 , Windows Phone 8 ve C# içerikli programcılık sitesi

Arşiv

Etiketlenen yazılar extension method

Listedeki Her X’inci Elemanı Çekmek

27 June 2013 1 yorum

Bu yazıda elimizdeki listenin her x’inci elemanını çeken extension method yazacağız.

Örneğin, bir ankete cevap veren katılımcılardan her 5000’inci katılımcıyı seçmek istediğimiz durumlarda böyle bir method’a ihtiyaç duyarız.

TakeEvery() extension method‘u, Language Integrated Query (LINQ) method’larından Skip() ve Take() gibi çalışacak, fakat listenin belli sayıda öğesini atlayıp, belli sayıda öğesini seçmek yerine, her x’inci elemanı seçecek.

İlk olarak TakeEverySample isimli projeyi oluşturalım;

IEnumerable<T> sınıfına TakeEvery() method’unu eklemek için projemize ExtensionMethods isimli sınıfı ekleyelim;

public static class ExtensionMethods
{
	public static IEnumerable<T> TakeEvery<T>(this IEnumerable<T> list, int every)
	{
		if (list == null)
		{
			throw new ArgumentException("list parametresi boş geçilemez");
		}
		if (every < 1)
		{
			throw new ArgumentException("'every' parametresi en az 1 olabilir");
		}

		var step = 0;

		var enumerator = list.GetEnumerator();
		while (enumerator.MoveNext())
		{
			step++;
			if (step == every)
			{
				yield return enumerator.Current;
				step = 0;
			}
		}
	}
}

İlk olarak method'a geçilen parametrelerin değerlerini kontrol ediyoruz, eğer uygun olmayan bir değer verilmişse ilgili mesaj ile bir hata fırlatıyoruz.

list parametresinin enumerator'unu GetEnumerator() method'u ile aldıktan sonra MoveNext() method'u ile liste üzerinde ilerliyoruz.

step değişkeni every değişkeninin değerine eşit olduğunda aradığımız kaydı bulmuşuzdur, ilgili kaydı geri döndürüyoruz.

Örnek kullanım;

var rakamlar = Enumerable.Range(1, 1000000);

var her50000 = rakamlar.TakeEvery(50000);

// her50000 = { 50000, 100000, 150000, 200000, 250000, 300000, ... }

LINQ tarzı Replace operatorü

12 March 2013 Yorum yapılmamış

Bir dizideki elemanlardan, belli bir değere sahip olanları başka bir değerle değiştirecek bir methoda sahip olmak çok faydalıdır.

Language-Integrated Query (LINQ) bu amaç için standart bir sorgu işlecini sağlamaz ama oluşturması basittir.

Örneğin; string sınıfı Replace() isminde bir method barındırır ve değişkenin değerinde yer alan belli bir karakterin tamamını başka bir karakter ile değiştirir.

Aynı mantık ile diziler üzerinde çalışan bir method olsaydı çok kullanışlı olurdu.

Öncelikle static ExtensionMethods isimli bir sınıf oluşturalım;

public static class ExtensionMethods
{
}

Replace() methodunu yazalım;

public static IEnumerable<T> Replace<T>(this IEnumerable<T> list, T find, T replaceWith)
{
	if (list == null) throw new ArgumentNullException("sequence");

	foreach (T item in list)
	{
		yield return find.Equals(item) ? replaceWith : item;
	}
}

Örnek kullanım şekli;

int[] values = new int[] { 1, 2, 3, 4, 5, 4, 3, 2, 1 };
int[] replacedInts = values.Replace(3, 0).ToArray();
 
// SONUÇ
// 1, 2, 0, 4, 5, 4, 0, 2, 1

string[] strings = new string[] { "A", "B", "C", "D", "C", "B", "A" };
string[] replacedStrings = strings.Replace("B", "-").ToArray();

// SONUÇ
// "A", "-", "C", "D", "C", "-", "A"

String ve SecureString için Encrypt, Decrypt ExtensionMethod

04 March 2013 Yorum yapılmamış

Windows Data Protection API (DPAPI) encryption key’e ihtiyaç duymadan, kullanıcının oturumuna özel şifreleme yapabilen bir yapıdır.

DPAPI’yi kullanabilmek için System.Security referans’ının proje’ye eklenmesi gerekmektedir.

.Net 2.0 versiyonundan beri .Net Framework‘un bir parçası olan DPAPI sayesinde metin şifreleme ve şifre çözme çok kolaylaşmıştır.

byte[] tipinde yapıların şifrelenmesi Protect() method’u ile, şifre çözülmesi ise Unprotect() method’u ile yapılmaktadır;

public static byte[] Encrypt(byte[] data)
{
	return ProtectedData.Protect(data, null, DataProtectionScope.CurrentUser);
}

public static byte[] Decrypt(byte[] data)
{
	return ProtectedData.Unprotect(data, null, DataProtectionScope.CurrentUser);
}

Bu yapının string tipindeki değişkenler ile de kullanılabilmesi için aşağıdaki extension method‘lar kullanılabilir;

public static class ExtensionMethods
{
	private const DataProtectionScope Scope = DataProtectionScope.CurrentUser;

	public static string Encrypt(this string plainText)
	{
		if (plainText == null) throw new ArgumentNullException("plainText");

		var data = Encoding.Unicode.GetBytes(plainText);
		byte[] encrypted = ProtectedData.Protect(data, null, Scope);

		return Convert.ToBase64String(encrypted);
	}

	public static string Decrypt(this string encryptedText)
	{
		if (encryptedText == null) throw new ArgumentNullException("encryptedText");

		byte[] data = Convert.FromBase64String(encryptedText);
		byte[] decrypted = ProtectedData.Unprotect(data, null, Scope);

		return Encoding.Unicode.GetString(decrypted);
	}
}

ProtectedData sınıfının Protect() ve Unprotect() method’larının üçüncü parametresi DataProtectionScope enum tipindedir ve CurrentUser, LocalMachine değerlerinden birini alabilir.

Artık string tipindeki değişkenleri Encrypt() ve Decrypt() method’ları ile şifreleyebilir, şifrelerini çözebiliriz. Fakat bu değişkenlerin değerleri hafızada hala okunabilir formatta tutuluyor olacak.

Eğer değişkenlerin içeriğinin hafızada daha güvenli tutulmasını istiyorsak SecureString tipini kullanmalıyız.

(MSDN’de yeralan Making Strings More Secure makalesini okumanızı tavsiye ederim.)

SecureString tipi için de Encrypt() ve Decrypt() extension method‘ları yazalım;

public static string Encrypt(this SecureString value)
{
	if (value == null) throw new ArgumentNullException("value");

	IntPtr ptr = Marshal.SecureStringToCoTaskMemUnicode(value);
	try
	{
		char[] buffer = new char[value.Length];
		Marshal.Copy(ptr, buffer, 0, value.Length);

		byte[] data = Encoding.Unicode.GetBytes(buffer);
		byte[] encrypted = ProtectedData.Protect(data, null, Scope);

		return Convert.ToBase64String(encrypted);
	}
	finally
	{
		Marshal.ZeroFreeCoTaskMemUnicode(ptr);
	}
}

public static SecureString DecryptSecure(this string encryptedText)
{
	if (encryptedText == null) throw new ArgumentNullException("encryptedText");

	byte[] data = Convert.FromBase64String(encryptedText);
	byte[] decrypted = ProtectedData.Unprotect(data, null, Scope);

	SecureString ss = new SecureString();

	int count = Encoding.Unicode.GetCharCount(decrypted);
	int bc = decrypted.Length / count;
	for (int i = 0; i < count; i++)
	{
		ss.AppendChar(Encoding.Unicode.GetChars(decrypted, i * bc, bc)[0]);
	}

	ss.MakeReadOnly();
	return ss;
}

Son olarak string tipi ile SecureString tipi arasındaki dönüşüm extension method'larını da yazalım;

public static SecureString ToSecureString(this IEnumerable value)
{
	if (value == null) throw new ArgumentNullException("value");

	var secured = new SecureString();

	var charArray = value.ToArray();
	for (int i = 0; i < charArray.Length; i++)
	{
		secured.AppendChar(charArray[i]);
	}

	secured.MakeReadOnly();
	return secured;
}

public static string UnWrap(this SecureString value)
{
	if (value == null) throw new ArgumentNullException("value");

	IntPtr ptr = Marshal.SecureStringToCoTaskMemUnicode(value);
	try
	{
		return Marshal.PtrToStringUni(ptr);
	}
	finally
	{
		Marshal.ZeroFreeCoTaskMemUnicode(ptr);
	}
}

Artık string ve SecureString tipleri için hazırladığımız extension method'ları kullanabiliriz;

bool isOk = false;

string plain = "password";

string encrypted = plain.Encrypt();

string decrypted = encrypted.Decrypt();

SecureString plainSecure = plain.ToSecureString();

string encryptedSecure = plainSecure.Encrypt();

string decryptedSecure = encryptedSecure.Decrypt();

isOk = plain == decrypted;
isOk = plain == plainSecure.UnWrap();
isOk = plain == decryptedSecure;

C# String Dizisini String Uzunluğuna Göre Sıralamak

19 August 2011 Yorum yapılmamış

Geliştirdiğimiz projelerde ekrana listeleyeceğimiz dizileri harf uzunluklarına göre sıralamak isteyebiliriz.

Bu makale ile, IEnumerable<string> tipinden değişkenlere bu özelliği nasıl ekleyeceğimizi göreceğiz.

Öncelikle Extension Method‘umuzu static bir sınıf içerisine (ExtensionManager) yazmamız lazım;

public static class ExtensionManager
{
	public static IEnumerable<string> Sirala(this IEnumerable<string> Liste)
	{
		string[] strArray = Liste.ToArray<string>();
		Array.Sort(strArray, new Comparison<string>(delegate(string Kelime1, string Kelime2)
		{
			if (Kelime1 == null && Kelime2 == null)
			{
				return 0;
			}
			else if (Kelime1 == null)
			{
				return -1;
			}
			else if (Kelime2 == null)
			{
				return 1;
			}
			else
			{
				if (Kelime1.Length < Kelime2.Length)
					return -1;
				else if (Kelime1.Length > Kelime2.Length)
					return 1;
				else
					return Kelime1.CompareTo(Kelime2);
			}
		}));

		return strArray;
	}
}

Yukarıdaki kod’da Array sınıfının static Sort method’unu çağırıyoruz. Karşılaştırma koşulunu, ikinci parametre’de delegate method olarak yazıyoruz.

Kullanımı;

var Rakamlar = new List<string> { "12", "1", null, "2314", "55555", "123", "222" };

foreach (var Rakam in Rakamlar.Sirala())
{
	Console.WriteLine(Rakam);
}

Sonuç;

[null]
1
12
123
222
2314
55555

C# Liste’nin eleman içerdiğini kontrol etmek

30 November 2010 1 yorum

Aşağıdaki kod parçasına, programlama üzerine yazılmış bir makaleyi okurken rastladım;

public void IsNullOrEmpty<T>(IEnumerable<T> koleksiyon)
{
	if (koleksiyon == null || koleksiyon.Count() == 0)
	{
		/// Koleksiyon'da hiç eleman yok
		/// Koleksiyon boş
	}
}

Yukarıdaki method IEnumerable generic tipinde parametre alıyor, null veya boş olup/olmadığını kontrol ediyor.

Karşılaştırmada kullanılan,

Liste.Count() == 0

kod parçası dikkatimi çekti.

Eğer IsNullOrEmpty fonksiyonuna parametre olarak ICollection<T> interface‘ini implemente etmeyen tipten bir değer gönderecek olursak (mesela LINQ sorgusu sonucu olarak IQueryable) Count method’u tüm liste üzerinde satır-satır ilerleyerek listedeki eleman adedini hesaplayacaktır.

Eğer fonksiyona parametre olarak IEnumerable interface‘ini implemente eden tipten bir değer gönderecek olursak, Count method’u, optimize edilmiş haliyle, sadece koleksiyonun Count özelliğini sorgulayacaktır.

Türkçe ifadesiyle; “Koleksiyonda eleman var mı?” sorusu yerine “Koleksiyon’da sıfır eleman mı var?” sorusunu sormuş oluyoruz.

Aslında çözüm basit, System.Linq namespace‘inde yeralan Any extension method‘unu kullanmamız gerekiyor;

public void Test<T>(IEnumerable<T> koleksiyon)
{
	if (koleksiyon == null || !koleksiyon.Any())
	{
		/// Koleksiyon'da hiç eleman yok
		/// Koleksiyon boş
	}
}

Bu yöntemin güzelliği, IEnumerable interface‘inin MoveNext methodunu sadece bir kere çağırması. Koleksiyonda bulunan eleman adedi ne olursa olsun, Any methodu sonucu çok hızlı birşekilde döndürecektir.

Hatta kendi extension method‘umuzu da Any methodunu kullanarak yazabiliriz;

public static bool IsNullOrEmpty<T>(this IEnumerable<T> koleksiyon)
{
	return koleksiyon == null || !koleksiyon.Any();
}

Artık extension method‘u da yazdığımıza göre, asıl method‘u iyileştirebiliriz;

public void Test<T>(IEnumerable<T> koleksiyon)
{
	if (koleksiyon.IsNullOrEmpty())
	{
		/// Koleksiyon'da hiç eleman yok
		/// Koleksiyon boş
	}
}