J'essaie de créer un nouvel objet de type T via son constructeur lors de l'ajout à la liste.
Je reçois une erreur de compilation: Le message d'erreur est:
'T': ne peut pas fournir d'arguments lors de la création d'une instance d'une variable
Mais mes classes ont un argument constructeur! Comment puis-je faire fonctionner cela?
public static string GetAllItems<T>(...) where T : new()
{
...
List<T> tabListItems = new List<T>();
foreach (ListItem listItem in listCollection)
{
tabListItems.Add(new T(listItem)); // error here.
}
...
}
Réponses:
Afin de créer une instance d'un type générique dans une fonction, vous devez la contraindre avec le "nouveau" indicateur.
Cependant, cela ne fonctionnera que si vous souhaitez appeler le constructeur qui n'a pas de paramètres. Pas le cas ici. Au lieu de cela, vous devrez fournir un autre paramètre qui permet la création d'objet basé sur des paramètres. Le plus simple est une fonction.
Vous pouvez alors l'appeler ainsi
la source
T result = thunk == null ? new T() : thunk();
l'avantage pour moi est de consolider la logique de laT
création en un seul endroit plutôt que de créer parfois à l'T
intérieur et parfois à l'extérieur de la méthode.dans .Net 3.5 et après, vous pouvez utiliser la classe d'activateur:
la source
Activator.CreateInstance
cette chose, il semblerait que votre constructeur ne soit pas utilisé du tout, et quelqu'un pourrait essayer de "nettoyer" et de le supprimer (pour provoquer une erreur d'exécution à dans le futur). Vous voudrez peut-être envisager d'ajouter une fonction fictive dans laquelle vous utilisez ce constructeur afin d'obtenir une erreur de compilation si vous essayez de le supprimer.Étant donné que personne n'a pris la peine de publier la réponse «Réflexion» (que je pense personnellement être la meilleure réponse), voici:
Modifier: cette réponse est obsolète en raison de Activator.CreateInstance de .NET 3.5, mais elle est toujours utile dans les anciennes versions de .NET.
la source
Initialiseur d'objet
Si votre constructeur avec le paramètre ne fait rien d'autre que de définir une propriété, vous pouvez le faire en C # 3 ou mieux en utilisant un initialiseur d'objet plutôt qu'en appelant un constructeur (ce qui est impossible, comme cela a été mentionné):
En utilisant cela, vous pouvez également toujours placer n'importe quelle logique de constructeur dans le constructeur par défaut (vide).
Activator.CreateInstance ()
Alternativement, vous pouvez appeler Activator.CreateInstance () comme ceci:
Notez qu'Activator.CreateInstance peut avoir une surcharge de performances que vous voudrez peut-être éviter si la vitesse d'exécution est une priorité absolue et qu'une autre option peut être maintenue.
la source
T
de protéger ses invariants (étant donné qu'ilT
a> 0 dépendances ou valeurs requises, vous pouvez maintenant créer des instancesT
qui sont dans un état invalide / inutilisable. sauf siT
quelque chose de mort simple comme un modèle de vue DTO och, je dirais éviter cela.Très vieille question, mais nouvelle réponse ;-)
La version ExpressionTree : (je pense que la solution la plus rapide et la plus propre)
Comme l'a dit Welly Tambunan , "nous pourrions également utiliser l'arbre d'expression pour construire l'objet"
Cela générera un «constructeur» (fonction) pour le type / les paramètres donnés. Il renvoie un délégué et accepte les types de paramètres comme un tableau d'objets.
C'est ici:
Exemple MyClass:
Usage:
Un autre exemple: passer les types sous forme de tableau
DebugView of Expression
C'est équivalent au code qui est généré:
Petit inconvénient
Tous les paramètres des types de valeurs sont encadrés lorsqu'ils sont transmis comme un tableau d'objets.
Test de performance simple:
Résultats:
L'utilisation
Expressions
est +/- 8 fois plus rapide que l'invocation deConstructorInfo
et +/- 20 fois plus rapide que l'utilisation de laActivator
la source
var myConstructor = CreateConstructor(typeof(MyClass<int>), typeof(int));
cela fonctionne bien. Je ne sais pas.Cela ne fonctionnera pas dans votre situation. Vous ne pouvez spécifier que la contrainte qu'il a un constructeur vide:
Ce que vous pourriez faire, c'est utiliser l'injection de propriétés en définissant cette interface:
Ensuite, vous pouvez modifier votre méthode comme suit:
L'autre alternative est la
Func
méthode décrite par JaredPar.la source
Vous devez ajouter où T: new () pour que le compilateur sache que T est garanti pour fournir un constructeur par défaut.
la source
Si vous souhaitez simplement initialiser un champ membre ou une propriété avec le paramètre constructeur, en C #> = 3, vous pouvez le faire très facilement:
C'est la même chose que Garry Shutler a dit, mais j'aimerais ajouter une note supplémentaire.
Bien sûr, vous pouvez utiliser une astuce de propriété pour faire plus de choses que simplement définir une valeur de champ. Une propriété "set ()" peut déclencher tout traitement nécessaire pour configurer ses champs associés et tout autre besoin pour l'objet lui-même, y compris une vérification pour voir si une initialisation complète doit avoir lieu avant que l'objet ne soit utilisé, simulant une construction complète ( oui, c'est une solution de contournement laide, mais elle surmonte la nouvelle limitation de M $).
Je ne peux pas être sûr s'il s'agit d'un trou planifié ou d'un effet secondaire accidentel, mais cela fonctionne.
C'est très drôle de voir comment les personnes atteintes de SEP ajoutent de nouvelles fonctionnalités au langage et ne semblent pas faire une analyse complète des effets secondaires. La chose générique entière en est une bonne preuve ...
la source
J'ai trouvé que j'obtenais une erreur «ne peut pas fournir d'arguments lors de la création d'une instance de type paramètre T», j'ai donc dû faire ceci:
la source
Si vous avez accès à la classe que vous allez utiliser, vous pouvez utiliser cette approche que j'ai utilisée.
Créez une interface qui a un créateur alternatif:
Créez vos classes avec un créateur vide et implémentez cette méthode:
Utilisez maintenant vos méthodes génériques:
Si vous n'y avez pas accès, encapsulez la classe cible:
la source
C'est un peu mucky, et quand je dis un peu mucky, je peux vouloir dire révoltant, mais en supposant que vous pouvez fournir votre type paramétré avec un constructeur vide, alors:
Vous permettra effectivement de construire un objet à partir d'un type paramétré avec un argument. Dans ce cas, je suppose que le constructeur que je veux a un seul argument de type
object
. Nous créons une instance fictive de T en utilisant le constructeur vide de contrainte autorisée, puis utilisons la réflexion pour obtenir l'un de ses autres constructeurs.la source
J'utilise parfois une approche qui ressemble aux réponses en utilisant l'injection de propriété, mais garde le code plus propre. Au lieu d'avoir une classe / interface de base avec un ensemble de propriétés, elle ne contient qu'une méthode (virtuelle) Initialize () - qui agit comme un "constructeur du pauvre". Ensuite, vous pouvez laisser chaque classe gérer sa propre initialisation comme le ferait un constructeur, ce qui ajoute également un moyen pratique de gérer les chaînes d'héritage.
Si je me retrouve souvent dans des situations où je veux que chaque classe de la chaîne initialise ses propriétés uniques, puis appelle la méthode Initialize () de son parent, qui à son tour initialise les propriétés uniques du parent, etc. Cela est particulièrement utile lorsque vous avez différentes classes, mais avec une hiérarchie similaire, par exemple des objets métier qui sont mappés vers / depuis DTO: s.
Exemple qui utilise un dictionnaire commun pour l'initialisation:
la source
Si tout ce dont vous avez besoin est une conversion de ListItem vers votre type T, vous pouvez implémenter cette conversion dans la classe T comme opérateur de conversion.
la source
Je crois que vous devez contraindre T avec une instruction where pour autoriser uniquement les objets avec un nouveau constructeur.
Maintenant, il accepte tout, y compris les objets sans lui.
la source