2010-06-04 3 views
128

Ich habe mich an viele Java-IDEs gewöhnt (Eclipse, Netbeans, IntelliJ), die einen Befehl zum Generieren eines Standardkonstruktors für eine Klasse basierend auf den Feldern in der Klasse bereitstellen.Wie erzeuge ich einen Konstruktor aus Klassenfeldern mit Visual Studio (und/oder ReSharper)?

Zum Beispiel:

public class Example 
{ 
    public decimal MyNumber { get; set; } 
    public string Description { get; set; } 
    public int SomeInteger { get; set; } 

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

einen Konstruktor Mit bevölkern die alle Felder eines Objekts ist eine solche gemeinsame Aufgabe in den meisten OOP langauges, ich gehe davon aus, dass es eine für mich einiges zu retten Zeit Schreiben dieses Boilerplate-Codes in C#. Ich bin neu in der C# -Welt, also frage ich mich, ob ich etwas Grundlegendes über die Sprache verpasse? Gibt es eine Option in Visual Studio, die offensichtlich ist?

Antwort

107

Resharper bietet ein Werkzeug Generate Constructor, in dem Sie alle Felder/Eigenschaften auswählen können, die Sie initialisieren möchten. Ich verwende den Alt + Ins Hot-Key, um darauf zuzugreifen.

+0

Das beantwortet die Frage für mich in Bezug auf "es fertig zu machen." Es gibt jedoch keine Unterstützung in VS2010 direkt, oder? – Elijah

+1

Wie Jared unten erwähnt, hat VS2010 ein Tool "Generate from use" hinzugefügt, aber soweit ich das beurteilen kann, gibt es keine Möglichkeit, einen Konstruktor basierend auf Feldern zu generieren, die bereits in der Klasse sind. Wenn Sie versuchen, die Klasse mit einer Signatur zu instanziieren, die mit keiner vorhandenen übereinstimmt, bietet sie an, diesen Konstruktor für Sie zu generieren. –

+5

Yay, ReSharper! – MrBoJangles

27

C# hat in Visual Studio 2010 eine neue Funktion namens generate from usage hinzugefügt. Die Absicht ist, den Standardcode aus einem Verwendungsmuster zu erzeugen. Eines der Merkmale ist das Erzeugen eines Konstruktors basierend auf einem Initialisierungsmuster.

Die Funktion ist über das Smart Tag verfügbar, das angezeigt wird, wenn das Muster erkannt wird.

Zum Beispiel. Lets sagen, dass ich die folgende Klasse

class MyType { 

} 

Und ich schreibe folgendes in meiner Anwendung

var v1 = new MyType(42); 

Ein Konstruktor nimmt ein int nicht existiert so ein Smart-Tag wird angezeigt und eine der Optionen haben sei "Erzeuge Konstruktor Stub". Durch Auswahl von wird der Code für MyType wie folgt geändert.

+0

Dies ist der Weg zu gehen. Prost. –

+2

Ich denke, es gibt einen Tippfehler. Meinst du "MyType" anstelle von "MyClass"? – Marc

15

Sie könnten dafür ein Makro schreiben - Sie würden den Visual Studio-Parser verwenden, um Informationen über die Mitglieder der Klasse abzurufen.

Ich schrieb ein ähnliches Makro. (Ich werde den Code unten teilen). Das Makro, das ich geschrieben habe, dient zum Kopieren aller Konstruktoren in einer Basisklasse, wenn man von ihr erbt (nützlich für Klassen wie Exception, die viele Überladungen auf dem ctor haben).

Hier ist mein Makro (auch hier ist es nicht Ihr Problem lösen, aber Sie können sich wahrscheinlich ändern zu tun, was Sie wollen)

 

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 compiles 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 compiles 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 String = ":base(" 
     Dim separator As String = "" 
     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 string 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 
 
+1

Es ist ein Operator fehlt: "Wenn searchParam.Type.AsFullName funcParam.Type.AsFullName Then" sollte "Dann Wenn searchParam.Type.AsFullName = funcParam.Type.AsFullName" – LTR

+1

@LTR großer Fang - außer es soll "Wenn searchParam.Type.AsFullName <> funcParam.Type.AsFullName" sein. Ich verpasste die Flucht in den spitzen Klammern - sie erschienen im Editor, aber nicht in der Ansicht. Vielen Dank! – JMarsch

11

Hier ist ein Makro, das ich für diesen Zweck verwendet werden. Es generiert einen Konstruktor aus Feldern und Eigenschaften mit einem privaten Setter.

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 String, String) 
     Dim element As CodeElement, parameterPosition As Integer, isFirst As Boolean = True 
     For Each element In classElement.Children 
      Dim fieldType As String 
      Dim fieldName As String 
      Dim parameterName As String 

      Select Case element.Kind 
       Case vsCMElement.vsCMElementVariable 
        Dim field As CodeVariable = CType(element, CodeVariable) 
        fieldType = field.Type.AsString 
        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.AsString 
         fieldName = field.Name 
         parameterName = field.Name.Substring(0, 1).ToLower() + field.Name.Substring(1) 
        End If 
      End Select 

      If Not String.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(String.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.vsCMPartWholeWithAttributes).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 
+0

Ich musste die Zeile teilen: "Wenn nicht String.IsNullOrEmpty (ParameterName) Und nicht visitedNames.ContainsKey (Parametername) Dann" in zwei Zeilen, um eine Null Referenz Ausnahme zu vermeiden: – cedd

-3

ich folgenden Trick bin mit:

ich die Deklaration der Klasse mit den Daten-Mitgliedern aus und drücken Sie:

Ctrl + C, Verschiebung + Ctrl + C, Strg + V.

  • Der erste Befehl kopiert die Erklärung in die Zwischenablage,
  • Der zweite Befehl ist eine Verknüpfung, die das Programm
  • Der letzte Befehl überschreibt die Auswahl von Text aus der Zwischenablage aufruft.

Die PROGRAM erhält die Erklärung aus der Zwischenablage, den Namen der Klasse findet, findet alle Mitglieder und deren Typen, erzeugt Konstruktor und kopiert sie alle zurück in die Zwischenablage.

Wir machen es mit Erstsemestern auf meiner "Programmierung-I" -Praxis (Karls-Universität, Prag) und die meisten Studenten bekommen es bis zum Ende der Stunde getan.

Wenn Sie den Quellcode sehen wollen, lassen Sie es mich wissen.

+1

Der zweite Befehl ist eine Verknüpfung zur Klassenansicht, oder? Oder geht es in diesem Tipp nicht um Visual Studio 2010? – Nenotlep

4

Hier ist JMarsh VS-Makro geändert, um einen Konstruktor basierend auf den Feldern und Eigenschaften in der Klasse zu generieren.

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 compiles 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 String 
     Public NameLocal As String 
     Public Type As Object 

     Private Sub New(ByVal name As String, ByVal nameLocal As String, ByVal type As Object) 
      Me.Name = name 
      Me.NameLocal = nameLocal 
      Me.Type = type 
     End Sub 

     Shared Function FromProperty(ByVal name As String, ByVal type As Object) 

      Dim nameLocal As String 
      If Len(name) > 1 Then 
       nameLocal = name.Substring(0, 1).ToLower + name.Substring(1) 
      Else 
       nameLocal = name.ToLower() 
      End If 

      Return New DataMember(name, nameLocal, type) 

     End Function 

     Shared Function FromPrivateVariable(ByVal name As String, 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 String = name.Substring(1) 

      Return New DataMember(name, nameLocal, type) 

     End Function 

    End Class 

End Module 
4

Ich weiß, das eine alte Frage, aber wenn jemand noch für sie ist auf der Suche können Sie dies einfach mit ReSharper 8+. Die Codefragmente , ctorp und ctorfp generieren Konstruktoren, die alle Felder, Eigenschaften oder Felder und Eigenschaften einer Klasse auffüllen.

2

Für VS 2015 fand ich eine extension, die genau dies tut. Es scheint gut zu funktionieren und hat eine recht hohe Anzahl an Downloads. Wenn Sie Resharper nicht verwenden können oder wollen, können Sie stattdessen diesen installieren.

UPDATE

Sie können es auch via nuget

119

In visuell diese Funktion Studio 2015 Update3 Ich habe erwerben.

nur durch Hervorheben von Eigenschaften und drücken Sie dann ctrl + . und drücken Sie Generate Constructor.

Zum Beispiel, wenn Sie 2 Eigenschaften markiert haben, wird es Ihnen vorschlagen, einen Konstruktor mit 2 Parametern zu erstellen, und wenn Sie 3 ausgewählt haben, schlägt es einen mit 3 Parametern und so weiter vor.

funktioniert auch mit VS2017.

auto generate shortcut visualisation

+2

Hey, das funktionierte für mich in Visual Studio 2015 Community. Ich bin mir nicht sicher, wie das öffentlich nicht bekannt ist, aber das ist schön. Vielen Dank. :) –

+2

Das ist perfekt. Die Arbeit, die das hätte speichern können, wenn ich es an dem Tag lesen würde, an dem Sie es gepostet haben ... xD – Timo

+2

Für was es wert ist, wird die Funktion nicht angezeigt, wenn Sie C# 6 Read-Only-Eigenschaften verwenden. (z. B. 'public int Age {get;}}) Sie müssen bei Settors angegeben haben, selbst wenn sie vorübergehend sind, damit die Option verfügbar ist. Getestet in VS2015 Community; nicht sicher, ob dies in VS2017 behoben wurde. –

3

Ab VS 2017 sieht dies ein in Funktion gebaut werden. Drücken Sie Ctrl + ., während sich der Cursor im Klassenkörper befindet, und wählen Sie "Konstruktor generieren" aus dem Dropdown-Menü "Schnelle Aktionen und Refactorings".