VB.NET How To Prevent Infinite Recursion During Object Population

时间:2016-10-20 20:10:45

标签: vb.net recursion

I'm a bit stuck right now in trying to determine the best solution to prevent an infinite recursion loop. Perhaps it's not exactly "recursion", but it's a set of function calls that I can pretty much guarantee will be calling each other indefinitely if I can't come up with a solution.

In trying to figure out how to explain the issue, it seems the best way I can come up with is to start with some simplified (and redacted) code. For this example, I'll use a Classroom and a Student.

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

So far, pretty straight forward. However, the problem comes in the fact that the same student may be tied to multiple classrooms. I want to have a similar method in the Student object to be able to pull all related classrooms for that 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

So, my consternation at this point is, how do I prevent it from constantly looping back and forth between the classrooms and the students populating the same things over and over again in an infinite loop?

The only thing I can think to do is to set a Boolean variable somewhere that I can set to identify whether or not to keep drilling down. The problem is that my brain is a bit fried at the moment, and I can't figure out how to implement such a solution.

Some of the searching I've done also mentioned the possibility of some sort of "backtracking", which sounds cool, but doesn't seem very likely to work in this type of situation. Of course, I could be wrong on that, and I'd love to see any sort of implementation that is somehow capable of intelligently identifying if I'm just looping the same things over and over again.

What I'd like to see happen is the top-level Classroom object creation should pick up its Student objects, which should pick up any additional Classroom objects to which they are associated, including each of those Classroom object's Student objects, and then it stops.

I hope that all makes sense. It's actually even more complicated than this as there are other similar objects that will be tied back to the top-level object (Classroom), which should also follow the same basic rules - don't dig too deep into the "sub" records, and prevent an infinite recursive loop

If any clarification is necessary, please let me know. I truly appreciate any assistance you can provide.

1 个答案:

答案 0 :(得分:0)

我想我可能知道如何解决这个问题,但我想对我的想法得到一些反馈。

如果我创建了一个接受布尔值(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或{ {1}},它只调用默认为GetStudentClassrooms的{​​{1}}构造函数重载。这样,我猜它应该阻止它一遍又一遍地无条件地循环回来。

当然,我对实施此方法的最佳方式持开放态度,但我认为这是我现在要做的。