How do I generate a constructor from class fields using Visual Studio (and/or ReSharper)?

前端 未结 12 1773
有刺的猬
有刺的猬 2020-11-27 12:54

I\'ve gotten accustomed to many of the Java IDEs (Eclipse, NetBeans, and IntelliJ IDEA) providing you with a command to generate a default constructor for a class based on t

相关标签:
12条回答
  • 2020-11-27 13:51

    In Visual Studio 2015 Update3 I have this feature.

    Just by highlighting properties and then press Ctrl + . and then press Generate Constructor.

    For example, if you've highlighted two properties it will suggest you to create a constructor with two parameters and if you've selected three it will suggest one with three parameters and so on.

    It also works with Visual Studio 2017.

    0 讨论(0)
  • 2020-11-27 13:51

    Here's JMarsh's Visual Studio macro modified to generate a constructor based on the fields and properties in the class.

    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
    
    0 讨论(0)
  • 2020-11-27 13:53

    You can do this easily with ReSharper 8 or later. The ctorf, ctorp, and ctorfp snippets generate constructors that populate all the fields, properties, or fields and properties of a class.

    0 讨论(0)
  • 2020-11-27 13:56

    Here is a macro that I use for that purpose. It will generate a constructor from fields and properties that have a private 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 讨论(0)
  • 2020-11-27 13:58

    Maybe you could try out this: http://cometaddin.codeplex.com/

    0 讨论(0)
  • 2020-11-27 13:58

    For Visual Studio 2015 I found an extension that does just this. It seems to work well and has a reasonably high amount of downloads. So if you can't or don't want to use ReSharper you can install this one instead.

    You can also acquire it via NuGet.

    0 讨论(0)
提交回复
热议问题