Tür Parametrelerindeki Kısıtlamalar (C# Programlama Kılavuzu)

Kısıtlamalar, derleyicisini bir tür bağımsız değişkeni olması gereken yetenekler hakkında bilgilendirir. Herhangi bir kısıtlama olmadan tür bağımsız değişkeni herhangi bir tür olabilir. Derleyici yalnızca System.Object , tüm .net türleri için en son temel sınıf olan üyelerini kabul edebilir. Daha fazla bilgi için bkz. kısıtlamaların neden kullanılması. İstemci kodu bir kısıtlamayı karşılamayan bir tür kullanıyorsa, derleyici bir hata verir. Kısıtlamalar, where bağlamsal anahtar sözcüğü kullanılarak belirtilir. Aşağıdaki tabloda çeşitli türlerdeki kısıtlamalar listelenmektedir:

Kısıtlaması Description
where T : struct Tür bağımsız değişkeni null yapılamayan bir değer türüolmalıdır. Nullable değer türleri hakkında daha fazla bilgi için bkz. Nullable değer türleri. Tüm değer türlerinde erişilebilir parametresiz bir Oluşturucu olduğundan, kısıtlama struct new() kısıtlamayı gösterir ve new() kısıtlamayla birleştirilemez. Kısıtlamayı struct unmanaged kısıtlamasıyla birleştiremezsiniz.
where T : class Tür bağımsız değişkeni bir başvuru türü olmalıdır. Bu kısıtlama, her sınıf, arabirim, temsilci veya dizi türü için de geçerlidir. C# 8,0 veya sonraki bir sürümde null yapılabilir bir bağlamda, T null atanamaz bir başvuru türü olmalıdır.
where T : class? Tür bağımsız değişkeni, null yapılabilir veya null yapılamayan bir başvuru türü olmalıdır. Bu kısıtlama herhangi bir sınıf, arabirim, temsilci veya dizi türü için de geçerlidir.
where T : notnull Tür bağımsız değişkeni null değere sahip olmayan bir tür olmalıdır. Bağımsız değişken, C# 8.0 veya sonraki bir dosyada null değere sahip olmayan bir başvuru türü veya null değere sahip olmayan bir değer türü olabilir.
where T : default Bu kısıtlama, bir yöntemi geçersiz kılarken veya açık bir arabirim uygulaması sağlarken sınırlandırlanmamış tür parametresi belirtmeniz gereken belirsizlikleri çözer. Kısıtlama, default veya kısıtlaması olmadan temel yöntemi class ifade struct eder. Daha fazla bilgi için bkz. default Kısıtlama özellikleri teklifi.
where T : unmanaged Tür bağımsız değişkeni, null değere sahip olmayan bir unmanaged türünde olmalıdır. Kısıtlama unmanaged kısıtlamayı struct ifade eder ve veya kısıtlamalarıyla struct new() birleştirilemiyor.
where T : new() Tür bağımsız değişkeninin genel bir parametresiz oluşturucusu olmalıdır. Diğer kısıtlamalarla birlikte kullanılırken new() kısıtlamanın en son belirtilmesi gerekir. Kısıtlama, new() ve kısıtlamalarıyla struct unmanaged birleştirilenemz.
where T : <base class name> Tür bağımsız değişkeni, belirtilen temel sınıftan türetilmelidir. C# 8.0 ve sonraki bir içinde null değere değiştirilebilir bağlamda, belirtilen temel sınıftan türetilen null değere sahip olmayan T bir başvuru türü olmalıdır.
where T : <base class name>? Tür bağımsız değişkeni, belirtilen temel sınıftan türetilmelidir. C# 8.0 ve sonraki bir içinde null değere değiştirilebilir bağlamda, belirtilen temel sınıftan türetilen null veya null değerdilemez T bir tür olabilir.
where T : <interface name> Tür bağımsız değişkeninin belirtilen arabirim olması veya uygulanması gerekir. Birden çok arabirim kısıtlaması belirtilebilir. Kısıtlanmış arabirim de genel olabilir. C# 8.0 ve sonraki bir içinde null değere değiştirilebilir bağlamda, belirtilen arabirimi uygulayan null değere sahip olmayan T bir tür olmalıdır.
where T : <interface name>? Tür bağımsız değişkeninin belirtilen arabirim olması veya uygulanması gerekir. Birden çok arabirim kısıtlaması belirlenebilir. Kısıtlama arabirimi de genel olabilir. C# 8,0 ' de null yapılabilir bir bağlamda, T null yapılabilir bir başvuru türü, null olamayan bir başvuru türü veya değer türü olabilir. T null yapılabilen bir değer türü olmayabilir.
where T : U İçin sağlanan tür bağımsız değişkeni, T için sağlanan bağımsız değişkenden türetilmiş veya türetilmiş olmalıdır U . Null yapılabilir bir bağlamda, U null atanamaz bir başvuru türü ise, T null olamayan bir başvuru türü olmalıdır. UNull yapılabilir bir başvuru türü ise, T null yapılabilir veya null atanamaz olabilir.

Neden kısıtlama kullanılmalıdır

Kısıtlamalar, bir tür parametresinin yeteneklerini ve beklentilerini belirtir. Bu kısıtlamaları bildirmek, kısıtlama türünün işlemlerini ve Yöntem çağrılarını kullanabileceğiniz anlamına gelir. Genel bir sınıf veya yönteminiz, Genel Üyeler üzerinde basit atamanın ötesinde veya tarafından desteklenmeyen herhangi bir yöntemi çağıran herhangi bir işlem kullanıyorsa System.Object , tür parametresine kısıtlamalar uygularsınız. Örneğin, temel sınıf kısıtlaması derleyiciye yalnızca bu türden veya bu türden türetilmiş nesnelerin tür bağımsız değişkenleri olarak kullanılacağını söyler. Derleyiciye bu garanti verildikten sonra, genel sınıfta bu tür yöntemlerin çağrılmasına izin verebilir. Aşağıdaki kod örneği, GenericList<T> bir temel sınıf kısıtlaması uygulayarak sınıfına ekleyebileceğiniz işlevselliği gösterir ( Genel türlere girişolarak).

public class Employee
{
    public Employee(string name, int id) => (Name, ID) = (name, id);
    public string Name { get; set; }
    public int ID { get; set; }
}

public class GenericList<T> where T : Employee
{
    private class Node
    {
        public Node(T t) => (Next, Data) = (null, t);

        public Node Next { get; set; }
        public T Data { get; set; }
    }

    private Node head;

    public void AddHead(T t)
    {
        Node n = new Node(t) { Next = head };
        head = n;
    }

    public IEnumerator<T> GetEnumerator()
    {
        Node current = head;

        while (current != null)
        {
            yield return current.Data;
            current = current.Next;
        }
    }

    public T FindFirstOccurrence(string s)
    {
        Node current = head;
        T t = null;

        while (current != null)
        {
            //The constraint enables access to the Name property.
            if (current.Data.Name == s)
            {
                t = current.Data;
                break;
            }
            else
            {
                current = current.Next;
            }
        }
        return t;
    }
}

Kısıtlama, genel sınıfın özelliğini kullanmasını sağlar Employee.Name . Kısıtlama, türündeki tüm öğelerin T bir Employee nesne ya da öğesinden devralan nesne olduğunu belirtir Employee .

Aynı tür parametresine birden çok kısıtlama uygulanabilir ve kısıtlamalar aşağıdaki gibi genel türler olabilir:

class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
{
    // ...
}

where T : classKısıtlama uygulanırken == != tür parametresindeki ve işleçlerden kaçının, çünkü bu işleçler yalnızca başvuru kimliğini test edecek, değer eşitlik için değil. Bu davranış, bu işleçler bağımsız değişken olarak kullanılan bir türde aşırı yüklense bile oluşur. Aşağıdaki kod bu noktayı göstermektedir; sınıf işleci aşırı yüklese String bile çıkış == false olur.

public static void OpEqualsTest<T>(T s, T t) where T : class
{
    System.Console.WriteLine(s == t);
}

private static void TestStringEquality()
{
    string s1 = "target";
    System.Text.StringBuilder sb = new System.Text.StringBuilder("target");
    string s2 = sb.ToString();
    OpEqualsTest<string>(s1, s2);
}

Derleyici bunun yalnızca derleme zamanında bir başvuru türü olduğunu bilir ve tüm başvuru türleri T için geçerli olan varsayılan işleçleri kullanması gerekir. Değer eşitliğini sınamanız gerekirse önerilen yol, veya kısıtlamasını uygulamak ve genel sınıfı oluşturmak için kullanılacak herhangi bir sınıfta arabirimi where T : IEquatable<T> where T : IComparable<T> uygulamaktır.

Birden çok parametreyi sınırlama

Aşağıdaki örnekte gösterildiği gibi, kısıtlamaları birden çok parametreye ve tek bir parametreye birden çok kısıtlama uygulayabilirsiniz:

class Base { }
class Test<T, U>
    where U : struct
    where T : Base, new()
{ }

Sınırsız tür parametreleri

Genel sınıftaki T gibi hiçbir kısıtlaması olmayan tür parametreleri SampleClass<T>{} sınırsız tür parametreleri olarak çağrılır. Sınırsız tür parametreleri aşağıdaki kurallara sahip:

  • Somut != tür bağımsız değişkeninin bu işleçleri desteklemesi garanti edilemez olduğundan ve == işleçleri kullanılamaz.
  • Bunlar herhangi bir arabirim türüne veya arabirim türüne dönüştürmek veya System.Object açıkça dönüştürmek için kullanılabilir.
  • Bunları null değeriyle karşılaştırarak. Sınırsız parametre ile karşılaştırıldı ise, tür bağımsız değişkeni bir değer türü ise karşılaştırma her null zaman false döndürür.

Parametreleri kısıtlama olarak yazın

Genel tür parametresinin kısıtlama olarak kullanımı, kendi tür parametresine sahip bir üye işlevinin, aşağıdaki örnekte gösterildiği gibi bu parametreyi içeren türün tür parametresiyle kısıtlaması gerektirse yararlıdır:

public class List<T>
{
    public void Add<U>(List<U> items) where U : T {/*...*/}
}

Önceki örnekte, T yöntemi bağlamında bir tür kısıtlaması Add ve sınıf bağlamında sınırsız tür List parametresidir.

Tür parametreleri, genel sınıf tanımlarında kısıtlamalar olarak da kullanılabilir. Tür parametresi, diğer tür parametreleriyle birlikte açılı ayraç içinde bildir gerekir:

//Type parameter V is used as a type constraint.
public class SampleClass<T, U, V> where T : V { }

Tür parametrelerinin genel sınıflarla kısıtlamalar olarak kullanışlılığı sınırlıdır çünkü derleyici tür parametresiyle ilgili hiçbir şey varsaymayabilirsiniz, ancak türünden türetilmesi System.Object dışında. İki tür parametresi arasında devralma ilişkisi uygulamak istediğiniz senaryolarda tür parametrelerini genel sınıflarda kısıtlamalar olarak kullanın.

notnull kısıtlaması

C# 8.0'dan başerek kısıtlamayı kullanarak tür bağımsız değişkeninin null değerdilemez bir değer türü veya null değere sahip olmayan bir başvuru türü notnull olması gerektiğini belirtebilirsiniz. Diğer kısıtlamaların aksine, bir tür bağımsız değişkeni kısıtlamayı ihlal notnull ediyorsa, derleyici hata yerine bir uyarı üretir.

Kısıtlamanın notnull etkisi yalnızca boş değere değiştirilebilir bir bağlamda kullanılır. Kısıtlamayı boş değere değiştirilebilir bir bağlamda eklersiniz, derleyici kısıtlama ihlalleri için herhangi bir uyarı notnull veya hata oluşturmaz.

class kısıtlaması

C# 8.0'dan itibaren null değere değiştirilebilir bir bağlamdaki kısıtlama, tür bağımsız değişkeninin null değerdilemez bir başvuru class türü olması gerektiğini belirtir. Null değere değiştirilebilir bağlamda, bir tür bağımsız değişkeni null değere değiştirilebilir bir başvuru türü olduğunda, derleyici bir uyarı üretir.

default kısıtlaması

Null değere değiştirilebilir başvuru türlerinin bir genel tür veya T? yöntemde kullanımını karmaşık hale döndürür. C# 8'den önce yalnızca T? kısıtlama struct uygulandığında T kullanılabilirdi. Bu bağlamda, T? Nullable<T> türüne başvurur. T C# 8'den başlayarak veya kısıtlaması ile T? struct class kullanılabilir, ancak bunlardan birinin mevcut olması gerekir. Kısıtlama class kullanılırken için T? null değere değiştirilebilir başvuru türüne başvurıldı. T C# 9'dan T? itibaren, hiçbir kısıtlama uygulanmazsa kullanılabilir. Bu durumda, T? değer türleri ve başvuru türleri için C# 8'de olduğu gibi yorumlanır. Ancak, T bir örneği ise Nullable<T> ile T? T aynıdır. Başka bir deyişle, haline T?? değil.

Artık T? veya kısıtlaması olmadan kullanılabı olduğundan, geçersiz kılmalarda veya açık arabirim class struct uygulamalarında belirsizlikler ortaya çıkabilir. Her iki durumda da geçersiz kılma kısıtlamaları içermez, ancak temel sınıftan devralınır. Temel sınıf veya kısıtlaması uygulamadığı zaman class struct , türetilmiş sınıfların herhangi bir kısıtlama olmadan taban yöntemine uygulanan bir geçersiz kılma işlemini belirtmesi gerekir. Türetilmiş yöntemin kısıtlaması uyguladığı bu default . defaultKısıtlama ne class ne de struct kısıtlama belirler.

Yönetilmeyen kısıtlama

C# 7,3 ' den başlayarak, unmanaged tür parametresinin null atanamaz yönetilmeyen bir türolması gerektiğini belirtmek için kısıtlamasını kullanabilirsiniz. unmanagedKısıtlama, aşağıdaki örnekte gösterildiği gibi, bellek blokları olarak işlenebilen türlerle çalışmak için yeniden kullanılabilir yordamlar yazmanızı sağlar:

unsafe public static byte[] ToByteArray<T>(this T argument) where T : unmanaged
{
    var size = sizeof(T);
    var result = new Byte[size];
    Byte* p = (byte*)&argument;
    for (var i = 0; i < size; i++)
        result[i] = *p++;
    return result;
}

Önceki yöntem, unsafe sizeof yerleşik bir tür olarak bilinen bir tür üzerinde işleç kullandığından bir bağlamda derlenmelidir. Kısıtlama olmadan unmanaged sizeof işleç kullanılamaz.

unmanagedKısıtlama struct kısıtlamayı belirtir ve onunla birleştirilemez. Kısıtlama struct kısıtlamayı gösterdiği için kısıtlama new() unmanaged kısıtlama ile birleştirilemez new() .

Temsilci kısıtlamaları

C# 7,3 ' den itibaren, System.Delegate ya da System.MulticastDelegate temel sınıf kısıtlaması olarak kullanabilirsiniz. CLR bu kısıtlamaya her zaman izin verilir, ancak C# dili izin vermez. System.DelegateKısıtlama, temsilcilerle birlikte, tür açısından güvenli bir şekilde kod yazmanıza olanak sağlar. Aşağıdaki kod, iki temsilciyi aynı tür olduklarından birleştiren bir genişletme yöntemi tanımlar:

public static TDelegate TypeSafeCombine<TDelegate>(this TDelegate source, TDelegate target)
    where TDelegate : System.Delegate
    => Delegate.Combine(source, target) as TDelegate;

Aynı türdeki temsilcileri birleştirmek için yukarıdaki yöntemi kullanabilirsiniz:

Action first = () => Console.WriteLine("this");
Action second = () => Console.WriteLine("that");

var combined = first.TypeSafeCombine(second);
combined();

Func<bool> test = () => true;
// Combine signature ensures combined delegates must
// have the same type.
//var badCombined = first.TypeSafeCombine(test);

Son satırın açıklamasını kaldırırsanız, derlenmez. Hem hem de first test temsilci türlerdir, ancak farklı temsilci türleridir.

Sabit listesi kısıtlamaları

C# 7,3 ' den başlayarak, System.Enum türü temel sınıf kısıtlaması olarak da belirtebilirsiniz. CLR bu kısıtlamaya her zaman izin verdi, ancak C# dili buna izin verilmiyor. kullanan genel System.Enum türler, içinde statik yöntemleri kullanarak sonuçları önbelleğe almak için tür kullanımı güvenli programlama System.Enum sağlar. Aşağıdaki örnek, bir enum türü için tüm geçerli değerleri bulur ve ardından bu değerleri dize gösterimiyle eşleyen bir sözlük derlemez.

public static Dictionary<int, string> EnumNamedValues<T>() where T : System.Enum
{
    var result = new Dictionary<int, string>();
    var values = Enum.GetValues(typeof(T));

    foreach (int item in values)
        result.Add(item, Enum.GetName(typeof(T), item));
    return result;
}

Enum.GetValues ve Enum.GetName performans etkileri olan yansımayı kullanır. Yansıma gerektiren EnumNamedValues çağrıları yinelemek yerine önbelleğe alınmış ve yeniden kullanılan bir koleksiyon oluşturmak için çağrısı da kullanabilirsiniz.

Bir enum oluşturmak ve değerleriyle adlarına sahip bir sözlük oluşturmak için aşağıdaki örnekte gösterildiği gibi kullanabilirsiniz:

enum Rainbow
{
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Indigo,
    Violet
}
var map = EnumNamedValues<Rainbow>();

foreach (var pair in map)
    Console.WriteLine($"{pair.Key}:\t{pair.Value}");

Ayrıca bkz.