Comment générer un constructeur à partir des champs de classe à l’aide de Visual Studio (et / ou ReSharper)?

Je me suis habitué à de nombreux IDE Java (Eclipse, Netbeans, IntelliJ) en vous fournissant une commande pour générer un constructeur par défaut pour une classe basée sur les champs de la classe.

Par exemple:

public class Example { public decimal MyNumber { get; set; } public ssortingng Description { get; set; } public int SomeInteger { get; set; } // ↓↓↓ This is what I want generated ↓↓↓ public Example(decimal myNumber, ssortingng description, int someInteger) { MyNumber = myNumber; Description = description; SomeInteger = someInteger; } } 

Avoir un constructeur remplissant tous les champs d’un object est une tâche si courante dans la plupart des langages de POO, je suppose qu’il y a un moyen de gagner du temps en écrivant ce code passe-partout en C #. Je suis nouveau dans le monde C #, alors je me demande si je manque quelque chose de fondamental sur le langage? Existe-t-il une option évidente dans Visual Studio?

Resharper propose un outil Generate Constructor dans lequel vous pouvez sélectionner tous les champs / propriétés que vous souhaitez initialiser. J’utilise la touche de raccourci Alt + Ins pour y accéder.

Dans Visual Studio 2015 Update3, j’ai cette fonctionnalité.

juste en mettant en évidence les propriétés, puis appuyez sur ctrl + . puis appuyez sur Générer constructeur.

Par exemple, si vous avez mis en évidence 2 propriétés, il vous proposera de créer un constructeur avec 2 parameters et si vous avez sélectionné 3, il vous en proposera un avec 3 parameters, etc.

fonctionne également avec VS2017.

générer automatiquement la visualisation des raccourcis

C # a ajouté une nouvelle fonctionnalité dans Visual Studio 2010 appelée générer à partir de l’utilisation. L’intention est de générer le code standard à partir d’un modèle d’utilisation. L’une des fonctionnalités est la génération d’un constructeur basé sur un modèle d’initialisation.

La fonctionnalité est accessible via la balise active qui apparaîtra lorsque le motif est détecté.

Par exemple. Disons que j’ai le cours suivant

 class MyType { } 

Et j’écris ce qui suit dans ma candidature

 var v1 = new MyType(42); 

Un constructeur prenant un int n’existe pas, une balise active apparaîtra et l’une des options sera “Générer un talon de constructeur”. Sélectionner cela modifiera le code pour MyType pour être le suivant.

 class MyType { private int p; public MyType(int p) { // TODO: Complete member initialization this.p = p; } } 

Vous pouvez écrire une macro pour ce faire – vous utiliserez l’parsingur de Visual Studio pour récupérer des informations sur les membres de la classe.

J’ai écrit une macro similaire. (Je partagerai le code ci-dessous). La macro que j’ai écrite est pour copier tous les constructeurs d’une classe de base lorsque vous en héritez (utile pour des classes comme Exception qui ont beaucoup de surcharges sur le ctor).

Voici ma macro (encore une fois, cela ne résout pas votre problème, mais vous pouvez probablement modifier pour faire ce que vous voulez)

Imports System Imports EnvDTE Imports EnvDTE80 Imports EnvDTE90 Imports EnvDTE100 Imports System.Diagnostics Public Module ConstructorEditor Public Sub StubConstructors() 'adds stubs for all of the constructors in the current class's base class Dim selection As TextSelection = DTE.ActiveDocument.Selection Dim classInfo As CodeClass2 = GetClassElement() If classInfo Is Nothing Then System.Windows.Forms.MessageBox.Show("No class was found surrounding the cursor. Make sure that this file comstacks and try again.", "Error") Return End If If classInfo.Bases.Count = 0 Then System.Windows.Forms.MessageBox.Show("No parent class was found for this class. Make sure that this file, and any file containing parent classes comstacks and try again") Return End If 'setting up an undo context -- one ctrl+z undoes everything Dim closeUndoContext As Boolean = False If DTE.UndoContext.IsOpen = False Then closeUndoContext = True DTE.UndoContext.Open("StubConstructorsContext", False) End If Try Dim parentInfo As CodeClass2 = classInfo.Bases.Item(1) Dim childConstructors As System.Collections.Generic.List(Of CodeFunction2) = GetConstructors(classInfo) Dim parentConstructors As System.Collections.Generic.List(Of CodeFunction2) = GetConstructors(parentInfo) For Each constructor As CodeFunction2 In parentConstructors If Not MatchingSignatureExists(constructor, childConstructors) Then ' we only want to create ctor stubs for ctors that are missing ' note: a dictionary could be more efficient, but I doubt most classes will have more than 4 or 5 ctors... StubConstructor(classInfo, constructor) End If Next Finally If closeUndoContext Then DTE.UndoContext.Close() End If End Try End Sub Private Function GetConstructors(ByVal classInfo As CodeClass2) As System.Collections.Generic.List(Of CodeFunction2) ' return a list of all of the constructors in the specified class Dim result As System.Collections.Generic.List(Of CodeFunction2) = New System.Collections.Generic.List(Of CodeFunction2) Dim func As CodeFunction2 For Each member As CodeElement2 In classInfo.Members ' members collection has all class members. filter out just the function members, and then of the functions, grab just the ctors func = TryCast(member, CodeFunction2) If func Is Nothing Then Continue For If func.FunctionKind = vsCMFunction.vsCMFunctionConstructor Then result.Add(func) End If Next Return result End Function Private Function MatchingSignatureExists(ByVal searchFunction As CodeFunction2, ByVal functions As System.Collections.Generic.List(Of CodeFunction2)) As Boolean ' given a function (searchFunction), searches a list of functions where the function signatures (not necessarily the names) match ' return null if no match is found, otherwise returns first match For Each func As CodeFunction In functions If func.Parameters.Count <> searchFunction.Parameters.Count Then Continue For Dim searchParam As CodeParameter2 Dim funcParam As CodeParameter2 Dim match As Boolean = True For count As Integer = 1 To searchFunction.Parameters.Count searchParam = searchFunction.Parameters.Item(count) funcParam = func.Parameters.Item(count) If searchParam.Type.AsFullName <> funcParam.Type.AsFullName Then match = False Exit For End If Next If match Then Return True End If Next ' no match found Return False End Function Private Sub StubConstructor(ByVal classInfo As CodeClass2, ByVal parentConstructor As CodeFunction2) ' adds a constructor to the current class, based upon the parentConstructor that is passed in ' highly inefficient hack to position the ctor where I want it (after the last ctor in the class, if there is another ctor ' note that passing zero as the position (put the ctor first) caused some problems when we were adding ctors to classes that already had ctors Dim position As Object Dim ctors As System.Collections.Generic.List(Of CodeFunction2) = GetConstructors(classInfo) If ctors.Count = 0 Then position = 0 Else position = ctors.Item(ctors.Count - 1) End If ' if there are no other ctors, put this one at the top Dim ctor As CodeFunction2 = classInfo.AddFunction(classInfo.Name, vsCMFunction.vsCMFunctionConstructor, vsCMTypeRef.vsCMTypeRefVoid, position, parentConstructor.Access) Dim baseCall As Ssortingng = ":base(" Dim separator As Ssortingng = "" For Each parameter As CodeParameter2 In parentConstructor.Parameters ctor.AddParameter(parameter.Name, parameter.Type, -1) baseCall += separator + parameter.Name separator = ", " Next baseCall += ")" ' and 1 sad hack -- appears to be no way to programmatically add the :base() calls without using direct ssortingng manipulation Dim startPoint As TextPoint = ctor.GetStartPoint() Dim endOfSignature As EditPoint = startPoint.CreateEditPoint() endOfSignature.EndOfLine() endOfSignature.Insert(baseCall) startPoint.CreateEditPoint().SmartFormat(endOfSignature) End Sub Private Function GetClassElement() As CodeClass2 'returns a CodeClass2 element representing the class that the cursor is within, or null if there is no class Try Dim selection As TextSelection = DTE.ActiveDocument.Selection Dim fileCodeModel As FileCodeModel2 = DTE.ActiveDocument.ProjectItem.FileCodeModel Dim element As CodeElement2 = fileCodeModel.CodeElementFromPoint(selection.TopPoint, vsCMElement.vsCMElementClass) Return element Catch Return Nothing End Try End Function End Module
Imports System Imports EnvDTE Imports EnvDTE80 Imports EnvDTE90 Imports EnvDTE100 Imports System.Diagnostics Public Module ConstructorEditor Public Sub StubConstructors() 'adds stubs for all of the constructors in the current class's base class Dim selection As TextSelection = DTE.ActiveDocument.Selection Dim classInfo As CodeClass2 = GetClassElement() If classInfo Is Nothing Then System.Windows.Forms.MessageBox.Show("No class was found surrounding the cursor. Make sure that this file comstacks and try again.", "Error") Return End If If classInfo.Bases.Count = 0 Then System.Windows.Forms.MessageBox.Show("No parent class was found for this class. Make sure that this file, and any file containing parent classes comstacks and try again") Return End If 'setting up an undo context -- one ctrl+z undoes everything Dim closeUndoContext As Boolean = False If DTE.UndoContext.IsOpen = False Then closeUndoContext = True DTE.UndoContext.Open("StubConstructorsContext", False) End If Try Dim parentInfo As CodeClass2 = classInfo.Bases.Item(1) Dim childConstructors As System.Collections.Generic.List(Of CodeFunction2) = GetConstructors(classInfo) Dim parentConstructors As System.Collections.Generic.List(Of CodeFunction2) = GetConstructors(parentInfo) For Each constructor As CodeFunction2 In parentConstructors If Not MatchingSignatureExists(constructor, childConstructors) Then ' we only want to create ctor stubs for ctors that are missing ' note: a dictionary could be more efficient, but I doubt most classes will have more than 4 or 5 ctors... StubConstructor(classInfo, constructor) End If Next Finally If closeUndoContext Then DTE.UndoContext.Close() End If End Try End Sub Private Function GetConstructors(ByVal classInfo As CodeClass2) As System.Collections.Generic.List(Of CodeFunction2) ' return a list of all of the constructors in the specified class Dim result As System.Collections.Generic.List(Of CodeFunction2) = New System.Collections.Generic.List(Of CodeFunction2) Dim func As CodeFunction2 For Each member As CodeElement2 In classInfo.Members ' members collection has all class members. filter out just the function members, and then of the functions, grab just the ctors func = TryCast(member, CodeFunction2) If func Is Nothing Then Continue For If func.FunctionKind = vsCMFunction.vsCMFunctionConstructor Then result.Add(func) End If Next Return result End Function Private Function MatchingSignatureExists(ByVal searchFunction As CodeFunction2, ByVal functions As System.Collections.Generic.List(Of CodeFunction2)) As Boolean ' given a function (searchFunction), searches a list of functions where the function signatures (not necessarily the names) match ' return null if no match is found, otherwise returns first match For Each func As CodeFunction In functions If func.Parameters.Count <> searchFunction.Parameters.Count Then Continue For Dim searchParam As CodeParameter2 Dim funcParam As CodeParameter2 Dim match As Boolean = True For count As Integer = 1 To searchFunction.Parameters.Count searchParam = searchFunction.Parameters.Item(count) funcParam = func.Parameters.Item(count) If searchParam.Type.AsFullName <> funcParam.Type.AsFullName Then match = False Exit For End If Next If match Then Return True End If Next ' no match found Return False End Function Private Sub StubConstructor(ByVal classInfo As CodeClass2, ByVal parentConstructor As CodeFunction2) ' adds a constructor to the current class, based upon the parentConstructor that is passed in ' highly inefficient hack to position the ctor where I want it (after the last ctor in the class, if there is another ctor ' note that passing zero as the position (put the ctor first) caused some problems when we were adding ctors to classes that already had ctors Dim position As Object Dim ctors As System.Collections.Generic.List(Of CodeFunction2) = GetConstructors(classInfo) If ctors.Count = 0 Then position = 0 Else position = ctors.Item(ctors.Count - 1) End If ' if there are no other ctors, put this one at the top Dim ctor As CodeFunction2 = classInfo.AddFunction(classInfo.Name, vsCMFunction.vsCMFunctionConstructor, vsCMTypeRef.vsCMTypeRefVoid, position, parentConstructor.Access) Dim baseCall As Ssortingng = ":base(" Dim separator As Ssortingng = "" For Each parameter As CodeParameter2 In parentConstructor.Parameters ctor.AddParameter(parameter.Name, parameter.Type, -1) baseCall += separator + parameter.Name separator = ", " Next baseCall += ")" ' and 1 sad hack -- appears to be no way to programmatically add the :base() calls without using direct ssortingng manipulation Dim startPoint As TextPoint = ctor.GetStartPoint() Dim endOfSignature As EditPoint = startPoint.CreateEditPoint() endOfSignature.EndOfLine() endOfSignature.Insert(baseCall) startPoint.CreateEditPoint().SmartFormat(endOfSignature) End Sub Private Function GetClassElement() As CodeClass2 'returns a CodeClass2 element representing the class that the cursor is within, or null if there is no class Try Dim selection As TextSelection = DTE.ActiveDocument.Selection Dim fileCodeModel As FileCodeModel2 = DTE.ActiveDocument.ProjectItem.FileCodeModel Dim element As CodeElement2 = fileCodeModel.CodeElementFromPoint(selection.TopPoint, vsCMElement.vsCMElementClass) Return element Catch Return Nothing End Try End Function End Module 

Voici une macro que j’utilise à cette fin. Il va générer un constructeur à partir de champs et de propriétés qui ont un setter privé.

 Imports System Imports EnvDTE Imports EnvDTE80 Imports EnvDTE90 Imports EnvDTE90a Imports EnvDTE100 Imports System.Diagnostics Imports System.Collections.Generic Public Module Temp Sub AddConstructorFromFields() DTE.UndoContext.Open("Add constructor from fields") Dim classElement As CodeClass, index As Integer GetClassAndInsertionIndex(classElement, index) Dim constructor As CodeFunction constructor = classElement.AddFunction(classElement.Name, vsCMFunction.vsCMFunctionConstructor, vsCMTypeRef.vsCMTypeRefVoid, index, vsCMAccess.vsCMAccessPublic) Dim visitedNames As New Dictionary(Of Ssortingng, Ssortingng) Dim element As CodeElement, parameterPosition As Integer, isFirst As Boolean = True For Each element In classElement.Children Dim fieldType As Ssortingng Dim fieldName As Ssortingng Dim parameterName As Ssortingng Select Case element.Kind Case vsCMElement.vsCMElementVariable Dim field As CodeVariable = CType(element, CodeVariable) fieldType = field.Type.AsSsortingng fieldName = field.Name parameterName = field.Name.TrimStart("_".ToCharArray()) Case vsCMElement.vsCMElementProperty Dim field As CodeProperty = CType(element, CodeProperty) If field.Setter.Access = vsCMAccess.vsCMAccessPrivate Then fieldType = field.Type.AsSsortingng fieldName = field.Name parameterName = field.Name.Subssortingng(0, 1).ToLower() + field.Name.Subssortingng(1) End If End Select If Not Ssortingng.IsNullOrEmpty(parameterName) And Not visitedNames.ContainsKey(parameterName) Then visitedNames.Add(parameterName, parameterName) constructor.AddParameter(parameterName, fieldType, parameterPosition) Dim endPoint As EditPoint endPoint = constructor.EndPoint.CreateEditPoint() endPoint.LineUp() endPoint.EndOfLine() If Not isFirst Then endPoint.Insert(Environment.NewLine) Else isFirst = False End If endPoint.Insert(Ssortingng.Format(MemberAssignmentFormat(constructor.Language), fieldName, parameterName)) parameterPosition = parameterPosition + 1 End If Next DTE.UndoContext.Close() Try ' This command fails sometimes ' DTE.ExecuteCommand("Edit.FormatDocument") Catch ex As Exception End Try End Sub Private Sub GetClassAndInsertionIndex(ByRef classElement As CodeClass, ByRef index As Integer, Optional ByVal useStartIndex As Boolean = False) Dim selection As TextSelection selection = CType(DTE.ActiveDocument.Selection, TextSelection) classElement = CType(selection.ActivePoint.CodeElement(vsCMElement.vsCMElementClass), CodeClass) Dim childElement As CodeElement index = 0 For Each childElement In classElement.Children Dim childOffset As Integer childOffset = childElement.GetStartPoint(vsCMPart.vsCMPartWholeWithAtsortingbutes).AbsoluteCharOffset If selection.ActivePoint.AbsoluteCharOffset < childOffset Or useStartIndex Then Exit For End If index = index + 1 Next End Sub Private ReadOnly Property MemberAssignmentFormat(ByVal language As String) As String Get Select Case language Case CodeModelLanguageConstants.vsCMLanguageCSharp Return "this.{0} = {1};" Case CodeModelLanguageConstants.vsCMLanguageVB Return "Me.{0} = {1}" Case Else Return "" End Select End Get End Property End Module 

Peut-être que vous pourriez essayer ceci: http://cometaddin.codeplex.com/

À partir de VS 2017, cela semble être une fonctionnalité intégrée. Hit Ctrl + . Lorsque votre curseur se trouve dans le corps de la classe, sélectionnez “Générer un constructeur” dans la liste déroulante Actions rapides et Refactorings.

Je me rends compte que c’est une vieille question, mais si quelqu’un le cherche encore, vous pouvez le faire facilement avec Resharper 8+. Les ctorf , ctorp et ctorfp génèrent des constructeurs qui ctorfp tous les champs, propriétés ou champs et propriétés d’une classe.

Voici la macro VS de JMarsh modifiée pour générer un constructeur basé sur les champs et les propriétés de la classe.

 Imports System Imports EnvDTE Imports EnvDTE80 Imports EnvDTE90 Imports EnvDTE100 Imports System.Diagnostics Imports System.Collections.Generic Public Module ConstructorEditor Public Sub AddConstructorFromFields() Dim classInfo As CodeClass2 = GetClassElement() If classInfo Is Nothing Then System.Windows.Forms.MessageBox.Show("No class was found surrounding the cursor. Make sure that this file comstacks and try again.", "Error") Return End If ' Setting up undo context. One Ctrl+Z undoes everything Dim closeUndoContext As Boolean = False If DTE.UndoContext.IsOpen = False Then closeUndoContext = True DTE.UndoContext.Open("AddConstructorFromFields", False) End If Try Dim dataMembers As List(Of DataMember) = GetDataMembers(classInfo) AddConstructor(classInfo, dataMembers) Finally If closeUndoContext Then DTE.UndoContext.Close() End If End Try End Sub Private Function GetClassElement() As CodeClass2 'returns a CodeClass2 element representing the class that the cursor is within, or null if there is no class Try Dim selection As TextSelection = DTE.ActiveDocument.Selection Dim fileCodeModel As FileCodeModel2 = DTE.ActiveDocument.ProjectItem.FileCodeModel Dim element As CodeElement2 = fileCodeModel.CodeElementFromPoint(selection.TopPoint, vsCMElement.vsCMElementClass) Return element Catch Return Nothing End Try End Function Private Function GetDataMembers(ByVal classInfo As CodeClass2) As System.Collections.Generic.List(Of DataMember) Dim dataMembers As List(Of DataMember) = New List(Of DataMember) Dim prop As CodeProperty2 Dim v As CodeVariable2 For Each member As CodeElement2 In classInfo.Members prop = TryCast(member, CodeProperty2) If Not prop Is Nothing Then dataMembers.Add(DataMember.FromProperty(prop.Name, prop.Type)) End If v = TryCast(member, CodeVariable2) If Not v Is Nothing Then If v.Name.StartsWith("_") And Not v.IsConstant Then dataMembers.Add(DataMember.FromPrivateVariable(v.Name, v.Type)) End If End If Next Return dataMembers End Function Private Sub AddConstructor(ByVal classInfo As CodeClass2, ByVal dataMembers As List(Of DataMember)) ' Put constructor after the data members Dim position As Object = dataMembers.Count ' Add new constructor Dim ctor As CodeFunction2 = classInfo.AddFunction(classInfo.Name, vsCMFunction.vsCMFunctionConstructor, vsCMTypeRef.vsCMTypeRefVoid, position, vsCMAccess.vsCMAccessPublic) For Each dataMember As DataMember In dataMembers ctor.AddParameter(dataMember.NameLocal, dataMember.Type, -1) Next ' Assignments Dim startPoint As TextPoint = ctor.GetStartPoint(vsCMPart.vsCMPartBody) Dim point As EditPoint = startPoint.CreateEditPoint() For Each dataMember As DataMember In dataMembers point.Insert(" " + dataMember.Name + " = " + dataMember.NameLocal + ";" + Environment.NewLine) Next End Sub Class DataMember Public Name As Ssortingng Public NameLocal As Ssortingng Public Type As Object Private Sub New(ByVal name As Ssortingng, ByVal nameLocal As Ssortingng, ByVal type As Object) Me.Name = name Me.NameLocal = nameLocal Me.Type = type End Sub Shared Function FromProperty(ByVal name As Ssortingng, ByVal type As Object) Dim nameLocal As Ssortingng If Len(name) > 1 Then nameLocal = name.Subssortingng(0, 1).ToLower + name.Subssortingng(1) Else nameLocal = name.ToLower() End If Return New DataMember(name, nameLocal, type) End Function Shared Function FromPrivateVariable(ByVal name As Ssortingng, ByVal type As Object) If Not name.StartsWith("_") Then Throw New ArgumentException("Expected private variable name to start with underscore.") End If Dim nameLocal As Ssortingng = name.Subssortingng(1) Return New DataMember(name, nameLocal, type) End Function End Class End Module 

Pour VS 2015, j’ai trouvé une extension qui fait exactement cela. Il semble bien fonctionner et possède une quantité de téléchargements assez élevée. Donc, si vous ne pouvez pas ou ne voulez pas utiliser Resharper, vous pouvez installer celui-ci à la place.

METTRE À JOUR

Vous pouvez également l’acquérir via nuget

J’utilise l’astuce suivante:

Je sélectionne la déclaration de la classe avec les membres des données et appuie sur:

Ctrl + C , Maj + Ctrl + C , Ctrl + V.

  • La première commande copie la déclaration dans le presse-papier,
  • La deuxième commande est un raccourci qui appelle le programme
  • La dernière commande remplace la sélection par le texte du presse-papiers.

Le PROGRAMME obtient la déclaration du presse-papiers, trouve le nom de la classe, trouve tous les membres et leurs types, génère le constructeur et le copie dans le presse-papiers.

Nous le faisons avec des étudiants de première année dans mon cabinet “Programming-I” (Université Charles, Prague) et la plupart des étudiants le font jusqu’à la fin de l’heure.

Si vous voulez voir le code source, faites le moi savoir.