Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A korlátozások tájékoztatják a fordítót arról, hogy milyen képességekkel kell rendelkeznie egy típusargumentumnak. Korlátozások nélkül a típusargumentum bármilyen típus lehet. A fordító csak a .NET-típus végső alaposztályának System.Objecttagjait feltételezheti. További információ: Miért használjon korlátozásokat. Ha az ügyfélkód olyan típust használ, amely nem felel meg a korlátozásnak, a fordító hibát ad ki. A kényszereket a where környezetfüggő kulcsszóval adhatók meg. Az alábbi táblázat a kényszerek különböző típusait sorolja fel:
| Megszorítás | Leírás |
|---|---|
where T : struct |
A típusargumentumnak nem null értékű értéknek kell lennie, amely típusokat is tartalmaz record struct . A null értékű értéktípusokról további információt a Null értékű értéktípusok című témakörben talál. Mivel minden értéktípushoz elérhető paraméter nélküli konstruktor van deklarálva vagy implicit módon, a struct kényszer a new() kényszert jelenti, és nem kombinálható a new() kényszerrel. A kényszer nem kombinálható a structunmanaged kényszerrel. |
where T : class |
A típusargumentumnak referenciatípusnak kell lennie. Ez a korlátozás minden osztályra, felületre, delegálásra vagy tömbtípusra is érvényes. Null értékű környezetben T nem null értékű hivatkozástípusnak kell lennie. |
where T : class? |
A típusargumentumnak null értékű vagy nem null értékű hivatkozástípusnak kell lennie. Ez a korlátozás minden osztályra, felületre, delegálásra vagy tömbtípusra is vonatkozik, beleértve a rekordokat is. |
where T : notnull |
A típusargumentumnak nem null értékű típusnak kell lennie. Az argumentum lehet nem null értékű hivatkozástípus vagy nem null értékű értéktípus. |
where T : unmanaged |
A típusargumentumnak nem null értékű , nem felügyelt típusnak kell lennie. A unmanaged kényszer a kényszert struct jelenti, és nem kombinálható sem a kényszerekkel, sem a structnew() korlátozásokkal. |
where T : new() |
A típusargumentumnak nyilvános paraméter nélküli konstruktorsal kell rendelkeznie. Ha más korlátozásokkal együtt használják, a kényszert utoljára new() kell megadni. A new() kényszer nem kombinálható a korlátozásokkal és struct a unmanaged korlátozásokkal. |
where T :
<alaposztály neve> |
A típusargumentumnak a megadott alaposztályból kell lennie vagy származnia. Null értékű környezetben a megadott alaposztályból származó nem T null értékű hivatkozástípusnak kell lennie. |
where T :
<alaposztály neve>? |
A típusargumentumnak a megadott alaposztályból kell lennie vagy származnia. Null értékű környezetben T lehet null értékű vagy nem null értékű típus, amely a megadott alaposztályból származik. |
where T :
<interfész neve> |
A típusargumentumnak a megadott illesztőnek kell lennie vagy implementálnia. Több felületi korlátozás is megadható. A korlátozó felület általános is lehet. Null értékű környezetben a megadott felületet megvalósító nem T null értékű típusnak kell lennie. |
where T :
<felület neve>? |
A típusargumentumnak a megadott illesztőnek kell lennie vagy implementálnia. Több felületi korlátozás is megadható. A korlátozó felület általános is lehet. Null értékű környezetben T lehet null értékű hivatkozástípus, nem null értékű hivatkozástípus vagy értéktípus.
T nem lehet null értékű típus. |
where T : U |
A megadott típusargumentumnak T a megadott Uargumentumból kell vagy származnia. Null értékű környezetben, ha U nem null értékű hivatkozástípus, T akkor nem null értékű hivatkozástípusnak kell lennie. Ha U null értékű hivatkozástípus, T akkor lehet null értékű vagy nem null értékű. |
where T : default |
Ez a korlátozás feloldja a kétértelműséget, ha nem engedélyezett típusparamétert kell megadnia egy metódus felülbírálásakor vagy explicit felületi implementáció biztosításakor. A default kényszer azt jelenti, hogy az alapmetódus vagy a classstruct kényszer nélkül is elérhető. További információkért tekintse meg a default kényszer-specifikáció javaslatát. |
where T : allows ref struct |
Ez az anti-korlátozás azt deklarálja, hogy a típus argumentuma T lehet típus ref struct . Az általános típusnak vagy metódusnak minden példányra be kell tartania T a ref biztonsági szabályokat, mert lehet, hogy egy ref struct. |
Egyes kényszerek kölcsönösen kizárják egymást, és egyes korlátozásoknak meghatározott sorrendben kell lenniük:
- Legfeljebb az
structegyik ,class, ,class?notnullésunmanagedkorlátozás alkalmazható. Ha ezen kényszerek bármelyikét megadja, annak kell lennie az adott típusparaméterhez megadott első kényszernek. - Az alaposztály kényszere (
where T : Basevagy ) nem kombinálható egyetlen kényszerrelwhere T : Base?sem ,structsemclassclass?notnull.unmanaged - Legfeljebb egy alaposztály-korlátozást alkalmazhat mindkét formában. Ha támogatni szeretné a null értékű alaptípust, használja a következőt
Base?: . - Az illesztő nem null értékű és null értékű formáját sem nevezheti kényszernek.
- A
new()kényszer nem kombinálható a korlátozással vagystructaunmanagedkorlátozással. Ha megadja anew()kényszert, annak kell lennie az adott típusparaméter utolsó kényszerének. Az esetleges korlátozások a kényszert követiknew(). - A
defaultkorlátozás csak felülbírálási vagy explicit felületi implementációkra alkalmazható. Nem kombinálható sem a korlátozásokkal, sem astructclasskorlátozásokkal. - Az
allows ref structanti-korlátozás nem kombinálható a korlátozással vagyclassaclass?korlátozással. - Az
allows ref structanti-kényszernek követnie kell az adott típusparaméterre vonatkozó összes korlátozást.
Miért érdemes kényszereket használni?
A korlátozások megadják egy típusparaméter képességeit és elvárásait. A korlátozások deklarálása azt jelenti, hogy használhatja a korlátozástípus műveleteit és metódushívásait. A típusparaméterre korlátozásokat alkalmazhat, ha az általános osztály vagy metódus az egyszerű hozzárendelésen túl bármilyen műveletet használ az általános tagokon, beleértve a nem támogatott metódusok meghívását System.Objectis. Az alaposztály-korlátozás például azt jelzi a fordítónak, hogy csak az ilyen típusú vagy ebből a típusból származtatott objektumok helyettesíthetik ezt a típusargumentumot. Ha a fordító rendelkezik ezzel a garanciával, lehetővé teszi az ilyen típusú metódusok meghívását az általános osztályban. Az alábbi példakód bemutatja az osztályhoz hozzáadható funkciókat (a Bevezetés a GenericList<T> Genericsba) egy alaposztály-korlátozás alkalmazásával.
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(t) { Next = head };
head = n;
}
public IEnumerator<T> GetEnumerator()
{
Node? current = head;
while (current is not null)
{
yield return current.Data;
current = current.Next;
}
}
public T? FindFirstOccurrence(string s)
{
Node? current = head;
while (current is not null)
{
//The constraint enables access to the Name property.
if (current.Data.Name == s)
{
return current.Data;
}
else
{
current = current.Next;
}
}
return null;
}
}
A korlátozás lehetővé teszi, hogy az általános osztály használja a tulajdonságot Employee.Name . A korlátozás azt határozza meg, hogy az összes elemtípus T garantáltan objektum Employee vagy egy olyan objektum legyen, amelytől Employeeöröklődik.
Ugyanarra a típusparaméterre több kényszer is alkalmazható, és maguk a kényszerek lehetnek általános típusok, az alábbiak szerint:
class EmployeeList<T> where T : notnull, Employee, IComparable<T>, new()
{
public void AddDefault()
{
T t = new();
}
}
A kényszer alkalmazásakor where T : class kerülje a típusparaméter és == operátorok != használatát, mert ezek az operátorok csak a referencia-identitást tesztelik, nem az értékegyenlőséget. Ez a viselkedés akkor is előfordul, ha ezek az operátorok túlterheltek egy argumentumként használt típusban. A következő kód ezt a pontot szemlélteti; a kimenet hamis, annak ellenére, hogy az String osztály túlterheli az operátort == .
public static void OpEqualsTest<T>(T s, T t) where T : class
{
Console.WriteLine(s == t);
}
private static void TestStringEquality()
{
string s1 = "target";
System.Text.StringBuilder sb = new("target");
string s2 = sb.ToString();
OpEqualsTest(s1, s2);
}
A fordító csak azt tudja, hogy T fordításkor ez egy referenciatípus, és az összes referenciatípusra érvényes alapértelmezett operátorokat kell használnia. Ha tesztelnie kell az értékegyenlőséget, alkalmazza a where T : IEquatable<T> korlátozást, where T : IComparable<T> és implementálja a felületet az általános osztály létrehozásához használt bármely osztályban.
Több paraméter korlátozása
A kényszereket több paraméterre, egy paraméterre pedig több kényszerre is alkalmazhatja, ahogyan az alábbi példában látható:
class Base { }
class Test<T, U>
where U : struct
where T : Base, new()
{ }
Kötetlen típusparaméterek
Az olyan típusparamétereket, amelyek nem rendelkeznek korlátozásokkal, például A nyilvános osztályban SampleClass<T>{}, kötetlen típusparamétereknek nevezzük. A kötetlen típusparaméterek a következő szabályokkal rendelkeznek:
- Az
!=és==operátorok nem használhatók, mert nincs garancia arra, hogy a konkrét típusú argumentum támogatja ezeket az operátorokat. - Ezek konvertálhatók bármely felülettípusra, amelyből származnak
System.Object, vagy explicit módon átalakíthatók. - A null értékhez hasonlíthatja őket. Ha egy kötetlen paraméterrel van összehasonlítva
null, az összehasonlítás mindig hamis értéket ad vissza, ha a típusargumentum értéktípus.
Paraméterek beírása kényszerként
Az általános típusparaméter kényszerként való használata akkor hasznos, ha egy saját típusparaméterrel rendelkező tagfüggvénynek ezt a paramétert a tartalmazó típus típusparaméteréhez kell korlátoznia, ahogyan az az alábbi példában látható:
public class List<T>
{
public void Add<U>(List<U> items) where U : T {/*...*/}
}
Az előző példában T a metódus kontextusában Add egy típuskényszer, az osztály környezetében List pedig egy kötetlen típusparaméter látható.
A típusparaméterek az általános osztálydefiníciók kényszereiként is használhatók. A típusparamétert a szögletes zárójeleken belül kell deklarálni bármely más típusparaméterrel együtt:
//Type parameter V is used as a type constraint.
public class SampleClass<T, U, V> where T : V { }
A típusparaméterek általános osztályokra vonatkozó megkötésekként való hasznossága korlátozott, mert a fordító semmit sem tud feltételezni a típusparaméterről, kivéve, hogy az származik System.Objectbelőle. Típusparaméterek használata az általános osztályokra vonatkozó korlátozásokként olyan helyzetekben, amelyekben két típusparaméter közötti öröklési kapcsolatot szeretne kikényszeríteni.
notnull kényszer
A kényszer használatával notnull megadhatja, hogy a típusargumentumnak nem null értékű vagy nem null értékű hivatkozástípusnak kell lennie. A legtöbb más kényszertől eltérően, ha egy típusargumentum megsérti a notnull kényszert, a fordító hibaüzenet helyett figyelmeztetést hoz létre.
A notnull kényszer csak null értékű környezetben való használat esetén érvényes. Ha a notnull kényszert null értékű, oblivious környezetben adja hozzá, a fordító nem generál figyelmeztetést vagy hibát a kényszer megsértése esetén.
class kényszer
A class null értékű környezet kényszere azt határozza meg, hogy a típusargumentumnak nem null értékű hivatkozástípusnak kell lennie. Null értékű környezetben, ha egy típusargumentum null értékű hivatkozástípus, a fordító figyelmeztetést hoz létre.
default kényszer
A null értékű hivatkozástípusok hozzáadása bonyolítja az általános típus vagy metódus használatát T? .
T?használható a korlátozással vagy struct a class korlátozással, de az egyiknek jelen kell lennie. A class kényszer használatakor T? a null értékű hivatkozástípusra hivatkozik a következőhöz T: .
T? akkor használható, ha egyik korlátozást sem alkalmazza. Ebben az esetben T? a rendszer az értéktípusok és a referenciatípusok esetében értelmezi T? . Ha T azonban egy példány Nullable<T>, akkor ugyanaz, T? mint Ta . Más szóval, ez nem válik T??.
Mivel T? most már a kényszer vagy class a struct korlátozás nélkül is használható, kétértelműségek merülhetnek fel a felülbírálásokban vagy explicit felületi implementációkban. Mindkét esetben a felülbírálás nem tartalmazza a korlátozásokat, hanem az alaposztálytól örökli őket. Ha az alaposztály nem alkalmazza sem a classstruct kényszert, a származtatott osztályoknak valamilyen módon felül kell írniuk az alapmetódusra vonatkozó felülbírálást kényszer nélkül. A származtatott módszer alkalmazza a kényszert default . A default kényszer nem tisztázza sem a kényszert, semclass a korlátozáststruct.
Nem felügyelt kényszer
A kényszer használatával unmanaged megadhatja, hogy a típusparaméternek nem null értékű , nem felügyelt típusnak kell lennie. A unmanaged korlátozás lehetővé teszi, hogy újrahasználható rutinokat írjon a memóriablokkokként kezelhető típusokkal való munkához, ahogyan az az alábbi példában látható:
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;
}
Az előző metódust egy unsafe környezetben kell lefordítani, mert az sizeof operátort olyan típuson használja, amely nem ismert, hogy beépített típus. A unmanaged korlátozás nélkül az sizeof operátor nem érhető el.
A unmanaged kényszer a kényszert struct jelenti, és nem kombinálható vele. Mivel a struct kényszer a new() kényszert jelenti, a unmanaged kényszer nem kombinálható a new() kényszerrel is.
Delegálási korlátozások
Használhatja System.Delegate vagy System.MulticastDelegate alaposztály-korlátozásként is. A Common Language Runtime (CLR) mindig engedélyezte ezt a korlátozást, de a C# nyelv nem engedélyezte. A System.Delegate korlátozás lehetővé teszi olyan kód írását, amely típusbiztos módon működik a meghatalmazottakkal. A következő kód egy olyan bővítménymetódust határoz meg, amely két meghatalmazottat egyesít, feltéve, hogy azonos típusúak:
extension<TDelegate>(TDelegate source) where TDelegate : System.Delegate
{
public TDelegate? TypeSafeCombine(TDelegate target)
=> Delegate.Combine(source, target) as TDelegate;
}
Az előző metódussal azonos típusú meghatalmazottakat egyesíthet:
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);
Ha visszavonja az utolsó sort, az nem fordítja le. Mindkettő first delegálási test típus, de eltérő delegálási típus.
Enumerálási korlátozások
A típust System.Enum alaposztály-korlátozásként is megadhatja. A CLR mindig engedélyezte ezt a korlátozást, de a C# nyelv nem engedélyezte. Az általános generikusok System.Enum típusbiztos programozást biztosítanak a statikus metódusok által kapott eredmények gyorsítótárazásához a következőben System.Enum: . Az alábbi minta megkeresi az enum típus összes érvényes értékét, majd létrehoz egy szótárt, amely ezeket az értékeket a sztring-ábrázoláshoz rendeli.
extension<T>(T) where T : System.Enum
{
public static Dictionary<int, string> EnumNamedValues()
{
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 és Enum.GetName használjon tükröződést, amely teljesítménybeli következményekkel jár. Meghívhat EnumNamedValues egy gyorsítótárazott és újrafelhasznált gyűjteményt ahelyett, hogy megismételte a tükrözést igénylő hívásokat.
Az alábbi példában látható módon létrehozhat egy enumerát, és létrehozhat egy szótárt az értékeiből és a neveiből:
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}");
A típusargumentumok deklarált felületet implementálnak
Egyes forgatókönyvek megkövetelik, hogy egy típusparaméterhez megadott argumentum implementálja ezt az interfészt. Példa:
public interface IAdditionSubtraction<T> where T : IAdditionSubtraction<T>
{
static abstract T operator +(T left, T right);
static abstract T operator -(T left, T right);
}
Ez a minta lehetővé teszi a C#-fordító számára, hogy meghatározza a túlterhelt operátorok vagy metódusok static virtualstatic abstract tartalmazó típusát. Megadja a szintaxist, hogy az összeadási és kivonási operátorok definiálhatók legyenek egy tartalmazó típuson. E kényszer nélkül a paramétereket és argumentumokat interfészként kell deklarálni a típusparaméter helyett:
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);
}
Az előző szintaxis megköveteli, hogy a implementátorok explicit felületi implementációt használjanak ezekhez a módszerekhez. Az extra korlátozás megadása lehetővé teszi, hogy az interfész a típusparaméterek szempontjából határozza meg az operátorokat. Az interfészt megvalósító típusok implicit módon implementálhatják a felületi metódusokat.
Átfstruktúra engedélyezése
Az allows ref struct anti-constraint deklarálja, hogy a megfelelő típusargumentum típus lehet ref struct . Az ilyen típusú paraméterek példányainak a következő szabályoknak kell eleget tennie:
- Nem lehet bekeretezett.
- Részt vesz a ref biztonsági szabályokban.
- A példányok nem használhatók olyan esetekben, ahol
ref structegy típus nem engedélyezett, példáulstaticmezők. - A példányok megjelölhetők a
scopedmódosítóval.
A allows ref struct záradék nem öröklődik. A következő kódban:
class SomeClass<T, S>
where T : allows ref struct
where S : T
{
// etc
}
Az argumentum S nem lehet, ref struct mert S nem rendelkezik a záradékkal allows ref struct .
A záradékot tartalmazó allows ref struct típusparaméter csak akkor használható típusargumentumként, ha a megfelelő típusparaméter is rendelkezik a allows ref struct záradékkal. Ezt a szabályt a következő példában mutatjuk be:
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
}
Az előző minta azt mutatja, hogy ref struct egy típus argumentuma nem helyettesíthető olyan típusparaméterrel, amely nem lehet ref struct típus.