如何使用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中有一些显而易见的选项吗?
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;
}
}
这是要走的路。干杯。 – 2014-07-23 06:40:15
我认为存在拼写错误。你的意思是'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
有一个操作丢失: “如果searchParam.Type.AsFullName funcParam.Type.AsFullName那么” 应该是 “如果searchParam.Type.AsFullName = funcParam.Type.AsFullName然后” – LTR 2012-07-15 09:58:58
@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
我不得不将分隔线:“如果不是String.IsNullOrEmpty(parameterName)和不visitedNameNames.ContainsKey(parameterName)然后”分成两行以避免空引用异常: – cedd 2014-11-07 16:10:36
我用以下技巧:
我选择与数据成员和按类的声明:
Ctrl + C,Shift + Ctrl + C,Ctrl + V。
- 第一命令复制声明到剪贴板,
- 第二个命令是调用PROGRAM
- 最后的命令覆盖通过从剪贴板中的文本的选择的快捷方式。
的程序得到声明从剪贴板, 找到类的名称,发现所有成员及其类型, 生成构造函数和副本都回来到剪贴板。
我们正在通过我的“编程-I”练习(查尔斯大学,布拉格) 与新生一起练习,大部分学生都将它完成到小时结束。
如果您想查看源代码,请告诉我。
第二个命令是一个快捷方式到班级来看,不是吗?或者,这个提示不是关于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
,ctorp
和ctorfp
片段会生成构造函数,该构造函数可以填充类的所有字段,属性或字段和属性。
在Visual Studio 2015年UPDATE3我有这个功能。
只需突出显示属性,然后按ctrl + 。然后按生成构造函数。
例如,如果你已经突出显示了2个属性,它会建议你创建一个带有2个参数的构造函数,如果你选择了3,它会建议带有3个参数的构造函数等等。
也适用于VS2017。
嘿,这在Visual Studio 2015社区中适用于我。不知道这是不是很公开的知道,但这很好。谢谢。 :) – 2017-03-23 14:50:39
这是完美的。如果在您发布它的那一天阅读它,可能会节省这些工作... xD – Timo 2017-04-18 11:03:58
如果您使用C#6只读属性,则该功能不会弹出。 (例如'public int Age {get;}')它们需要在setters中指定,即使是暂时的,以使该选项可用。在VS2015社区进行测试;不确定这是否已在VS2017中修复。 – 2017-05-17 15:47:17
作为VS 2017的,这看起来是一个在内置功能。点击Ctrl
+ .
而您的光标在类体中,并从Quick Actions和Refactorings下拉列表中选择“Generate Constructor”。
这就为我解决了“完成它”的问题。但是,直接在VS2010中不支持它,对吧? – Elijah 2010-06-04 17:31:28
与Jared在下面提到的一样,VS2010添加了“从使用中生成”工具,但据我所知,无法根据班级中已有的字段生成构造函数。如果您尝试使用与任何现有的签名不匹配的类实例化该类,它将提供为您生成该构造函数。 – 2010-06-04 17:45:18
耶,ReSharper! – MrBoJangles 2010-06-04 17:50:37