C# Generics'i Ustalaşmak
C#'ta genellik, tanıtımlarından bu yana dilin ayrılmaz bir parçası haline geldi, tüm geliştiriciler tarafından tam anlaşılamasalar bile sayısız fayda sağlar. Videosunda, "C#'ta Genellik Nasıl Oluşturulur, Yeni Özellikler Dahil", Tim Corey, genelliğin neden önemli olduğunu, onları nasıl oluşturup kullanacağını ve pratik uygulamalarını gösterir.
Bu makale, Tim Corey'nin bu konudaki videosundan yararlı bilgiler sunarak, C# genelliklerine kapsamlı bir rehber sağlar. Genellikler hakkında temel bilgileri, tür güvenliğini, performans faydalarını ve pratik uygulamaları ele alır. Makale ayrıca genel metotların, sınıfların, arayüzlerin nasıl oluşturulacağını ve genel kısıtların uygulamalarını keşfeder. Ek olarak, verimliliği artırmak, tür güvenliğini sağlamak ve etkili, tür güvenliği sağlayan kod yazmak amacıyla genelliklerin nasıl kullanıldığına dair gerçek dünya kullanım örneklerini vurgular.
Giriş
C# genellikleri, geliştiricilerin herhangi bir veri türüyle çalışabilen sınıflar, metotlar, arayüzler ve koleksiyonlar tanımlamalarına olanak tanıyarak esnek, yeniden kullanılabilir ve tür güvenliği sağlayan kod oluşturmanın güçlü bir yolunu sunar. Genel bir sınıf veya genel metot kullanarak, geliştiriciler herhangi bir veri türünü temsil edebilen bir genel tür parametresi (örneğin, T) tanımlayabilir. Bu, kod çoğaltımını ortadan kaldırır ve derleme zamanı sırasında tür güvenliğini sağlarken kodun yeniden kullanılabilirliğini artırır. List ve Dictionary<TKey, TValue> gibi genel koleksiyon sınıfları, farklı veri türlerinin etkin şekilde işlenmesine olanak tanırken, genel arabirimler ve genel delegeler, çoklu tür parametreleri ile çalışabilecek özel genel türlerin oluşturulmasını sağlar. Generiklerden yararlanarak, geliştiriciler kod verimliliğini en üst düzeye çıkarabilir ve genel olmayan sınıfların açıklarını en aza indirebilir. Bu esneklik, performans veya tür güvenliğinden ödün vermeden daha yeniden kullanılabilir kodların oluşturulmasını sağlar.
Tim (0:00) genelliğin C# içinde yaygın kullanımını vurgulayarak konuya giriş yapar. Genelliğin neden bu kadar önemli olduğunu açıklamayı ve bunları nasıl oluşturup kullanacağını göstermeyi hedefler.
Projeyi Oluşturma
Tim, göze çarpan herhangi bir kullanıcı arayüzü distraksiyonu olmadan yalnızca genellikleri göstermeye odaklanmak için "GenericsDemoApp" adında yeni bir konsol uygulaması oluşturur. Projeyi, .NET 8 ve Visual Studio 2022 kullanarak kurar.
Genelliklerin Temelleri
Tim, (2:22)'de List sınıfını kullanarak generics kavramını açıklamak için bir örnekle başlar. Genellikler, bir koleksiyonun tutabileceği elemanların türünü belirtmeye izin vererek tür güvenliği sağlar ve çalışma zamanı hatalarını önler.
List<int> numbers = new List<int> { 1, 2, 3 };
List<string> strings = new List<string> { "Tim", "Corey", "Sue" };List<int> numbers = new List<int> { 1, 2, 3 };
List<string> strings = new List<string> { "Tim", "Corey", "Sue" };List, sayılar listesine yalnızca tamsayıların ve dizeler listesine yalnızca dizelerin eklenebileceğini garanti eder.
Tür Güvenliği ve Verimlilik
Tim, genelliklerin sağladığı tür güvenliğinin önemine vurgu yapar. Derleyici, türleri tasarım zamanında kontrol eder, tür uyuşmazlıklarını önler ve güvenli kod yürütmeyi sağlar. Genellikler, ayrıca boks ve unboxing ihtiyacını ortadan kaldırarak daha verimli bir kod sağlar.
Genel Olmayan Koleksiyonların Verimsizliği
Genel olmayan koleksiyonların kullanılmasının verimsizliğini göstermek için Tim, bir List oluşturur ve farklı türdeki nesneleri nasıl tutabileceğini gösterir.
List<object> objects = new List<object> { "Tim", 4, 3.6m };List<object> objects = new List<object> { "Tim", 4, 3.6m };Genel olmayan koleksiyonların kullanılmasının tür uyumsuzluklarına ve boks ile unboxing nedeniyle verimsizliklere yol açabileceğini açıklar.
Performans Karşılaştırması
Tim (6:15), bir List
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 1; i <= 1_000_000; i++)
{
objects.Add(i);
}
stopwatch.Stop();
Console.WriteLine($"List of object elapsed time: {stopwatch.ElapsedMilliseconds} ms");
stopwatch.Restart();
for (int i = 1; i <= 1_000_000; i++)
{
numbers.Add(i);
}
stopwatch.Stop();
Console.WriteLine($"List of int elapsed time: {stopwatch.ElapsedMilliseconds} ms");Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 1; i <= 1_000_000; i++)
{
objects.Add(i);
}
stopwatch.Stop();
Console.WriteLine($"List of object elapsed time: {stopwatch.ElapsedMilliseconds} ms");
stopwatch.Restart();
for (int i = 1; i <= 1_000_000; i++)
{
numbers.Add(i);
}
stopwatch.Stop();
Console.WriteLine($"List of int elapsed time: {stopwatch.ElapsedMilliseconds} ms");Sonuçlar, tam sayıları bir List

Bir Tür Kontrol Edici Metot Oluşturma
Tim (10:14), TypeChecker adında bir genel metot oluşturmanın nasıl yapılacağını gösterir. Bu metot, verilen bir değerin türünü kontrol eder ve onu yazdırır, genelliklerin esnekliğini ve gücünü gösterir.
public static void TypeChecker<t>(T value)
{
Console.WriteLine($"Type: {typeof(T)}, Value: {value}");
}public static void TypeChecker<t>(T value)
{
Console.WriteLine($"Type: {typeof(T)}, Value: {value}");
}TypeChecker metodu, genel parametre T'nin türünü belirlemek için typeof operatörünü kullanır ve hem türü hem de değeri yazdırır.
Tür Kontrol Edici Metodunun Kullanımı
Tim, TypeChecker metodu ile farklı türdeki argümanları çağırma örnekleri sağlar.
TypeChecker(1); // Type: System.Int32, Value: 1
TypeChecker("Tim"); // Type: System.String, Value: Tim
TypeChecker(1.1); // Type: System.Double, Value: 1.1TypeChecker(1); // Type: System.Int32, Value: 1
TypeChecker("Tim"); // Type: System.String, Value: Tim
TypeChecker(1.1); // Type: System.Double, Value: 1.1TypeChecker methoduna çeşitli türler geçirerek, Tim, genelliklerin farklı veri türlerini sorunsuz bir şekilde nasıl yönetebileceğini gösterir.
Bir Genel Sınıf Oluşturma: Daha İyi Liste
Tim (16:25), BetterList adında bir genel sınıf yaratmaya geçer. Bu sınıf, belirtilen türde bir listeyi kapsar ve ek işlevsellik sunar.
public class BetterList<t>
{
private List<t> data = new List<t>();
public void AddToList(T value)
{
data.Add(value);
Console.WriteLine($"{value} has been added to the list");
}
}public class BetterList<t>
{
private List<t> data = new List<t>();
public void AddToList(T value)
{
data.Add(value);
Console.WriteLine($"{value} has been added to the list");
}
}BetterList sınıfı, bir özel List ve listeye bir değer ekleyen ve ekleme işlemini belirten bir mesaj yazdıran AddToList adında bir metot içerir.
Daha İyi Liste Sınıfının Kullanımı
Tim, BetterList sınıfının farklı türlerle kullanım örnekleri sağlar.
BetterList<int> betterNumbers = new BetterList<int>();
betterNumbers.AddToList(5);
BetterList<PersonRecord> people = new BetterList<PersonRecord>();
people.AddToList(new PersonRecord("Tim", "Corey"));BetterList<int> betterNumbers = new BetterList<int>();
betterNumbers.AddToList(5);
BetterList<PersonRecord> people = new BetterList<PersonRecord>();
people.AddToList(new PersonRecord("Tim", "Corey"));Bu örneklerde BetterList
Bir Genel Arayüz Oluşturma
Tim (21:48), IImportance adında bir genel arayüz fikrini tanıtır. Bu arayüz, iki değerden hangisinin daha önemli olduğunu belirlemeye yönelik bir metot tanımlar.
public interface IImportance<t>
{
T MostImportant(T a, T b);
}public interface IImportance<t>
{
T MostImportant(T a, T b);
}Genel Arayüzü Uygulama
Tim, bu arayüzün farklı türlerde nasıl uygulanacağını gösterir. Integer uygulamasıyla başlar.
public class EvaluateImportance : IImportance<int>
{
public int MostImportant(int a, int b)
{
return a > b ? a : b;
}
}public class EvaluateImportance : IImportance<int>
{
public int MostImportant(int a, int b)
{
return a > b ? a : b;
}
}Sonraki adımda, dizelerin önemini belirlemek için dizelerin uzunluğunu kullanarak arayüzü dizeler için uygulanır.
public class EvaluateStringImportance : IImportance<string>
{
public string MostImportant(string a, string b)
{
return a.Length > b.Length ? a : b;
}
}public class EvaluateStringImportance : IImportance<string>
{
public string MostImportant(string a, string b)
{
return a.Length > b.Length ? a : b;
}
}Bu uygulamalar, aynı arayüzün farklı türlerle, her tür için belirli bir mantıkla nasıl uygulanabileceğini gösterir.
Genellere Kısıtlamalar Uygulama
Tim (25:21)'de, jeneriklere hangi koşulları uygulayabileceğinizi, belirli koşullara uygun olmalarını sağlamak için açıklar. Örneğin, bir jenerik türün boş bir yapıcıya sahip olması veya belirli bir arabirimi uygulaması sınırlanabilir.
public class SampleClass<t> where T : new()
{
// Class implementation
}
public class SampleClassWithInterface<t> where T : IImportance<t>
{
// Class implementation
}public class SampleClass<t> where T : new()
{
// Class implementation
}
public class SampleClassWithInterface<t> where T : IImportance<t>
{
// Class implementation
}Bu kısıtlamalar, jenerik türün gerekli kriterleri karşıladığını, çalışma zamanı hatalarını önlediğini ve tür güvenliğini artırdığını garanti etmeye yardımcı olur.
Microsoft'un INumber Uygulaması
Tim, Microsoft'un sayısal işlemleri sınırlandırmak için INumber arayüzünü nasıl kullandığını tartışıyor. Bu, jenerik türlerde toplama ve çıkarma gibi aritmetik işlemleri mümkün kılar.
public class MathOperations<t> where T : INumber<t>
{
public T Add(T x, T y)
{
return x + y;
}
}public class MathOperations<t> where T : INumber<t>
{
public T Add(T x, T y)
{
return x + y;
}
}Genel tür T'yi INumber ile kısıtlayarak, türün sayısal işlemleri desteklediğini garanti eder.
Farklı Sayısal Türlerle Jenerikleri Kullanma
Tim (33:55)'de MathOperations sınıfını genişleterek, jeneriklerin çiftler ve ondalıklar gibi farklı sayısal türlerle nasıl kullanılabileceğini gösterir.
Tim, MathOperations'un tamsayılar ve çiftler için örneklerini nasıl oluşturacağını gösteriyor:
MathOperations<int> intMath = new MathOperations<int>();
Console.WriteLine(intMath.Add(1, 4)); // Outputs: 5
MathOperations<double> doubleMath = new MathOperations<double>();
Console.WriteLine(doubleMath.Add(1.5, 4.3)); // Outputs: 5.8MathOperations<int> intMath = new MathOperations<int>();
Console.WriteLine(intMath.Add(1, 4)); // Outputs: 5
MathOperations<double> doubleMath = new MathOperations<double>();
Console.WriteLine(doubleMath.Add(1.5, 4.3)); // Outputs: 5.8Bu, jeneriklerin esnekliğini gösterir, bu da aynı sınıf içinde farklı sayısal türlerin sorunsuz bir şekilde ele alınmasına imkan tanır.
Farklı Sayısal Türleri Ele Alma
Tim, farklı sayısal türlerin bir arada kullanılamayacağını, tür güvenliğinin önemini vurgular. Örneğin, bir tamsayıya bir çifti eklemeye çalışmak bir derleme zamanı hatası ile sonuçlanır.
// This will result in a compile-time error
// Console.WriteLine(intMath.Add(1.5, 4));// This will result in a compile-time error
// Console.WriteLine(intMath.Add(1.5, 4));Tür Dönüşümü Aşırı Yükünden Kaçınma
Tim, tür dönüşümü nedeniyle oluşan aşırı yükten kaçınmanın faydalarını jenerikleri kullanarak açıklar. Örneğin, matematiksel işlemler için tamsayıları çifte dönüştürmek ve sonra tekrar tamsayıya dönüştürmek maliyetli olabilir. Jenerikleri kullanmak, doğrudan doğal türler üzerinde işlem yapmayı, performansı ve hassasiyeti koruyarak sağlar.
Uygulamada Jenerikler
Tim, jenerikleri kullanırken dikkatli olunması gerektiği konusunda uyarır, geliştiricilerin bunları uygun şekilde kullanmalarını ve aşırı kullanımdan kaçınmalarını önerir. Jeneriklerin faydalarına dikkat çeker: tür güvenliği, kutulama ve kutudan çıkarma azaltılması, derleme zamanı kontrolü ve artırılmış kod okunabilirliği gibi.
Ayrıca, generics'in List ve Dictionary<TKey, TValue> gibi koleksiyonlarda yaygın olduğunu, ve çeşitli türleri önceden spesifikasyon olmadan işleyebilen günlük kayıt sistemlerinde kullanıldığını da belirtir.
Sonuç
Tim Corey'nin C#'taki gelişmiş jeneriklerin ayrıntılı keşfi, pratik uygulamaları ve faydaları hakkında değerli bilgiler sunar. Jenerikler hakkındaki bilginizi derinleştirmeyi ve gerçek dünya örneklerini eylemde görmeyi istiyorsanız, Tim Corey'nin C# Generics konulu ayrıntılı videosunu izlemeyi unutmayın. Açık açıklamaları ve uygulamalı gösterimleri, kavramları tamamen kavramanıza ve kendi projelerinizde etkin bir şekilde uygulamanıza yardımcı olacaktır.

