Visual Studio (및 / 또는 ReSharper)를 사용하여 클래스 필드에서 생성자를 어떻게 생성합니까?


159

클래스의 필드를 기반으로 클래스의 기본 생성자를 생성하는 명령을 제공하는 많은 Java IDE ( Eclipse , NetBeansIntelliJ IDEA )에 익숙해졌습니다 .

예를 들면 다음과 같습니다.

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;
    }
}

생성자가 객체의 모든 필드를 채우는 것은 대부분의 OOP 언어에서 일반적인 작업이므로 C #에서 상용구 코드를 작성하는 시간을 절약 할 수있는 방법이 있다고 가정합니다. 나는 C # 세계에 익숙하지 않아서 언어에 대한 근본적인 내용이 없는지 궁금합니다. Visual Studio에 명백한 옵션이 있습니까?

답변:


124

ReSharper는 생성 하려는 필드 / 속성을 선택할 수있는 생성자 생성 도구를 제공합니다 . 내가 사용하는 Alt+ Ins이 액세스 할 수 핫 키를 누릅니다.


그것은 "완료"라는 관점에서 나에게 질문에 대한 답을 준다. 그러나 VS2010에서는 직접 지원하지 않습니다.
Elijah

1
아래의 Jared 언급과 같이 VS2010은 "사용에서 생성"도구를 추가했지만 내가 알 수있는 한 이미 클래스에있는 필드를 기반으로 생성자를 생성 할 수있는 방법이 없습니다. 기존 서명과 일치하지 않는 서명으로 클래스를 인스턴스화하려고 시도하면 해당 생성자를 생성하도록 제안합니다.
James Kolpack

와우, 나는 이것이 꽤 오래된 질문이라는 것을 알고 있지만 나는 단지 이것을 발견했습니다!
Brett

49
아마도 ReSharper는 무료아닙니다 .
b1nary.atr0phy 7

184

Visual Studio 2015 Update3에는이 기능이 있습니다.

속성을 강조 표시 한 다음 Ctrl+ .를 누른 다음 생성자 생성 을 누릅니다 .

예를 들어, 두 개의 속성을 강조 표시 한 경우 두 개의 매개 변수를 사용하여 생성자를 생성하고 세 개의 매개 변수를 선택하면 세 개의 매개 변수가있는 것 등이 제안됩니다.

Visual Studio 2017에서도 작동합니다.

바로 가기 시각화 자동 생성


3
안녕하세요, Visual Studio 2015 커뮤니티에서 저에게 효과적이었습니다. 이것이 어떻게 공개적으로 알려지지 않았는지 잘 모르겠지만 이것은 좋습니다. 감사. :)
0bserver

3
완벽 해. 내가 게시 한 날에 읽었다면이 작업이 저장되었을 수 있습니다. xD
Timo

3
C # 6 읽기 전용 속성을 사용하면 유용한 기능이 표시되지 않습니다. (예 public int Age { get; }:) 옵션을 사용하려면 일시적인 경우에도 지정된 세터를 지정해야합니다. VS2015 커뮤니티에서 테스트되었습니다. 이것이 VS2017에서 수정되었는지 확실하지 않습니다.
Chris Sinclair

1
@PouyaSamie : C # 6.0에서는 생성자에서 읽기 전용 자동 속성을 할당 할 수 있습니다. 예를 보려면 다음을 참조하십시오 : github.com/dotnet/roslyn/wiki/…
Chris Sinclair

5
이것은 완벽한 솔루션입니다! 나는 이것을 진정한 해결책으로 표시 할 것입니다!
Václav Holuša

29

C #은 Visual Studio 2010에서 사용법에서 생성이라는 새로운 기능을 추가했습니다. 의도는 사용 패턴에서 표준 코드를 생성하는 것입니다. 기능 중 하나는 초기화 패턴을 기반으로 생성자를 생성하는 것입니다.

이 기능은 패턴이 감지 될 때 표시되는 스마트 태그를 통해 액세스 할 수 있습니다.

예를 들어 다음과 같은 클래스가 있다고 가정 해 봅시다.

class MyType { 

}

그리고 나는 내 응용 프로그램에서 다음을 작성합니다

var v1 = new MyType(42);

생성자가 int존재하지 않으므로 스마트 태그가 표시되고 옵션 중 하나가 "생성자 생성자 생성"입니다. 이를 선택하면 코드 MyType가 다음과 같이 수정됩니다 .

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

15

이를 위해 매크로를 작성할 수 있습니다. Visual Studio의 파서를 사용하여 클래스 멤버에 대한 정보를 검색합니다.

나는 비슷한 매크로를 썼다. (아래 코드를 공유하겠습니다). 내가 작성한 매크로는 상속 할 때 기본 클래스의 모든 생성자를 복사하는 것입니다 (ctor에 과부하가 많은 Exception과 같은 클래스에 유용합니다).

여기 내 매크로가 있습니다 (다시 말해서 문제를 해결하지는 않지만 원하는 것을 수행하도록 수정 할 수 있습니다)


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
"searchParam.Type.AsFullName의 경우 funcParam.Type.AsFullName이면"연산자가 없어야합니다. "searchParam.Type.AsFullName = funcParam.Type.AsFullName이면"
LTR

1
@LTR 대단한 캐치- "searchParam.Type.AsFullName <> funcParam.Type.AsFullName"인 경우를 제외하고는 예외입니다. 꺾쇠 괄호에서 탈출을 놓쳤습니다. 편집기에 표시되었지만보기에는 표시되지 않았습니다. 감사!
JMarsch

13

Visual Studio 2017부터는 기본 제공 기능인 것 같습니다. 히트 Ctrl+ .커서가 클래스 본문에, 선택하면서 "생성자를 생성" 으로부터 빠른 작업 및 리팩토링의 드롭 다운.


11

그 목적으로 사용하는 매크로는 다음과 같습니다. 개인 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

null 참조 예외를 피하기 위해 "If Not String.IsNullOrEmpty (parameterName) And Not visitedNames.ContainsKey (parameterName) Then"행을 두 줄로 분할해야했습니다.
cedd

9

아마도 당신은 이것을 시도 할 수 있습니다 : http://cometaddin.codeplex.com/


CodePlex가 종료되었습니다 (하지만 링크는 현재 다운로드 가능한 아카이브로 여전히 어느 정도 유효합니다). 그러나 프로젝트를 다른 곳으로 옮긴 경우 링크를 업데이트하십시오. 현재 링크가 나중에 끊어 질 경우 재난을 방지하기위한 조치를 취하십시오.
Peter Mortensen

5

ReSharper 8 이상에서이 작업을 쉽게 수행 할 수 있습니다. ctorf, ctorp그리고 ctorfp조각은 모든 필드, 속성 또는 필드와 클래스의 속성을 채우는 생성자를 생성합니다.


4

다음 은 클래스의 필드와 속성을 기반으로 생성자를 생성하도록 수정 된 JMarsh 의 Visual Studio 매크로입니다.

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

2

Visual Studio 2015의 경우이 작업을 수행 하는 확장 프로그램 을 찾았습니다 . 잘 작동하는 것으로 보이며 상당히 많은 다운로드가 있습니다. 따라서 ReSharper를 사용할 수 없거나 사용하지 않으려면 대신 이것을 설치하십시오.

NuGet통해 얻을 수도 있습니다 .


-3

다음 트릭을 사용하고 있습니다.

데이터 멤버로 클래스 선언을 선택하고 다음을 누릅니다.

Ctrl+ C, Shift+ Ctrl+ C, Ctrl+ V.

  • 첫 번째 명령은 선언을 클립 보드에 복사합니다.
  • 두 번째 명령은 프로그램을 호출하는 바로 가기입니다
  • 마지막 명령은 클립 보드의 텍스트로 선택을 덮어 씁니다.

PROGRAM은 클립 보드에서 선언을 가져오고 클래스 이름을 찾고 모든 멤버와 해당 유형을 찾고 생성자를 생성하여 클립 보드에 모두 다시 복사합니다.

우리는 나의 "프로그래밍 -I"연습 (프라하 찰스 대학교)에서 신입생들과 함께하고 있으며 대부분의 학생들은 한 시간이 끝날 때까지 일을합니다.

소스 코드를 보려면 알려주세요.


1
두 번째 명령은 클래스 뷰에 대한 바로 가기입니다. 그렇지 않습니까? 아니면이 팁이 Visual Studio 2010에 관한 것이 아닙니까?
Joel Peltonen
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.