Tür parametreleriyle ilgili kısıtlamalar (C# Programlama Kılavuzu)
Kısıtlamalar, bir tür bağımsız değişkeninin sahip olması gereken özellikler hakkında derleyiciye bilgi sağlar. Herhangi bir kısıtlama olmadan, tür bağımsız değişkeni herhangi bir tür olabilir. Derleyici yalnızca herhangi bir .NET türü için System.Object en üst düzey temel sınıf olan 'ın üyelerini varsay olabilir. Daha fazla bilgi için bkz. Neden kısıtlamaları kullanmalı? İstemci kodu kısıtlamayı karşılamayan bir tür kullanıyorsa, derleyici bir hata verir. Kısıtlamalar bağlamsal anahtar sözcük where kullanılarak belirtilir. Aşağıdaki tabloda çeşitli kısıtlama türleri liste almaktadır:
| Kısıtlama | Description |
|---|---|
where T : struct |
Tür bağımsız değişkeni, null değere sahip olmayan bir değer türünde olmalıdır. Null değere değiştirilebilir değer türleri hakkında bilgi için bkz. Null değer türleri. Tüm değer türlerinin erişilebilir bir parametresiz oluşturucusu olduğundan kısıtlama kısıtlamayı struct new() ifade eder ve kısıtlamayla new() birleştirilemez. Kısıtlamayı kısıtlamayla struct unmanaged birleştiresiniz. |
where T : class |
Tür bağımsız değişkeni bir başvuru türü olması gerekir. Bu kısıtlama herhangi bir sınıf, arabirim, temsilci veya dizi türü için de geçerlidir. C# 8.0 veya sonraki bir bağlamda null değere değiştirilebilir bir başvuru T türü olmalıdır. |
where T : class? |
Tür bağımsız değişkeni, null veya null değere sahip olmayan 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 bağlamda null değere değiştirilebilir, 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ı belirtilebilir. Kısıtlanmış arabirim de genel olabilir. C# 8.0'da null değere değiştirilebilir bir bağlamda, null değerdilemez bir başvuru türü veya T bir değer türü olabilir. T null değere değiştirilebilir bir değer türü olamaz. |
where T : U |
için sağlanan tür bağımsız T değişkeni, için sağlanan bağımsız değişkenden türetildi veya türetildi. U Null değere değerdilemez bir başvuru türü ise null değerdilemez U T başvuru türü olmalıdır. , null değere değiştirilebilir bir başvuru türü ise, U T null veya null değere sahip olmayan olabilir. |
Kısıtlamaları neden kullan
Kısıtlamalar, bir tür parametresinin özelliklerini ve beklentilerini belirtir. Bu kısıtlamaların bildirilmesi, kısıtlama türünün işlemlerini ve yöntem çağrılarını kullanabileceğiniz anlamına gelir. Genel sınıfınız veya yönteminiz basit atamanın ötesinde genel üyeler üzerinde herhangi bir işlem kullanıyorsa veya tarafından desteklenmiyorsa, tür parametresine kısıtlamalar System.Object uygulayabilirsiniz. Örneğin, temel sınıf kısıtlaması derleyiciye yalnızca bu türdeki veya bu türden türetilen nesnelerin tür bağımsız değişkenleri olarak kullan olacağını söyler. Derleyici bu garantiye sahip olduktan sonra, bu tür yöntemlerin genel sınıfta çağrılmalarına izin ve olabilir. Aşağıdaki kod örneği, bir temel sınıf kısıtlaması uygulayarak sınıfına ek olarak (Genel Türlere Giriş GenericList<T> içinde) ekln işlevsellik gösterir.
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ıtlaması, genel sınıfın özelliğini kullanmasına olanak Employee.Name sağlar. Kısıtlama, türdeki tüm öğelerin bir nesne veya 'den devralınan T Employee bir nesne olması garantisini Employee belirtir.
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()
{
// ...
}
Kısıtlamayı uygularken, bu işleçler değer eşitliği için değil yalnızca başvuru kimliği için test gerçekleştirecekleri için tür parametresinde where T : class == ve != işleçlerini kaçının. Bu davranış, bu işleçler bağımsız değişken olarak kullanılan bir türe 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 için geçerli T 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.Objectaçı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
nullzaman 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ı gerekende 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ı ve sınıf bağlamında sınırsız Add 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 belirtilebilir. Diğer kısıtlamaların aksine, 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ını uygulamazsa türetilmiş sınıfların bir şekilde kısıtlama olmadan temel yönteme uygulanan bir geçersiz class struct kılma belirtmesi gerekir. Türetilmiş yöntem kısıtlamayı default uygular. Kısıtlama default ne ne de kısıtlamasını net bir şekilde class struct gösterir.
Unmanaged constraint
C# 7.3'den başerek, tür parametresinin null değerdilemez bir unmanaged türü olması gerektiğini belirtmek için unmanaged kısıtlamasını kullanabilirsiniz. Kısıtlama, aşağıdaki örnekte gösterildiği gibi bellek blokları olarak işlenebilir türlerle çalışmak için yeniden unmanaged kullanılabilir yordamlar yazmanıza olanak 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;
}
Yerleşik tür olarak bilinmediği bir tür üzerinde işleci kullandığı için yukarıdaki yöntemin unsafe sizeof bir bağlamda derlenmiş olması gerekir. Kısıtlama unmanaged olmadan sizeof işleç kullanılamaz.
Kısıtlama, unmanaged struct kısıtlamayı ifade eder ve kısıtlamayla birleştirileyemz. Kısıtlama struct kısıtlamayı new() ifade ettiği unmanaged için kısıtlamayla da new() birleştirilenemleri vardır.
Kısıtlamaları temsilci olarak seç
Ayrıca C# 7.3'den itibaren veya temel System.Delegate sınıf System.MulticastDelegate kısıtlaması olarak kullanabilirsiniz. CLR bu kısıtlamaya her zaman izin verdi, ancak C# dili buna izin verilmiyor. Kısıtlama, System.Delegate temsilcilerle tür güvenli bir şekilde çalışan kod yazmanız için olanak sağlar. Aşağıdaki kod, aynı türde olması şartıyla iki temsilciyi 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üre sahip 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ı açıklarsanız derlemez. hem first hem de temsilci test türleridir, ancak bunlar farklı temsilci türleridir.
Enum kısıtlamaları
C# 7.3'den başerek türü temel System.Enum sınıf kısıtlaması olarak da belirtsiniz. 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.
Bunu aşağıdaki örnekte gösterildiği gibi kullanarak bir enum oluşturabilir ve değerleriyle adlarına sahip bir sözlük oluşturabilirsiniz:
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}");