Tür parametrelerindeki kısıtlamalar (C# Programlama Kılavuzu)
Kısıtlamalar, derleyiciyi bir tür bağımsız değişkeninin sahip olması gereken özellikler hakkında 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 öğesinin System.Objectüyelerini varsayabilir. 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, türleri içeren null atanamaz bir değer türü olmalıdır.record struct Boş değer türleri hakkında bilgi için bkz . Null atanabilir değer türleri. Tüm değer türlerinin, bildirilen veya örtük olarak 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 null 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, kayıtlar da dahil olmak üzere tüm sınıf, arabirim, temsilci veya dizi türleri için de geçerlidir. |
where T : notnull |
Tür bağımsız değişkeni null atanamaz bir tür olmalıdır. Bağımsız değişken, boş değer atanamayan bir başvuru türü veya null atanamayan bir değer türü olabilir. |
where T : unmanaged |
Tür bağımsız değişkeni null atanamaz yönetilmeyen bir tür olmalıdır. Kısıtlama unmanaged 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şturucusunun 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 bir bağlamda, T belirtilen temel sınıftan türetilmiş 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üretilmiş 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 bir 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, T null atanabilir bir başvuru türü, null atanamayan başvuru türü veya değer türü olabilir. T null atanabilir bir değer türü olamaz. |
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 veya türünden türemelidir. Boş değer atanabilir bir bağlamda, boş değer atanamayan bir başvuru türüyse U , T boş değer atanamayan bir başvuru türü olmalıdır. Boş değer atanabilir bir başvuru türüyse U , T null atanabilir veya null atanamaz 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ış tür parametresi belirtmeniz gerektiğinde belirsizliği giderir. kısıtlamasıdefault , veya struct kısıtlaması olmadan class temel yöntemi gösterir. Daha fazla bilgi için kısıtlama belirtimi teklifine default bakın. |
where T : allows ref struct |
Bu kısıtlama önleme, için T tür bağımsız değişkeninin bir ref struct tür olabileceğini bildirir. Genel tür veya yöntem, herhangi bir örneği T için başvuru güvenlik kurallarına uymalıdır çünkü bu bir ref struct olabilir. |
Bazı kısıtlamalar birbirini dışlar ve bazı kısıtlamalar belirtilen sırada olmalıdır:
- , ,
class
,class?
notnull
veunmanaged
kısıtlamalarınınstruct
en çoğunu uygulayabilirsiniz. Bu kısıtlamalardan herhangi birini sağlarsanız, bu tür parametresi için belirtilen ilk kısıtlama olmalıdır. - Temel sınıf kısıtlaması (
where T : Base
veyawhere T : Base?
), , ,notnull
class
class?
veyaunmanaged
kısıtlamalarıylastruct
birleştirilemiyor. - Her iki biçimde de en fazla bir temel sınıf kısıtlaması uygulayabilirsiniz. Boş değer atanabilir taban türünü desteklemek istiyorsanız kullanın
Base?
. - Bir arabirimin hem null atanamaz hem de null atanabilir biçimini kısıtlama olarak adlandıramazsınız.
- Kısıtlama
new()
veyaunmanaged
kısıtlamasıylastruct
birleştirilemiyor. Kısıtlamayınew()
belirtirseniz, bu tür parametresi için son kısıtlama olmalıdır. Varsa kısıtlama önleme, kısıtlamayınew()
izleyebilir. - Kısıtlama
default
yalnızca geçersiz kılma veya açık arabirim uygulamalarına uygulanabilir. Veya kısıtlamalarıylastruct
class
birleştirilemiyor. - Kısıtlama
allows ref struct
önleme, veyaclass?
kısıtlamasıylaclass
birleştirilemiyor. - Kısıtlama önleme,
allows ref struct
bu tür parametresi için tüm kısıtlamaları izlemelidir.
Kısıtlamalar neden kullanılır?
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 tarafından desteklenmeyen System.Objectyöntemleri çağırmayı içeren basit atamanın ötesinde genel üyeler üzerinde herhangi bir işlem kullandığında 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üretilmiş nesnelerin bu tür bağımsız değişkeninin yerini aabileceğini söyler. Derleyici bu garantiye sahip olduktan sonra, bu türdeki yöntemlerin genel sınıfta çağrılmasına izin verebilir. Aşağıdaki kod örneği, temel sınıf kısıtlaması uygulayarak sınıfa GenericList<T>
ekleyebileceğiniz işlevselliği gösterir (Genel Giriş'te).
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 bir Employee
nesne veya öğesinden Employee
devralan bir nesne olmasını garanti eder.
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 : notnull, Employee, IComparable<T>, new()
{
// ...
public void AddDefault()
{
T t = new T();
// ...
}
}
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 sınar. Bu işleçler bağımsız değişken olarak kullanılan bir türde aşırı yüklenmiş olsa bile bu davranış oluşur. Aşağıdaki kod bu noktayı gösterir; sınıfı işleci 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 test etmeniz gerekiyorsa, veya where T : IComparable<T>
kısıtlamasını where T : IEquatable<T>
uygulayın ve genel sınıfı oluşturmak için kullanılan herhangi bir sınıfta arabirimini uygulayın.
Birden çok parametreyi kısıtlama
Aşağıdaki örnekte gösterildiği gibi 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()
{ }
İlişkisiz tür parametreleri
Genel sınıftaki SampleClass<T>{}
T gibi hiçbir kısıtlaması olmayan tür parametreleri, ilişkisiz tür parametreleri olarak adlandırılır. İlişkisiz tür parametreleri aşağıdaki kurallara sahiptir:
- Somut tür bağımsız değişkeninin
!=
bu işleçleri desteklediğinin garantisi olmadığından 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 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ıldığında, tür bağımsız değişkeni bir değer türü olduğunda karşılaştırma her zaman false döndürür.
Parametreleri kısıtlama olarak yazın
Bir genel tür parametresinin kısıtlama olarak kullanılması, 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 sınırlaması gerektiğinde kullanışlı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 ilişkisiz bir 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çlar 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 kısıtlama olarak kullanışlılığı sınırlıdır, çünkü derleyici tür parametresi hakkında hiçbir şey varsayamaz, ancak parametresinden System.Object
türetilir. tür parametrelerini, iki tür parametresi arasında devralma ilişkisini zorlamak istediğiniz senaryolarda genel sınıflarda kısıtlamalar olarak kullanın.
notnull
kısıtlama
Tür bağımsız değişkeninin notnull
null atanamaz bir değer türü veya null atanamaz başvuru türü olması gerektiğini belirtmek için kısıtlamayı kullanabilirsiniz. Diğer kısıtlamaların çoğundan farklı olarak, 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 bir bağlamda kullanıldığında bir etkisi vardır. Kısıtlamayı notnull
boş 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ıtlama
Null class
atanabilir bağlamdaki kısıtlama, tür bağımsız değişkeninin null atanamaz bir başvuru türü olması gerektiğini belirtir. Boş değer atanabilir bir bağlamda, 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ıtlama
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 biri mevcut olmalıdır. class
Kısıtlama kullanıldığında, T?
için T
null atanabilir başvuru türüne başvurur. 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>T?
ise T
ile 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 bir geçersiz kılmanın temel yönteme uygulanacağını belirtmesi gerekir. Türetilmiş yöntem kısıtlamayı default
uygular. Kısıtlama default
ne ne de struct
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 bir tür olduğu bilinmeyen bir unsafe
tür üzerinde işlecini sizeof
kullandığından, önceki yöntemin bir bağlamda derlenmesi gerekir. unmanaged
Kısıtlama olmadan işleç sizeof
kullanılamaz.
Kısıtlama, unmanaged
kısıtlamayı struct
gösterir ve bununla birleştirilemiyor. Kısıtlama kısıtlamayı struct
ima ettiğinden new()
unmanaged
, kısıtlama da kısıtlamayla new()
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 izin vermemiş. Kısıtlama, 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ö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ırdığınızda, derlenmez. Hem hem test
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 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ğırabilirsiniz EnumNamedValues
.
Bir sabit listesi oluşturmak ve değerlerinin ve adlarının bir sözlüğü 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ı senaryolarda, tür parametresi için sağlanan bir bağımsız değişkenin bu arabirimi uygulaması gerekir. Örneğin:
public interface IAdditionSubtraction<T> where T : IAdditionSubtraction<T>
{
static abstract T operator +(T left, T right);
static abstract 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çerik türünü 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>
{
static abstract IAdditionSubtraction<T> operator +(
IAdditionSubtraction<T> left,
IAdditionSubtraction<T> right);
static abstract 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 işleçleri tür parametreleri açısından tanımlamasını sağlar. Arabirimi uygulayan türler, arabirim yöntemlerini örtük olarak uygulayabilir.
Başvuru yapısına izin verir
Kısıtlama önleme, allows ref struct
karşılık gelen tür bağımsız değişkeninin bir ref struct
tür olabileceğini bildirir. Bu tür parametrenin örnekleri aşağıdaki kurallara uymalıdır:
- Kutulanamaz.
- Başvuru güvenliği kurallarına katılır.
- Alanlar gibi
static
birref struct
türe izin verilmeyen örnekler kullanılamaz. - Örnekler değiştirici ile
scoped
işaretlenebilir.
allows ref struct
Yan tümcesi devralınmıyor. Aşağıdaki kodda:
class SomeClass<T, S>
where T : allows ref struct
where S : T
{
// etc
}
bağımsız değişkeniS
, yan tümcesine allows ref struct
sahip olmadığından bir ref struct
S
olamaz.
Yan tümcesine allows ref struct
sahip bir tür parametresi, ilgili tür parametresinde yan tümcesi olmadığı allows ref struct
sürece tür bağımsız değişkeni olarak kullanılamaz. Bu kural aşağıdaki örnekte gösterilmiştir:
public class Allow<T> where T : allows ref struct
{
}
public class Disallow<T>
{
}
public class Example<T> where T : allows ref struct
{
private Allow<T> fieldOne; // Allowed. T is allowed to be a ref struct
private Disallow<T> fieldTwo; // Error. T is not allowed to be a ref struct
}
Yukarıdaki örnekte, tür olabilecek bir ref struct
tür bağımsız değişkeninin, tür olmayan bir tür parametresiyle değiştirilebileceği gösterilmektedir ref struct
.