How to deal with the immutability of returned structs?

前端 未结 6 1885

I\'m writing a game that has a huge 2D array of \"cells\". A cell takes only 3 bytes. I also have a class called CellMap, which contains the 2D array as a private field, and pro

6条回答
  •  离开以前
    2021-02-04 17:18

    Eric Lippert's approach is good, but I would suggest using a base class rather than an interface for the indirect accessor. The following program demonstrates a class which acts like a sparse array of points. Provided that one never persists any item of type PointRef(*), things should work beautifully. Saying:

      MyPointHolder(123) = somePoint
    

    or

      MyPointHolder(123).thePoint = somePoint
    

    will both create a temporary pointRef object (a pointRef.onePoint in one case; a pointHolder.IndexedPointRef in the other) but the widening typecasts work to maintain value semantics. Of course, things would have been much easier if (1) methods on value types could be marked as mutators, and (2) writing a field of a structure accessed via property would could automatically read the property, edit the temporary structure, and write it back. The approach used here works, though alas I don't know any way to make it generic.

    (*) Items of type PointRef should only be returned by properties, and should never be stored in a variable or used as parameters to anything other than a setter property which will convert to a Point.

    MustInherit Class PointRef
        Public MustOverride Property thePoint() As Point
        Public Property X() As Integer
            Get
                Return thePoint.X
            End Get
            Set(ByVal value As Integer)
                Dim mypoint As Point = thePoint
                mypoint.X = value
                thePoint = mypoint
            End Set
        End Property
        Public Property Y() As Integer
            Get
                Return thePoint.X
            End Get
            Set(ByVal value As Integer)
                Dim mypoint As Point = thePoint
                mypoint.Y = value
                thePoint = mypoint
            End Set
        End Property
        Public Shared Widening Operator CType(ByVal val As Point) As PointRef
            Return New onePoint(val)
        End Operator
        Public Shared Widening Operator CType(ByVal val As PointRef) As Point
            Return val.thePoint
        End Operator
        Private Class onePoint
            Inherits PointRef
    
            Dim myPoint As Point
    
            Sub New(ByVal pt As Point)
                myPoint = pt
            End Sub
    
            Public Overrides Property thePoint() As System.Drawing.Point
                Get
                    Return myPoint
                End Get
                Set(ByVal value As System.Drawing.Point)
                    myPoint = value
                End Set
            End Property
        End Class
    End Class
    
    
    Class pointHolder
        Dim myPoints As New Dictionary(Of Integer, Point)
        Private Class IndexedPointRef
            Inherits PointRef
    
            Dim ref As pointHolder
            Dim index As Integer
            Sub New(ByVal ref As pointHolder, ByVal index As Integer)
                Me.ref = ref
                Me.index = index
            End Sub
            Public Overrides Property thePoint() As System.Drawing.Point
                Get
                    Dim mypoint As New Point(0, 0)
                    ref.myPoints.TryGetValue(index, mypoint)
                    Return mypoint
                End Get
                Set(ByVal value As System.Drawing.Point)
                    ref.myPoints(index) = value
                End Set
            End Property
        End Class
    
        Default Public Property item(ByVal index As Integer) As PointRef
            Get
                Return New IndexedPointRef(Me, index)
            End Get
            Set(ByVal value As PointRef)
                myPoints(index) = value.thePoint
            End Set
        End Property
    
        Shared Sub test()
            Dim theH1, theH2 As New pointHolder
            theH1(5).X = 9
            theH1(9).Y = 20
            theH2(12).X = theH1(9).Y
            theH1(20) = theH2(12)
            theH2(12).Y = 6
            Dim h5, h9, h12, h20 As Point
            h5 = theH1(5)
            h9 = theH1(9)
            h12 = theH2(12)
            h20 = theH1(20)
        End Sub
    End Class
    

提交回复
热议问题