Y a-t-il un moyen d'ajouter une contrainte de type à une classe générique à partir d'une variable?
class GenericClass<{X}>
{
}
où {X} est par exemple un tableau de types comme: Object1, Object2, String
3 Réponses :
Vous ne pouvez pas contraindre un tableau.
Un tableau n'est pas vraiment un type, c'est un type faux : une structure de données de mémoire de base brute comme un train avec des wagons comme cellules même si Array est une classe pour les manipuler.
Dans le code généré par IL, l'utilisation du mot-clé new est transposée dans newarr code > opérande:
types de classe: aucune instance n'est créée, seulement l'allocation de cellules pour les références qui sont nulles par défaut.
types de valeur: aucune instance n'est créée, uniquement l'allocation de cellules pour les valeurs.
La contrainte d'argument de type générique doit être effectuée sur un type réel .
Vous pouvez utiliser une List à la place qui est un type générique implémentation orientée objet du concept de tableau (il s'agit en fait d'un wrapper vers un tableau).
Vous pouvez écrire par exemple:
// _ = new int[10]; IL_0000: ldc.i4.s 10 IL_0002: newarr [mscorlib]System.Int32 IL_0007: pop // _ = new List<int>[10]; IL_0008: ldc.i4.s 10 IL_000a: newarr class [mscorlib]System.Collections.Generic.List`1<int32> IL_000f: pop // _ = new Data[10]; IL_0010: ldc.i4.s 10 IL_0012: newarr ConsoleApp.Program/Data IL_0017: pop // new List<int>(); IL_0018: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
Mais c'est inutile.
Par conséquent, vous pouvez écrire:
static void Test()
{
var arr1 = new int[10];
var arr2 = new List<int>[10];
var arr3 = new Data[10];
var instance = new List<int>();
}
public struct Data
{
public int Value;
}
class MustHaveConstraints<T1, T2>
where T1: List<T2>
{
}
var instance1 = new MustHaveConstraints<List<object>, object>();
var instance2 = new MustHaveConstraints<List<int>, int>();
var instance3 = new MustHaveConstraints<List<string>, string>();
var instance4 = new MustHaveConstraints<List<bool[]>, bool[]>();
class MustHaveConstraints<X>
where X : List<string>`
{
}
https://docs.microsoft.com/dotnet/api/ system.reflection.emit.opcodes.newarr
https://docs.microsoft.com/dotnet/api/system.reflection.emit.opcodes.newobj
Que diriez-vous de quelque chose comme ça?
class Class1 : IConstraint {}
class Class2 : IConstraint {}
interface IConstraint {}
class MustHaveConstraints<T> where T : IConstraint {}
Si vos types ne sont que des classes, ce type d'implémentation sera plus flexible.
MISE À JOUR
Comme @DRapp l'a suggéré, cela peut également être fait avec des classes. Fondamentalement, lorsque vous définissez un type générique, vous pouvez ajouter une restriction d'utilisation avec des contraintes. Pour plus de détails, veuillez consulter ce lien
Mis à part l'interface, le WHERE pourrait s'appliquer à une classe de base dont d'autres classes sont dérivées. Je vous suggère de modifier votre réponse et de montrer ce scénario aussi ... Ex: Classe Animal, sous-classe Chien, Chat, etc ... ce sont tous des animaux et chacun peut avoir ses propres composants personnalisés.
En réponse à ma propre question, oui c'est possible mais pas de manière conventionnelle. Il faudrait utiliser des modèles de texte pour générer ces classes
<#@ var types = "T1, T2, T3"; #>
namespace Generics
{
class GenericClass<<#=types#>>
{
}
}
Pourquoi voulez-vous l'utiliser? J'espère que nous pouvons suggérer des alternatives
Je crois que je viens de répondre à ma propre question, je peux utiliser des modèles de texte pour générer ces classes où je peux définir les types dont j'ai besoin sous forme de tableau.
Vous pouvez générer des fichiers .cs statiques, puis les charger ou utiliser System.Reflection.Emit créant efficacement votre type au moment de l'exécution. Vous pouvez peut-être aussi utiliser Roslyn
Pas vraiment une possibilité de créer des objets au moment de l'exécution pour cette solution.