How do I generate a constructor from class fields using Visual Studio (and/or ReSharper)?

C#Visual StudioVisual Studio-2010ResharperCode Generation

C# Problem Overview


I've gotten accustomed to many of the Java IDEs (Eclipse, NetBeans, and IntelliJ IDEA) providing you with a command to generate a default constructor for a class based on the fields in the class.

For example:

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

Having a constructor populate all of the fields of an object is such a common task in most OOP languages, I'm assuming that there is a some way for me to save time writing this boilerplate code in C#. I'm new to the C# world, so I'm wondering if I'm missing something fundamental about the language? Is there some option in Visual Studio that is obvious?

C# Solutions


Solution 1 - C#

In Visual Studio 2015 Update3 I have this feature.

Just by highlighting properties and then press Ctrl + . and then press Generate Constructor.

For example, if you've highlighted two properties it will suggest you to create a constructor with two parameters and if you've selected three it will suggest one with three parameters and so on.

It also works with Visual Studio 2017 and 2019.

Auto generate shortcut visualisation

Solution 2 - C#

ReSharper offers a Generate Constructor tool where you can select any field/properties that you want initialized. I use the Alt + Ins hot-key to access this.

Solution 3 - C#

C# added a new feature in Visual Studio 2010 called generate from usage. The intent is to generate the standard code from a usage pattern. One of the features is generating a constructor based off an initialization pattern.

The feature is accessible via the smart tag that will appear when the pattern is detected.

For example, let’s say I have the following class

class MyType { 

}

And I write the following in my application

var v1 = new MyType(42);

A constructor taking an int does not exist so a smart tag will show up and one of the options will be "Generate constructor stub". Selecting that will modify the code for MyType to be the following.

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

Solution 4 - C#

As of Visual Studio 2017, this looks to be a built-in feature. Hit Ctrl + . while your cursor is in the class body, and select "Generate Constructor" from the Quick Actions and Refactorings dropdown.

Solution 5 - C#

You could write a macro to do this -- you would use Visual Studio's parser to retrieve information about the class's members.

I wrote a similar macro. (I'll share the code below). The macro I wrote is for copying forward all of the constructors in a base class when you inherit from it (useful for classes like Exception that have lots of overloads on the ctor).

Here's my macro (again, it doesn't solve your problem, but you can probably modify to do what you want)


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

End Module

Solution 6 - C#

Here is a macro that I use for that purpose. It will generate a constructor from fields and properties that have a private 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

Solution 7 - C#

Maybe you could try out this: http://cometaddin.codeplex.com/

Solution 8 - C#

You can do this easily with ReSharper 8 or later. The ctorf, ctorp, and ctorfp snippets generate constructors that populate all the fields, properties, or fields and properties of a class.

Solution 9 - C#

Here's JMarsh's Visual Studio macro modified to generate a constructor based on the fields and properties in the class.

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

Solution 10 - C#

For Visual Studio 2015 I found an extension that does just this. It seems to work well and has a reasonably high amount of downloads. So if you can't or don't want to use ReSharper you can install this one instead.

You can also acquire it via NuGet.

Solution 11 - C#

In Visual Studio click on one of the fields -> click the light bulb -> Generate Constructors -> Select the fields

Solution 12 - C#

I'm using the following trick:

I select the declaration of the class with the data-members and press:

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

  • The first command copies the declaration to the clipboard,
  • The second command is a shortcut that invokes the PROGRAM
  • The last command overwrites the selection by text from the clipboard.

The PROGRAM gets the declaration from the clipboard, finds the name of the class, finds all members and their types, generates constructor and copies it all back into the clipboard.

We are doing it with freshmen on my "Programming-I" practice (Charles University, Prague) and most of students gets it done till the end of the hour.

If you want to see the source code, let me know.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionElijahView Question on Stackoverflow
Solution 1 - C#Pouya SamieView Answer on Stackoverflow
Solution 2 - C#James KolpackView Answer on Stackoverflow
Solution 3 - C#JaredParView Answer on Stackoverflow
Solution 4 - C#lt1View Answer on Stackoverflow
Solution 5 - C#JMarschView Answer on Stackoverflow
Solution 6 - C#Antoine AubryView Answer on Stackoverflow
Solution 7 - C#SimonView Answer on Stackoverflow
Solution 8 - C#Mike BView Answer on Stackoverflow
Solution 9 - C#rybo103View Answer on Stackoverflow
Solution 10 - C#TKharaishviliView Answer on Stackoverflow
Solution 11 - C#DoroView Answer on Stackoverflow
Solution 12 - C#Tom HolanView Answer on Stackoverflow