Genel türler ve yöntemler

Tavsiye

Yazılım geliştirme konusunda yeni misiniz? İlk olarak Başlangıç öğreticileri ile başlayın. gibi List<T>koleksiyonları kullanır kullanmaz genel türlerle karşılaşırsınız.

Başka bir dilde mi deneyimlisiniz? C# jenarikler, Java jenariklerine veya C++ şablonlarına benzer, ancak tam çalışma zamanı türü bilgilerinden faydalanır ve tür silinmesi olmaz. C#'ye özgü desenleri bulmak için koleksiyon ifadeleri ve kovaryans ve kontravaryans bölümlerini gözden geçirin.

Genel değerler , tam tür güvenliğini koruyarak herhangi bir türle çalışan kod yazmanıza olanak sağlar. Ayrı sınıflar veya yöntemler yazmak yerine int, string ve ihtiyaç duyduğunuz diğer her tür için, bir veya daha fazla tür parametresiyle (T, veya TKey ve TValue gibi) bir sürüm yazın ve kullandığınızda gerçek türleri belirtin. Derleyici derleme zamanında türleri denetler, bu nedenle çalışma zamanı atamalarına veya risklerine InvalidCastExceptionihtiyacınız yoktur.

Günlük C# dilinde sürekli olarak genel değerlerle karşılaşırsınız. Koleksiyonlar, asenkron dönüş türleri, delegeler ve LINQ hepsi genel türlere dayanır:

List<int> scores = [95, 87, 72, 91];
Dictionary<string, decimal> prices = new()
{
    ["Widget"] = 19.99m,
    ["Gadget"] = 29.99m
};
Task<string> greeting = Task.FromResult("Hello, generics!");
Func<int, bool> isPositive = n => n > 0;

Console.WriteLine($"First score: {scores[0]}");
Console.WriteLine($"Widget price: {prices["Widget"]:C}");
Console.WriteLine($"Greeting: {await greeting}");
Console.WriteLine($"Is 5 positive? {isPositive(5)}");

Her durumda, açılı köşeli ayraçlardaki tür bağımsız değişkeni (<int>, <string>, <Product>), genel tipe hangi türde veriler tuttuğunu veya işlettiğini bildirir. Derleyici tür güvenliğini sağlar. Yanlışlıkla bir string'i bir List<int>'e ekleyemezsiniz.

Genel türleri kullanma

Daha sık olarak, kendi sınıf kitaplığınızı oluşturmak yerine .NET sınıf kitaplığından genel türleri tüketirsiniz. Aşağıdaki bölümlerde kullanacağınız en yaygın genel türler gösterilmektedir.

Genel koleksiyonlar

Ad alanı System.Collections.Generic, tür güvenli koleksiyon sınıfları sağlar. Gibi ArrayListgenel olmayan koleksiyonlar yerine her zaman bu koleksiyonları kullanın:

// A strongly typed list of strings
List<string> names = ["Alice", "Bob", "Carol"];
names.Add("Dave");
// names.Add(42); // Compile-time error: can't add an int to List<string>

// A dictionary mapping string keys to int values
var inventory = new Dictionary<string, int>
{
    ["Apples"] = 50,
    ["Oranges"] = 30
};
inventory["Bananas"] = 25;

// A set that prevents duplicates
HashSet<int> uniqueIds = [1, 2, 3, 1, 2];
Console.WriteLine($"Unique count: {uniqueIds.Count}"); // 3

// A FIFO queue
Queue<string> tasks = new();
tasks.Enqueue("Build");
tasks.Enqueue("Test");
Console.WriteLine($"Next task: {tasks.Dequeue()}"); // Build

Genel koleksiyonlar, tür hatalarının çalışma zamanında oluşmasını engeller çünkü bu hatalar bunun yerine derleme zamanında ortaya çıkar. Bu koleksiyonlar ayrıca değer türleri için kutulama yapmaktan kaçınarak performansı artırır.

Genel yöntemler

Genel bir yöntem kendi tür parametresini bildirir. Derleyici genellikle geçirdiğiniz değerlerden tür bağımsız değişkenini çıkardığından açıkça belirtmeniz gerekmez:

static void Print<T>(T value) =>
    Console.WriteLine($"Value: {value}");

Print(42);        // Compiler infers T as int
Print("hello");   // Compiler infers T as string
Print(3.14);      // Compiler infers T as double

ÇağrıPrint(42) sırasında, derleyici bağımsız değişkenden T'i int olarak sonuçlandırır. Açıkça yazabilirsiniz Print<int>(42) , ancak tür çıkarımı kodu daha temiz tutar.

Koleksiyon ifadeleri

Koleksiyon ifadeleri (C# 12), koleksiyon oluşturmak için kısa bir söz dizimi sağlar. Oluşturucu çağrıları veya başlatıcı söz dizimi yerine köşeli ayraç kullanın:

// Create a list with a collection expression
List<string> fruits = ["Apple", "Banana", "Cherry"];

// Create an array
int[] numbers = [1, 2, 3, 4, 5];

// Works with any supported collection type
IReadOnlyList<double> temperatures = [72.0, 68.5, 75.3];

Console.WriteLine($"Fruits: {string.Join(", ", fruits)}");
Console.WriteLine($"Numbers: {string.Join(", ", numbers)}");
Console.WriteLine($"Temps: {string.Join(", ", temperatures)}");

Spread işleci (..), bir koleksiyonun öğelerini başka bir koleksiyona yayar ve bu, dizileri birleştirmek için kullanışlıdır.

List<int> first = [1, 2, 3];
List<int> second = [4, 5, 6];

// Spread both lists into a new combined list
List<int> combined = [.. first, .. second];
Console.WriteLine(string.Join(", ", combined));
// Output: 1, 2, 3, 4, 5, 6

// Add extra elements alongside spreads
List<int> withExtras = [0, .. first, 99, .. second];
Console.WriteLine(string.Join(", ", withExtras));
// Output: 0, 1, 2, 3, 99, 4, 5, 6

Koleksiyon ifadeleri diziler, List<T>, Span<T>, ImmutableArray<T>ve koleksiyon oluşturucu desenini destekleyen herhangi bir türle çalışır. Tam söz dizimi başvurusu için bkz. Koleksiyon ifadeleri.

Sözlük başlatma

Sözlükleri dizin oluşturucu başlatıcılarıyla kısa bir şekilde başlatabilirsiniz. Bu söz diziminde anahtar-değer çiftlerini ayarlamak için köşeli ayraçlar kullanılır:

Dictionary<string, int> scores = new()
{
    ["Alice"] = 95,
    ["Bob"] = 87,
    ["Carol"] = 92
};

foreach (var (name, score) in scores)
{
    Console.WriteLine($"{name}: {score}");
}

Bir sözlük kopyalayıp geçersiz kılmalar uygulayarak sözlükleri birleştirebilirsiniz:

Dictionary<string, int> defaults = new()
{
    ["Timeout"] = 30,
    ["Retries"] = 3
};
Dictionary<string, int> overrides = new()
{
    ["Timeout"] = 60
};

// Merge defaults and overrides into a new dictionary
Dictionary<string, int> config = new(defaults);
foreach (var (key, value) in overrides)
{
    config[key] = value;
}

Console.WriteLine($"Timeout: {config["Timeout"]}");  // 60
Console.WriteLine($"Retries: {config["Retries"]}");   // 3

Tür kısıtlamaları

Kısıtlamalar, bir genel türün veya yöntemin kabul ettiği tür bağımsız değişkenlerini sınırlar. Kısıtlamalar, tür parametresinde tek başına kullanılamayabilecek object yöntemleri çağırmanıza veya özelliklere erişmenize olanak sağlar:

static T Max<T>(T a, T b) where T : IComparable<T> =>
    a.CompareTo(b) >= 0 ? a : b;

Console.WriteLine(Max(3, 7));          // 7
Console.WriteLine(Max("apple", "banana")); // banana

static T CreateDefault<T>() where T : new() => new T();

var list = CreateDefault<List<int>>(); // Creates an empty List<int>
Console.WriteLine($"Empty list count: {list.Count}"); // 0

En yaygın kısıtlamalar şunlardır:

Kısıtlama Anlamı
where T : class T bir başvuru türü olmalıdır
where T : struct T null atanamayan bir değer türü olmalıdır
where T : new() T ortak bir parametresiz oluşturucuya sahip olmalıdır
where T : BaseClass T şu kaynaktan türetilmelidir: BaseClass
where T : IInterface T uygulamak zorundadır IInterface

Kısıtlamaları birleştirebilirsiniz: where T : class, IComparable<T>, new(). Daha az yaygın sınırlamalar, özel senaryolar için where T : System.Enum, where T : System.Delegate ve where T : unmanaged içerir. Listenin tamamı için bkz. Tür parametrelerindeki kısıtlamalar.

Kovaryans ve kontravaryans

Kovaryans ve kontravaryans , genel türlerin devralma ile nasıl davrandığını açıklar. Başlangıçta belirtilenden daha fazla veya daha az türetilmiş bir tür bağımsız değişkeni kullanıp kullanamayacağınızı onlar belirler.

// Covariance: IEnumerable<Dog> can be used as IEnumerable<Animal>
// because IEnumerable<out T> is covariant
List<Dog> dogs = [new("Rex"), new("Buddy")];
IEnumerable<Animal> animals = dogs; // Allowed because Dog derives from Animal

foreach (var animal in animals)
{
    Console.WriteLine(animal.Name);
}

// Contravariance: Action<Animal> can be used as Action<Dog>
// because Action<in T> is contravariant
Action<Animal> printAnimal = a => Console.WriteLine($"Animal: {a.Name}");
Action<Dog> printDog = printAnimal; // Allowed because any Animal handler can handle Dog

printDog(new Dog("Spot"));
  • Kovaryans (out T): Çünkü Dog, Animal'den türetilmiştir, IEnumerable<Animal> yerine IEnumerable<Dog> kullanılabilir. out tür parametresindeki anahtar sözcük bunu etkinleştirir. Kovarant tür parametreleri yalnızca çıkış konumlarında (dönüş türleri) görünebilir.
  • Kontravaryans (in T): Bir Action<Animal>ın Action<Dog>ın beklendiği yerde kullanılabilmesi mümkündür çünkü Action<Dog>'ü işleyebilen herhangi bir işlem Dog'i de işleyebilir. anahtar in sözcüğü bunu etkinleştirir. Kontravaryant tür parametreleri yalnızca giriş konumlarında (parametreler) görünebilir.

Birçok yerleşik arabirim ve temsilci zaten değişkendir: IEnumerable<out T>, IReadOnlyList<out T>, Func<out TResult>ve Action<in T>. Bu türlerle çalışırken varyanstan otomatik olarak yararlanabilirsiniz. Değişken arabirimleri ve temsilciler tasarlamaya yönelik ayrıntılı bir işlem için bkz. Kovaryans ve ters değişken.

Kendi genel türlerinizi oluşturma

Kendi genel sınıflarınızı, yapılarınızı, arabirimlerinizi ve yöntemlerinizi tanımlayabilirsiniz. Aşağıdaki örnekte, çizim için basit bir genel bağlantılı liste gösterilmektedir. Uygulamada List<T> veya başka bir yerleşik koleksiyon kullanın.

public class GenericList<T>
{
    private class Node(T data)
    {
        public T Data { get; set; } = data;
        public Node? Next { get; set; }
    }

    private Node? head;

    public void AddHead(T data)
    {
        var node = new Node(data) { Next = head };
        head = node;
    }

    public IEnumerator<T> GetEnumerator()
    {
        var current = head;
        while (current is not null)
        {
            yield return current.Data;
            current = current.Next;
        }
    }
}
var list = new GenericList<int>();
for (var i = 0; i < 5; i++)
{
    list.AddHead(i);
}

foreach (var item in list)
{
    Console.Write($"{item} ");
}
Console.WriteLine();
// Output: 4 3 2 1 0

Genel türler sınıflar ile sınırlı değildir. Genel interface, structve record türlerini tanımlayabilirsiniz. Genel algoritmalar ve karmaşık kısıtlama bileşimleri tasarlama hakkında daha fazla bilgi için bkz. .NET'da Generics.

Ayrıca bakınız