Tür parametrelerindeki 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 derleyiciyi bilgilendirmektedir. 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 nihai temel sınıf olan üyelerini System.Objectvarsayabilir. Daha fazla bilgi için bkz . Kısıtlamalar neden kullanılır? İstemci kodu bir kısıtlamayı karşılamayan bir tür kullanıyorsa, derleyici bir hata döndürür. Kısıtlamalar bağlamsal anahtar sözcük kullanılarak where
belirtilir. Aşağıdaki tabloda çeşitli kısıtlama türleri listelenmiştir:
Kısıtlama | Açıklama |
---|---|
where T : struct |
Tür bağımsız değişkeni null değer atanamayan bir değer türü olmalıdır. Boş değer atanabilir değer türleri hakkında bilgi için bkz. Null atanabilir değer türleri. Tüm değer türleri erişilebilir bir parametresiz oluşturucuya sahip olduğundan, struct kısıtlama kısıtlamayı new() gösterir ve kısıtlamayla new() birleştirilemez. Kısıtlamayı struct kısıtlamayla unmanaged 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 herhangi bir sınıf, arabirim, temsilci veya dizi türü için de geçerlidir. Boş değer atanabilir bir bağlamda, T boş değer atanamayan bir başvuru türü olmalıdır. |
where T : class? |
Tür bağımsız değişkeni null atanabilir veya null atanamaz 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 atanamayan bir tür olmalıdır. Bağımsız değişken, boş değer atanamayan bir başvuru türü veya boş değer atanamayan 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 kısıtlanmamış bir tür parametresi belirtmeniz gerektiğinde belirsizliği giderir. Kısıtlama, default veya struct kısıtlaması olmadan class temel yöntemi ifade eder. Daha fazla bilgi için kısıtlama belirtimi teklifine default bakın. |
where T : unmanaged |
Tür bağımsız değişkeni null atanamaz yönetilmeyen bir tür olmalıdır. Kısıtlamaunmanaged , kısıtlamayı struct gösterir ve veya new() kısıtlamalarıyla struct birleştirilemiyor. |
where T : new() |
Tür bağımsız değişkeninin genel parametresiz oluşturucuya sahip olması gerekir. Diğer kısıtlamalarla birlikte kullanıldığında kısıtlamanın new() en son belirtilmesi gerekir. Kısıtlama new() ve unmanaged kısıtlamalarıyla struct birleştirilemiyor. |
where T : <temel sınıf adı> |
Tür bağımsız değişkeni belirtilen temel sınıftan türemelidir veya türemelidir. Null atanabilir bağlamda, T belirtilen temel sınıftan türetilen null atanamaz bir başvuru türü olmalıdır. |
where T : <temel sınıf adı>? |
Tür bağımsız değişkeni belirtilen temel sınıftan türemelidir veya türemelidir. Null atanabilir bir bağlamda, T belirtilen temel sınıftan türetilen null atanabilir veya null atanamaz bir tür olabilir. |
where T : <arabirim adı> |
Tür bağımsız değişkeninin belirtilen arabirim olması veya uygulaması gerekir. Birden çok arabirim kısıtlaması belirtilebilir. Kısıtlayan arabirim de genel olabilir. Null atanabilir bağlamda, T belirtilen arabirimi uygulayan null atanamaz bir tür olmalıdır. |
where T : <arabirim adı>? |
Tür bağımsız değişkeninin belirtilen arabirim olması veya uygulaması gerekir. Birden çok arabirim kısıtlaması belirtilebilir. Kısıtlayan arabirim de genel olabilir. Boş değer atanabilir bir bağlamda null T atanabilir bir başvuru türü, boş değer atanamayan başvuru türü veya değer türü olabilir. T null atanabilir bir değer türü olmayabilir. |
where T : U |
için sağlanan tür bağımsız değişkeni, için T U sağlanan bağımsız değişkenden türetilmelidir. Null atanabilir bir bağlamda, boş değer atanamayan bir başvuru türüyse U , T boş değer atanamayan başvuru türü olmalıdır. Boş değer atanabilir bir başvuru türüyse U , T boş değer atanabilir veya boş değer atanamaz olabilir. |
Kısıtlamaları neden kullanmalısınız?
Kısıtlamalar, bir tür parametresinin özelliklerini ve beklentilerini belirtir. Bu kısıtlamaları bildirmek, kısıtlayan türü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 System.Objectdesteklenmeyen yöntemleri çağırıyorsa, tür parametresine kısıtlamalar uygularsınız. Ö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ılacağını söyler. Derleyici bu garantiye sahip olduktan sonra, bu türdeki yöntemlerin genel sınıfında çağrılmasına izin verebilir. Aşağıdaki kod örneği, temel sınıf kısıtlaması uygulayarak sınıfına GenericList<T>
ekleyebileceğiniz işlevselliği gösterir ( Genel Türlere Giriş bölümünde).
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ını Employee.Name
sağlar. kısıtlaması, türündeki T
tüm öğelerin öğesinden Employee
devralan bir Employee
nesne veya nesne olması garanti edilir.
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ı where T : class
uygularken tür parametresinde ve !=
işleçlerinden kaçının ==
çünkü bu işleçler değer eşitliği için değil yalnızca başvuru kimliği için test eder. Bu davranış, bu işleçler bağımsız değişken olarak kullanılan bir türe aşırı yüklenmiş olsa bile oluşur. Aşağıdaki kodda bu nokta gösterilmektedir; sınıfı işlecini aşırı yüklese String bile çıkış false değeridir ==
.
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 yalnızca derleme zamanında bir başvuru türü olduğunu T
bilir ve tüm başvuru türleri için geçerli olan varsayılan işleçleri kullanmalıdır. Değer eşitliğini sınamanız gerekiyorsa önerilen yol, veya where T : IComparable<T>
kısıtlamasını where T : IEquatable<T>
uygulamak ve arabirimini genel sınıfı oluşturmak için kullanılacak herhangi bir sınıfta uygulamaktır.
Birden çok parametreyi kısıtlama
Aşağıdaki örnekte gösterildiği gibi, birden çok parametreye kısıtlamalar ve tek bir parametreye birden çok kısıtlama uygulayabilirsiniz:
class Base { }
class Test<T, U>
where U : struct
where T : Base, new()
{ }
İlişkisiz tür parametreleri
Genel sınıftaki SampleClass<T>{}
T gibi kısıtlaması olmayan tür parametrelerine ilişkisiz tür parametreleri adı verilir. İlişkisiz tür parametreleri aşağıdaki kurallara sahiptir:
- Somut tür bağımsız değişkeninin
!=
bu işleçleri desteklemesi garanti edilmediğinden ve==
işleçleri kullanılamaz. - Bunlar, herhangi bir arabirim türüne ve türüne
System.Object
dönüştürülebilir veya herhangi bir arabirim türüne açıkça dönüştürülebilir. - Bunları null ile karşılaştırabilirsiniz. İlişkisiz bir parametre ile
null
karşılaştırılırsa, tür bağımsız değişkeni bir değer türündeyse karşılaştırma her zaman false döndürür.
Parametreleri kısıtlama olarak yazın
Aşağıdaki örnekte gösterildiği gibi, kendi tür parametresine sahip bir üye işlevinin bu parametreyi içeren türün tür parametresiyle kısıtlaması gerektiğinde, kısıtlama olarak genel tür parametresinin kullanılması 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 Add
bir tür kısıtlaması ve sınıfı bağlamında bir ilişkisiz tür parametresidir List
.
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 bildirilmelidir:
//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 sınırlamalar olarak kullanışlılığı sınırlıdır çünkü derleyici tür parametresi hakkında hiçbir şey varsayabileceğinden, parametresinden System.Object
türetilir. İki tür parametresi arasında devralma ilişkisini zorlamak istediğiniz senaryolarda tür parametrelerini genel sınıflarda kısıtlamalar olarak kullanın.
notnull
kısıtlaması
Tür bağımsız değişkeninin notnull
null atanamaz bir değer türü veya boş değer atanamayan başvuru türü olması gerektiğini belirtmek için kısıtlamayı kullanabilirsiniz. Diğer kısıtlamaların çoğundan farklı olarak, bir tür bağımsız değişkeni kısıtlamayı notnull
ihlal ederse, derleyici hata yerine bir uyarı oluşturur.
Kısıtlamanın notnull
yalnızca null atanabilir bağlamda kullanıldığında bir etkisi vardır. Kısıtlamayı notnull
null değer atanabilir bir belirsiz bağlama eklerseniz, derleyici kısıtlama ihlalleri için herhangi bir uyarı veya hata oluşturmaz.
class
kısıtlaması
Null class
atanabilir bağlamdaki kısıtlama, tür bağımsız değişkeninin boş değer atanamayan bir başvuru türü olması gerektiğini belirtir. Boş değer atanabilir bağlamda, bir tür bağımsız değişkeni null atanabilir bir başvuru türü olduğunda, derleyici bir uyarı oluşturur.
default
kısıtlaması
Null atanabilir başvuru türlerinin eklenmesi, genel bir tür veya yöntemde kullanımını T?
karmaşıklaştırır. T?
veya class
kısıtlamasıyla struct
kullanılabilir, ancak bunlardan birinin mevcut olması gerekir. class
Kısıtlama kullanıldığında, T?
için T
null atanabilir başvuru türüne başvuruldu. C# 9 ile başlayarak, T?
hiçbir kısıtlama uygulanmadığında kullanılabilir. Bu durumda, T?
değer türleri ve başvuru türleri olarak T?
yorumlanır. Ancak, örneği Nullable<T>ise T
ile T?
aynıdırT
. Başka bir deyişle, haline gelmez T??
.
Artık T?
veya struct
kısıtlaması olmadan class
kullanılabildiğinden geçersiz kılmalarda veya açık arabirim uygulamalarında belirsizlikler ortaya çıkabilir. Her iki durumda da geçersiz kılma kısıtlamaları içermez, ancak bunları temel sınıftan devralır. Temel sınıf veya struct
kısıtlamasını class
uygulamadığında, türetilmiş sınıfların herhangi bir kısıtlama olmadan temel yönteme uygulanacak bir geçersiz kılma belirtmesi gerekir. Türetilen yöntem kısıtlamayı uyguladığında default
. Kısıtlama default
ne ne destruct
kısıtlamasını net bir şekilde class
açıklar.
Yönetilmeyen kısıtlama
tür parametresinin unmanaged
null atanamaz yönetilmeyen bir tür olması gerektiğini belirtmek için kısıtlamasını kullanabilirsiniz. Kısıtlama, unmanaged
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;
}
Yerleşik tür olarak bilinen bir unsafe
tür üzerinde işlecini kullandığından sizeof
, önceki yöntemin bir bağlamda derlenmesi gerekir. unmanaged
Kısıtlama sizeof
olmadan işleç kullanılamaz.
Kısıtlama unmanaged
, kısıtlamayı struct
gösterir ve bununla birleştirilemiyor. struct
Kısıtlama kısıtlamayı ima ettiğinden new()
unmanaged
, kısıtlamayla new()
da birleştirilemiyor.
Temsilci kısıtlamaları
veya System.MulticastDelegate öğesini temel sınıf kısıtlaması olarak kullanabilirsinizSystem.Delegate. CLR bu kısıtlamaya her zaman izin verdi, ancak C# dili bu kısıtlamaya izin vermemiş. kısıtlaması System.Delegate
, temsilcilerle tür açısından güvenli bir şekilde çalışan kod yazmanızı sağlar. Aşağıdaki kod, aynı türde olmaları koşuluyla iki temsilciyi birleştiren bir uzantı yöntemini 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ırdığınızda, bu satır derlenmez. test
Hem hem de first
temsilci türleridir, ancak bunlar farklı temsilci türleridir.
Sabit listesi kısıtlamaları
Türü temel sınıf kısıtlaması olarak da belirtebilirsiniz System.Enum . CLR bu kısıtlamaya her zaman izin verdi, ancak C# dili bu kısıtlamaya izin vermemiş. kullanan System.Enum
genel değerler, içindeki statik yöntemleri System.Enum
kullanarak sonuçları önbelleğe almak için tür açısından güvenli programlama sağlar. Aşağıdaki örnek bir sabit listesi 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 oluşturur.
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 üzerindeki etkileri olan yansımayı kullanın. Yansıma gerektiren çağrıları yinelemek yerine önbelleğe alınmış ve yeniden kullanılan bir koleksiyon oluşturmak için çağrısı EnumNamedValues
yapabilirsiniz.
Bir sabit listesi oluşturmak ve değerleriyle adlarından oluşan 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}");
Tür bağımsız değişkenleri bildirilen arabirimi uygular
Bazı senaryolar, bir tür parametresi için sağlanan bir bağımsız değişkenin bu arabirimi uygulamasını gerektirir. Örnek:
public interface IAdditionSubtraction<T> where T : IAdditionSubtraction<T>
{
public abstract static T operator +(T left, T right);
public abstract static T operator -(T left, T right);
}
Bu düzen, C# derleyicisinin aşırı yüklenmiş işleçler veya herhangi bir static virtual
veya static abstract
yöntem için içeren türü belirlemesini sağlar. Ekleme ve çıkarma işleçlerinin içeren bir tür üzerinde tanımlanabilmesi için söz dizimini sağlar. Bu kısıtlama olmadan, parametrelerin ve bağımsız değişkenlerin tür parametresi yerine arabirim olarak bildirilmesi gerekir:
public interface IAdditionSubtraction<T> where T : IAdditionSubtraction<T>
{
public abstract static IAdditionSubtraction<T> operator +(
IAdditionSubtraction<T> left,
IAdditionSubtraction<T> right);
public abstract static IAdditionSubtraction<T> operator -(
IAdditionSubtraction<T> left,
IAdditionSubtraction<T> right);
}
Yukarıdaki söz dizimi uygulayıcıların bu yöntemler için açık arabirim uygulaması kullanmasını gerektirir. Ek kısıtlamanın sağlanması, arabirimin tür parametreleri açısından işleçleri tanımlamasını sağlar. Arabirimi uygulayan türler, arabirim yöntemlerini örtük olarak uygulayabilir.