클래스의 필드를 기반으로 클래스의 기본 생성자를 생성하는 명령을 제공하는 많은 Java IDE ( Eclipse , NetBeans 및 IntelliJ 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에 명백한 옵션이 있습니까?
답변
답변
Visual Studio 2015 Update3에는이 기능이 있습니다.
속성을 강조 표시 한 다음 Ctrl+ .를 누른 다음 생성자 생성 을 누릅니다 .
예를 들어, 두 개의 속성을 강조 표시 한 경우 두 개의 매개 변수를 사용하여 생성자를 생성하고 세 개의 매개 변수를 선택하면 세 개의 매개 변수가있는 것 등이 제안됩니다.
Visual Studio 2017에서도 작동합니다.
답변
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;
}
}
답변
이를 위해 매크로를 작성할 수 있습니다. 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
답변
Visual Studio 2017부터는 기본 제공 기능인 것 같습니다. 히트 Ctrl+ .커서가 클래스 본문에, 선택하면서 “생성자를 생성” 으로부터 빠른 작업 및 리팩토링의 드롭 다운.
답변
그 목적으로 사용하는 매크로는 다음과 같습니다. 개인 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