如何使用Visual Studio(和/或ReSharper)从类字段生成构造函数?

我已经习惯了许多Java IDE(Eclipse,Netbeans,IntelliJ),它们为您提供了一个命令,用于根据类中的字段为类生成默认的构造函数。

例如:

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中有一些选项是显而易见的吗?

Resharper提供了一个“ 生成构造器”工具,您可以在其中select要初始化的任何字段/属性。 我使用Alt + Ins热键来访问它。

在Visual Studio 2015 Update3中,我有这个function。

只需突出显示属性,然后按Ctrl + 然后按生成构造函数。

例如,如果您突出显示了2个属性,则会build议您创build一个带有2个参数的构造函数,如果您select了3个参数,则会build议一个带有3个参数的构造函数等等。

也适用于VS2017。

自动生成快捷可视化

C#在Visual Studio 2010中添加了一个名为generate的新特性。 目的是从使用模式中生成标准代码。 其中一个特性是基于初始化模式生成构造函数。

该function可通过智能标签进行访问,智能标签将在检测到花样时显示。

例如。 可以说我有以下class级

 class MyType { } 

我在我的应用程序中写下以下内容

 var v1 = new MyType(42); 

接受一个int的构造函数不存在,所以智能标签将显示出来,其中一个选项将是“生成构造函数存根”。 select它将修改MyType的代码如下。

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

你可以编写一个macros来做到这一点 – 你将使用Visual Studio的parsing器来检索有关类的成员的信息。

我写了一个类似的macros。 (我将分享下面的代码)。 我写的macros是为了在从类inheritance所有构造函数的时候拷贝所有的构造函数(对类似Exception的类有用,它对ctor有很多重载)。

这是我的macros(再次,它不能解决你的问题,但你可以修改,以做你想做的事情)

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

这是我用于这个目的的一个macros。 它将从具有私有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 

也许你可以试试这个: http : //cometaddin.codeplex.com/

这里的JMarsh的VSmacros被修改,以生成基于类中的字段和属性的构造函数。

 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 

我意识到这是一个古老的问题,但如果有人仍然在寻找它,你可以用Resharper 8+轻松地做到这一点。 ctorfctorpctorfp片段会生成构造函数,用于填充类的所有字段,属性或字段和属性。

对于VS 2015,我发现了一个扩展 ,做到这一点。 它似乎运作良好,并具有相当高的下载量。 所以,如果你不能或不想使用Resharper,你可以安装这个。

UPDATE

你也可以通过nuget获取它

我正在使用以下技巧:

我用数据成员select类的声明并按下:

Ctrl + CShift + Ctrl + CCtrl + V。

  • 第一个命令将声明复制到剪贴板,
  • 第二个命令是调用PROGRAM的快捷方式
  • 最后一个命令将通过剪贴板中的文本覆盖select。

PROGRAM从剪贴板获取声明,find类的名称,查找所有成员及其types,生成构造函数并将其全部复制到剪贴板中。

我们正在和我的“编程 – 我”(布拉格查尔斯大学)大学一年级的新生一起练习,大多math生都是在这个小时结束之前完成的。

如果你想看到源代码,请告诉我。