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

问题描述:

我已经习惯了许多Java IDE(Eclipse,Netbeans,IntelliJ),它们为您提供了一个基于类中字段为类生成默认构造函数的命令。如何使用Visual Studio(和/或ReSharper)从类字段生成构造函数?

例如:

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提供了一个Generate Constructor工具,您可以在其中选择要初始化的任何字段/属性。我使用Alt + Ins热键来访问此。

+0

这就为我解决了“完成它”的问题。但是,直接在VS2010中不支持它,对吧? – Elijah 2010-06-04 17:31:28

+1

与Jared在下面提到的一样,VS2010添加了“从使用中生成”工具,但据我所知,无法根据班级中已有的字段生成构造函数。如果您尝试使用与任何现有的签名不匹配的类实例化该类,它将提供为您生成该构造函数。 – 2010-06-04 17:45:18

+5

耶,ReSharper! – MrBoJangles 2010-06-04 17:50:37

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

该功能可通过智能标签进行访问,智能标签将在检测到图案时显示。

例如。比方说,我有下面的类

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

这是要走的路。干杯。 – 2014-07-23 06:40:15

+2

我认为存在拼写错误。你的意思是'MyType'而不是'MyClass'? – Marc 2016-04-13 17:58:26

您可以编写一个宏来做到这一点 - 您将使用Visual Studio的解析器来检索有关该类成员的信息。

我写了一个类似的宏。 (我将分享下面的代码)。我写的宏用于在继承所有构造函数时从基类继承所有构造函数(对类似Exception的类有用,它们在ctor上有很多重载)。

这里是我的宏(同样,它不能解决你的问题,但你可能可以修改你想要的)

 

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 2012-07-15 09:58:58

+1

@LTR大有赶超 - 除了它应该是“如果searchParam.Type.AsFullName funcParam.Type.AsFullName”。我错过了尖括号中的逃生 - 他们出现在编辑器中,但没有出现在视图中。谢谢! – JMarsch 2012-07-16 01:43:19

这里是我使用这一目的的宏。它将从具有私有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

我不得不将分隔线:“如果不是String.IsNullOrEmpty(parameterName)和不visitedNameNames.ContainsKey(parameterName)然后”分成两行以避免空引用异常: – cedd 2014-11-07 16:10:36

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

+0

工作得很好,谢谢 – vtortola 2011-03-07 15:15:30

我用以下技巧:

我选择与数据成员和按类的声明:

Ctrl + CShift + Ctrl + CCtrl + V

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

的程序得到声明从剪贴板, 找到类的名称,发现所有成员及其类型, 生成构造函数和副本都回来到剪贴板。

我们正在通过我的“编程-I”练习(查尔斯大学,布拉格) 与新生一起练习,大部分学生都将它完成到小时结束。

如果您想查看源代码,请告诉我。

+1

第二个命令是一个快捷方式到班级来看,不是吗?或者,这个提示不是关于Visual Studio 2010的? – Nenotlep 2014-04-25 12:24:58

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

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+轻松完成此操作。 ctorf,ctorpctorfp片段会生成构造函数,该构造函数可以填充类的所有字段,属性或字段和属性。

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

UPDATE

您也可以获取它via nuget

在Visual Studio 2015年UPDATE3我有这个功能。

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

例如,如果你已经突出显示了2个属性,它会建议你创建一个带有2个参数的构造函数,如果你选择了3,它会建议带有3个参数的构造函数等等。

也适用于VS2017。

auto generate shortcut visualisation

+2

嘿,这在Visual Studio 2015社区中适用于我。不知道这是不是很公开的知道,但这很好。谢谢。 :) – 2017-03-23 14:50:39

+2

这是完美的。如果在您发布它的那一天阅读它,可能会节省这些工作... xD – Timo 2017-04-18 11:03:58

+2

如果您使用C#6只读属性,则该功能不会弹出。 (例如'public int Age {get;}')它们需要在setters中指定,即使是暂时的,以使该选项可用。在VS2015社区进行测试;不确定这是否已在VS2017中修复。 – 2017-05-17 15:47:17

作为VS 2017的,这看起来是一个在内置功能。点击Ctrl + .而您的光标在类体中,并从Quick Actions和Refactorings下拉列表中选择“Generate Constructor”。