VB.NET Как предотвратить бесконечную рекурсию во время заполнения объекта

#vb.net #recursion

#vb.net #рекурсия

Вопрос:

Сейчас я немного застрял, пытаясь определить наилучшее решение для предотвращения бесконечного цикла рекурсии. Возможно, это не точно «рекурсия», но это набор вызовов функций, которые, я могу в значительной степени гарантировать, будут вызывать друг друга бесконечно, если я не смогу найти решение.

Пытаясь выяснить, как объяснить проблему, кажется, что лучший способ, который я могу придумать, — начать с некоторого упрощенного (и отредактированного) кода. Для этого примера я буду использовать класс и ученика.

 Public Class Classroom
    Public Property ClassroomID As Integer
    Public Property ClassroomDescription As String
    Public Property Students As List(Of Student)

    Public Sub New(ByVal ClassroomID As Integer)
        Initialize()
        GetClassroomDetail(ClassroomID)
    End Sub

    Public Sub GetClassroomDetail(ByVal ClassroomID As Integer)
        Dim Reader As SqlDataReader

        ' HERE'S WHERE I MAKE THE DATABASE CALL TO
        ' GET THE CLASSROOM RECORD DETAILS

        FillClassroomRecord(Reader)
    End Sub

    Private Sub FillClassroomRecord(Reader)
        While Reader.Read
            ClassroomID = CType(Reader("ClassroomID"), Integer)
            ClassroomDescription = CType(Reader("ClassroomDescription"), String)

            Students = GetClassroomStudents(ClassroomID)
        End While
    End Sub

    Private Function GetClassroomStudents(ByVal ClassroomID As Integer) As List(Of Student)
        Dim StudentData As DataTable
        Dim ClassroomStudents As New List(Of Student)

        ' I PULL A LIST OF STUDENTS RELATED TO THE SPECIFIC CLASSROOMID

        For Each StudentRow As DataRow In StudentData.Rows
            Dim NewStudent As New Student(CType(StudentRow("studentid"), Integer))

            ClassroomStudents.Add(NewStudent)
        Next StudentRow

        Return ClassroomStudents
    End Function
End Class
  

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

 Public Class Student
    Public Property StudentID As Integer
    Public Property Name As String
    Public Property Classrooms As List(Of Classroom)
    ...
    Private Function GetStudentClassrooms(ByVal StudentID As Integer) As List(Of Classroom)
        Dim ClassroomData As DataTable
        Dim StudentClassrooms As New List(Of Classroom)

        ' PULL A LIST OF CLASSROOMS RELATED TO THE SPECIFIC STUDENTID

        For Each ClassroomRow As DataRow In ClassroomData.Rows
            Dim NewClassroom As New Classroom(CType(ClassroomRow("classroomid"), Integer))

            StudentClassrooms.Add(NewClassroom)
        Next ClassroomRow 

        Return StudentClassrooms
    End Function
End Class
  

Итак, мое оцепенение на данный момент заключается в том, как мне предотвратить постоянное переключение между классами и студентами, заполняющими одни и те же объекты снова и снова в бесконечном цикле?

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

В некоторых результатах поиска, которые я выполнил, также упоминалась возможность какого-то «обратного отслеживания», что звучит круто, но, похоже, не очень похоже на работу в ситуации такого типа. Конечно, я могу ошибаться в этом, и я хотел бы увидеть какую-либо реализацию, которая каким-то образом способна разумно определять, просто ли я повторяю одни и те же вещи снова и снова.

Что я хотел бы видеть, так это то, что Classroom создание объекта верхнего уровня должно забрать свои Student объекты, которые должны забрать любые дополнительные Classroom объекты, с которыми они связаны, включая каждый из объектов этого Classroom объекта Student , а затем это останавливается.

Я надеюсь, что все имеет смысл. На самом деле это даже сложнее, чем это, поскольку существуют другие подобные объекты, которые будут привязаны обратно к объекту верхнего уровня ( Classroom ), который также должен следовать тем же основным правилам — не копаться слишком глубоко в «вспомогательных» записях и предотвращать бесконечный рекурсивный цикл

Если необходимо какое-либо разъяснение, пожалуйста, дайте мне знать. Я искренне ценю любую помощь, которую вы можете предоставить.

Комментарии:

1. Почему у ClassRoom должны быть какие-либо интересы к другим ClassRoom объектам?

2. Если я понимаю ваш вопрос, ответ «нет». Не в том смысле, что одному Classroom нужно знать, что другой Classroom существует сам по себе . Только в том, что касается Student .

3. Ваши классы немного перегружены. Каждому классу не нужен список учащихся — достаточно списка идентификаторов, представляющих учащихся. GetClassroomStudents создает НОВЫХ УЧЕНИКОВ вместо получения списка текущих учеников из некоторой их коллекции. Если бы вам пришлось повторять свой список, чтобы отметить некоторые отсутствующие или установленные оценки, это было бы сделано для другого набора учащихся, чем в основной коллекции. В противном случае linq упрощает извлечение наборов на основе того или иного свойства.

4. Я думаю, вы можете неправильно понять. GetClassroomStudents На самом деле не создается новых учеников, это просто создание нового экземпляра Student объекта для каждого из существующих Students в этом Classroom . База данных настроена с таблицей «многие ко многим» «index» между двумя таблицами, которые содержат эти элементы данных, которые я использую для получения определенных Classroom / Student пар. Если бы я пришел к этому, используя Student в качестве «верхнего уровня», я бы хотел извлечь все, Classrooms которым Student присвоен, наряду с любым другим Students в этом Classroom

5. Является ли это многопользовательским приложением, в котором учащиеся и классы могут обновляться в любое время, что требует нового вызова базы данных для каждого запроса о студентах и классах, или вы могли бы хранить все данные на стороне клиента во время выполнения?

Ответ №1:

Я думаю, у меня может быть идея, как это решить, но я хотел бы получить некоторые отзывы о моей идее.

Если я создам перегрузку New конструктора, который принимает логическое значение ( GetRelatedDetails ), то я могу вызвать его изначально с установленным флагом True . Практически все остальное должно оставаться неизменным, кроме передачи этого значения по цепочке. Используя приведенный выше пример, это выглядело бы примерно так:

 Public Class Classroom
    Public Property ClassroomID As Integer
    Public Property ClassroomDescription As String
    Public Property Students As List(Of Student)

    Public Sub New(ByVal ClassroomID As Integer)
        Initialize()
        GetClassroomDetail(ClassroomID, False)
    End Sub

    ' OVERLOAD WITH BOOLEAN VALUE TO GET RELATED DETAILS
    Public Sub New(ByVal ClassroomID As Integer, ByVal GetRelatedDetails As Boolean)
        Initialize()
        GetClassroomDetail(ClassroomID, GetRelatedDetails)
    End Sub

    Public Sub GetClassroomDetail(ByVal ClassroomID As Integer, ByVal GetRelatedDetails As Boolean)
        Dim Reader As SqlDataReader

        ' HERE'S WHERE I MAKE THE CALL TO GET THE 
        ' CLASSROOM RECORD DETAILS FROM THE DATABASE

        FillClassroomRecord(Reader, GetRelatedDetails)
    End Sub

    Private Sub FillClassroomRecord(ByVal Reader As SqlDataReader, ByVal GetRelatedDetails As Boolean)
        While Reader.Read
            ClassroomID = CType(Reader("ClassroomID"), Integer)
            ClassroomDescription = CType(Reader("ClassroomDescription"), String)

            If GetRelatedDetails Then
                Students = GetClassroomStudents(ClassroomID)
            End If
        End While
    End Sub

    Private Function GetClassroomStudents(ByVal ClassroomID As Integer) As List(Of Student)
        Dim StudentData As DataTable
        Dim ClassroomStudents As New List(Of Student)

        ' I PULL A LIST OF STUDENTS RELATED TO THE SPECIFIC CLASSROOMID

        For Each StudentRow As DataRow In StudentData.Rows
            Dim NewStudent As New Student(CType(StudentRow("studentid"), Integer))

            ClassroomStudents.Add(NewStudent)
        Next StudentRow

        Return ClassroomStudents
    End Function
End Class
  

Затем Student объект делает в основном то же самое:

 Public Class Student
    Public Property StudentID As Integer
    Public Property StudentName As String
    Public Property Classrooms As List(Of Classroom)

    Public Sub New(ByVal StudentID As Integer)
        Initialize()
        GetStudentDetail(StudentID, False)
    End Sub

    ' OVERLOAD WITH BOOLEAN VALUE TO GET RELATED DETAILS
    Public Sub New(ByVal StudentID As Integer, ByVal GetRelatedDetails As Boolean)
        Initialize()
        GetStudentDetail(StudentID, GetRelatedDetails)
    End Sub

    Public Sub GetStudentDetail(ByVal StudentID As Integer, ByVal GetRelatedDetails As Boolean)
        Dim Reader As SqlDataReader

        ' HERE'S WHERE I MAKE THE CALL TO GET THE 
        ' STUDENT RECORD DETAILS FROM THE DATABASE

        FillStudentRecord(Reader, GetRelatedDetails)
    End Sub

    Private Sub FillStudentRecord(ByVal Reader As SqlDataReader, ByVal GetRelatedDetails As Boolean)
        While Reader.Read
            StudentID = CType(Reader("StudentID"), Integer)
            StudentName = CType(Reader("StudentName"), String)

            If GetRelatedDetails Then
                Classrooms = GetStudentClassrooms(StudentID)
            End If
        End While
    End Sub

    Private Function GetStudentClassrooms(ByVal StudentID As Integer) As List(Of Classroom)
        Dim ClassroomData As DataTable
        Dim StudentClassrooms As New List(Of Classroom)

        ' PULL A LIST OF CLASSROOMS RELATED TO THE SPECIFIC STUDENTID

        For Each ClassroomRow As DataRow In ClassroomData.Rows
            Dim NewClassroom As New Classroom(CType(ClassroomRow("classroomid"), Integer))

            StudentClassrooms.Add(NewClassroom)
        Next ClassroomRow 

        Return StudentClassrooms
    End Function
End Class
  

Вы заметите, что, даже если я передам значение True исходному объекту, когда он перейдет к Fill методу и перейдет к GetClassroomStudents или GetStudentClassrooms , это просто вызовет New перегрузку конструктора, значение которой по умолчанию равно False . Таким образом, я предполагаю, что это должно предотвратить бесконечное повторение одних и тех же записей снова и снова.

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