.NET C # auto generate ctor [duplicate]

Другим случаем, когда NullReferenceExceptions может случиться, является (неправильное) использование оператора as :

class Book {
    public string Name { get; set; }
}
class Car { }

Car mycar = new Car();
Book mybook = mycar as Book;   // Incompatible conversion --> mybook = null

Console.WriteLine(mybook.Name);   // NullReferenceException

Здесь Book и Car являются несовместимыми типами; a Car не может быть преобразован / передан в Book. Когда этот сбой завершается неудачно, as возвращает null. Используя mybook после этого, вы вызываете NullReferenceException.

В общем случае вы должны использовать cast или as, как показано ниже:

Если вы ожидаете преобразования типа в всегда преуспевает (т. е. вы знаете, какой объект должен быть впереди времени), тогда вы должны использовать cast:

ComicBook cb = (ComicBook)specificBook;

Если вы не уверены в типе, но хотите попробовать , чтобы использовать его как определенный тип, затем используйте as:

ComicBook cb = specificBook as ComicBook;
if (cb != null) {
   // ...
}

137
задан Elijah 4 June 2010 в 18:23
поделиться

11 ответов

Resharper предлагает инструмент Generate Constructor , где вы можете выбрать любое поле / свойства, которые вы хотите инициализировать. Я использую горячую клавишу Alt + Ins для доступа к ней.

114
ответ дан T D Nguyen 29 August 2018 в 01:24
поделиться
  • 1
    Это отвечает на вопрос для меня с точки зрения «получения». Однако в VS2010 нет никакой поддержки, верно? – Elijah 4 June 2010 в 18:31
  • 2
    Как упоминает Jared ниже, VS2010 добавил «Generate from usage» но, насколько я могу судить, нет возможности генерировать конструктор на основе полей, которые уже находятся в классе. Если вы попытаетесь создать экземпляр класса с сигнатурой, которая не соответствует существующим, она предложит создать для вас этот конструктор. – James Kolpack 4 June 2010 в 18:45
  • 3
    Yay, ReSharper! – MrBoJangles 4 June 2010 в 18:50
  • 4
    О, вау, я знаю, что это довольно старый вопрос, но я только что открыл это! – Brett 25 July 2013 в 16:17
  • 5
    Вероятно, вы должны упомянуть, что ReSharper не является бесплатным . – b1nary.atr0phy 26 March 2016 в 08:17

Вот макрос, который я использую для этой цели. Он будет генерировать конструктор из полей и свойств, имеющих частный сеттер.

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
11
ответ дан Antoine Aubry 29 August 2018 в 01:24
поделиться
  • 1
    Мне пришлось разделить строку: «If Not String.IsNullOrEmpty (имя_файла) и« Не посещеноNames.ContainsKey (parameterName) »Затем« quot; в две строки, чтобы исключить исключение с нулевой ссылкой: – cedd 7 November 2014 в 17:10

Здесь изменен макрос VS JMarsh для генерации конструктора на основе полей и свойств в классе.

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
4
ответ дан Community 29 August 2018 в 01:24
поделиться

Вы можете написать макрос для этого - вы бы использовали анализатор 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

15
ответ дан JMarsch 29 August 2018 в 01:24
поделиться
  • 1
    Отсутствует оператор: & quot; Если searchParam.Type.AsFullName funcParam.Type.AsFullName Then & quot; должен быть " Если searchParam.Type.AsFullName = funcParam.Type.AsFullName Then & quot; – LTR 15 July 2012 в 10:58
  • 2
    @LTR Отличный catch - за исключением того, что он должен быть «Если searchParam.Type.AsFullName & lt; gt; funcParam.Type.AsFullName & quot ;. Я пропустил побег на угловых скобках - они появились в редакторе, но не в представлении. Благодаря! – JMarsch 16 July 2012 в 02:43

C # добавлена ​​новая функция в Visual Studio 2010, называемая generate из использования. Цель состоит в том, чтобы сгенерировать стандартный код из шаблона использования. Одной из особенностей является создание конструктора, основанного на шаблоне инициализации.

Функция доступна через смарт-тег, который будет отображаться при обнаружении шаблона.

Например. Допустим, у меня есть следующий класс

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;
    }
}
28
ответ дан John Cummings 29 August 2018 в 01:24
поделиться
  • 1
    Это путь. Приветствия. – Victor Ionescu 23 July 2014 в 07:40
  • 2
    Я думаю, что есть опечатка. Вы имеете в виду MyType вместо MyClass? – Marc 13 April 2016 в 17:58

Я использую следующий трюк:

Я выбираю объявление класса с элементами данных и нажимаю:

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

  • Первая команда копирует объявление в буфер обмена,
  • Вторая команда - это ярлык, который вызывает PROGRAM
  • Последний команда перезаписывает выделение по тексту из буфера обмена.

PROGRAM получает объявление из буфера обмена, находит имя класса, находит всех членов и их типы, генерирует конструктор и копирует его все вернуться в буфер обмена.

Мы делаем это с первокурсниками в моей практике «Программирование-I» (Карл-Университет, Прага), и большинство студентов добираются до конца часа.

Если вы хотите увидеть исходный код, дайте мне знать.

-3
ответ дан Littm 29 August 2018 в 01:24
поделиться
  • 1
    Вторая команда - это ярлык для представления класса, не так ли? Или этот отзыв не о Visual Studio 2010? – Nenotlep 25 April 2014 в 13:24

Как и VS 2017, это выглядит как встроенная функция. Нажмите Ctrl +. в то время как ваш курсор находится в классе, и выберите «Generate Constructor» из раскрывающегося списка «Быстрые действия и рефакторинг».

136
ответ дан Lonely Neuron 29 August 2018 в 01:24
поделиться
  • 1
    Эй, это сработало для меня в сообществе Visual Studio 2015. Не уверен, что это не так широко известно, но это хорошо. Благодарю. :) – The 0bserver 23 March 2017 в 15:50
  • 2
    Это идеально. Работа, которую это могло бы спасти, если бы я прочитал ее в тот день, когда вы разместили ее ... xD – Timo 18 April 2017 в 11:03
  • 3
    Для чего это стоит, функция не появляется, если вы используете свойства C # 6 для чтения. (например, public int Age { get; }). Они должны иметь в настройках, указанных, даже если временно, для того, чтобы опция была доступна. Протестировано в VS2015 Сообщество; не уверен, что это было исправлено в VS2017. – Chris Sinclair 17 May 2017 в 15:47
  • 4
    @PouyaSamie: В C # 6.0 в конструкторе могут быть назначены автоматические свойства readonly. См. Это для примера: github.com/dotnet/roslyn/wiki/… – Chris Sinclair 23 May 2017 в 14:13
  • 5
    Это идеальное решение! Я бы отметил это реальное решение! – Václav Holuša 4 September 2017 в 07:18

Я понимаю, что это старый вопрос, но если кто-то все еще ищет его, вы можете сделать это легко с помощью Resharper 8+. Фрагменты ctorf, ctorp и ctorfp генерируют конструкторы, которые заполняют все поля, свойства или поля и свойства класса.

5
ответ дан Mike B 29 August 2018 в 01:24
поделиться

Возможно, вы могли бы попробовать это: http://cometaddin.codeplex.com/

9
ответ дан Simon 29 August 2018 в 01:24
поделиться

Для VS 2015 я нашел расширение , которое делает именно это. Кажется, что он работает хорошо и имеет достаточно большой объем загрузок. Поэтому, если вы не можете или не хотите использовать Resharper, вы можете установить этот вариант.

UPDATE

Вы также можете приобрести его через nuget

2
ответ дан TKharaishvili 29 August 2018 в 01:24
поделиться

В visual studio 2015 Update3 У меня есть эта функция.

, просто выделив свойства, а затем нажмите ctrl +. а затем нажмите «Создать конструктор».

Например, если вы выделили 2 свойства, это предложит вам создать конструктор с 2 параметрами, и если вы выбрали 3, он предложит один с тремя параметрами и т. д.

также работает с VS2017.

136
ответ дан Lonely Neuron 31 August 2018 в 07:53
поделиться
  • 1
    Эй, это сработало для меня в сообществе Visual Studio 2015. Не уверен, что это не так широко известно, но это хорошо. Благодарю. :) – The 0bserver 23 March 2017 в 15:50
  • 2
    Это идеально. Работа, которую это могло бы спасти, если бы я прочитал ее в тот день, когда вы разместили ее ... xD – Timo 18 April 2017 в 11:03
  • 3
    Для чего это стоит, функция не появляется, если вы используете свойства C # 6 для чтения. (например, public int Age { get; }). Они должны иметь в настройках, указанных, даже если временно, для того, чтобы опция была доступна. Протестировано в VS2015 Сообщество; не уверен, что это было исправлено в VS2017. – Chris Sinclair 17 May 2017 в 15:47
  • 4
    @PouyaSamie: В C # 6.0 в конструкторе могут быть назначены автоматические свойства readonly. См. Это для примера: github.com/dotnet/roslyn/wiki/… – Chris Sinclair 23 May 2017 в 14:13
  • 5
    Это идеальное решение! Я бы отметил это реальное решение! – Václav Holuša 4 September 2017 в 07:18