【问题标题】:Is it possible to create a fixed-length array property是否可以创建一个固定长度的数组属性
【发布时间】:2013-12-18 17:20:03
【问题描述】:

我有一种情况,我需要创建一个 DTO 类来存储子项列表,但列表中子项的数量需要固定。由于它是 DTO,我需要能够修改列表中每个项目的值,但我不希望使用代码能够从列表中添加或删除项目。

例如,假设我需要一个 Person 类,并且我需要该 Person 类来存储电话号码列表。为了这个例子,假设一个人只能拥有三个电话号码。在任何其他语言中,我只是将列表创建为数组,因为其他语言中的数组无法调整大小。但是,在 VB.NET 中,您始终可以在任何数组上调用 ReDim,即使您使用 VBFixedArray 属性也是如此。例如,这 做我需要的:

Public Sub Main()
    Dim p As New Person()
    p.PhoneNumbers(0) = "555-555-5555"
    ReDim Preserve p.PhoneNumbers(5)  ' Successfuly changes the size of the array--not good
End Sub

Public Class Person
    <VBFixedArray(2)>
    Public PhoneNumbers As String() = {"", "", ""}
End Class

如果我尝试使用IReadOnlyList,则大小会固定,但项目本身也是如此。例如,这是行不通的:

Public Sub Main()
    Dim p As New Person()
    p.PhoneNumbers(0) = "555-555-5555" ' Compile error: Property 'Item' is 'ReadOnly'.
End Sub

Public Class Person
    Public ReadOnly Property PhoneNumbers As IReadOnlyList(Of String)
        Get
            Return _phoneNumbers.AsReadOnly()
        End Get
    End Property
    Private _phoneNumbers As New List(Of String)({"", "", ""})
End Class

那么,在 VB.NET 中,我如何在我的 DTO 类上创建一个公共属性,其中包含一个固定长度的项目列表,其中项目的值可以更改,但项目的数量不能。

【问题讨论】:

    标签: arrays vb.net properties


    【解决方案1】:

    要考虑的另一个选项是使 PhoneNumbers 属性参数化(仅返回字符串而不是数组)。这会强制使用代码一次获取一个电话号码,这对您来说可能是也可能不是问题。我在这个代码示例中使用了 List(of String),但它可以很容易地改用数组。

    Public Class Person
        Private _lstPhones As New List(Of String)
        Private Const _intPhoneUpperBounds As Integer = 3
    
        Public Sub New()
            For intCursor As Integer = 0 To _intPhoneUpperBounds
                _lstPhones.Add(String.Empty)
            Next
        End Sub
    
        Public Property Phones(ByVal intIndex As Integer) As String
            Get
                If _lstPhones.Count > intIndex Then
                    Return _lstPhones(intIndex)
                Else
                    'Or exception.
                    Return String.Empty
                End If
            End Get
            Set(ByVal value As String)
                If intIndex < _intPhoneUpperBounds Then
                    _lstPhones(intIndex) = value
                Else
                    'Throw Exception
                End If
            End Set
        End Property
    End Class
    

    【讨论】:

    • 有趣的想法。我尽量避免属性上的参数,因为其他语言不太支持它们,但这是一个好点。我想这种方法的另一个缺点是,如果不为此添加另一个属性,使用代码就无法获得计数,尽管在这些情况下,通常这将是一个已知的常数,无论如何。感谢您的输入 +1
    【解决方案2】:

    确实,在 VB.NET 中,所有数组都可以使用 ReDim 调整大小,但这实际上是一种错觉。底层 MSIL 实际上并不支持可调整大小的数组。当您在 VB.NET 中对数组调用 ReDim Preserve 时,它不会调整现有数组的大小 - 它实际上会创建一个新数组,将数据从旧数组复制到新数组,然后将您的数组变量指向新数组。请记住,.NET 中的数组是引用类型。例如:

    Dim test1() As String = {"1", "2", "3"}
    Dim test2() As String = test1
    test2(0) = "after"
    Console.WriteLine(String.Join(", ", test1))  ' Outputs "after, 2, 3"
    

    因此,如果您为数组创建属性,则无论何时使用ReDim 调整属性数组的大小,都会调用属性设置器。例如:

    Public Sub Main()
        Dim p As New Person()
        ReDim p.PhoneNumbers(3)  ' Outputs "Setter called"
    End Sub
    
    Public Class Person
        Public Property PhoneNumbers As String()
            Get
                Return _phoneNumbers
            End Get
            Set(value As String())
                Console.WriteLine("Setter called")
                _phoneNumbers = value
            End Set
        End Property
        Private _phoneNumbers As String()
    End Class
    

    因此,如果您想创建一个固定长度的列表,其中项目可以更改,但项目数量不能更改,您可以创建一个返回数组的只读属性,如下所示:

    Public Sub Main()
        Dim p As New Person()
        p.PhoneNumbers(0) = "555-555-5555"  ' Works
        ReDim p.PhoneNumbers(5)  ' Compile error: Property 'PhoneNumbers' is 'ReadOnly'.
    End Sub
    
    Public Class Person
        Public ReadOnly Property PhoneNumbers As String()
            Get
                Return _phoneNumbers
            End Get
        End Property
        Private _phoneNumbers(2) As String
    End Class
    

    或者,如果您不想将列表公开为数组,则可以使属性返回 IList,如下所示:

    Public Sub Main()
        Dim p As New Person()
        p.PhoneNumbers(0) = "555-555-5555"  ' Works
        Console.WriteLine(p.PhoneNumbers.IsReadOnly)  ' Outputs "True"
        p.PhoneNumbers.Add("555-555-5555")  ' Compiles, but throws a NotSupportedException: "Collection was of a fixed size."
    End Sub
    
    Public Class Person
        Public ReadOnly Property PhoneNumbers As IList(Of String)
            Get
                Return _phoneNumbers
            End Get
        End Property
        Private _phoneNumbers(2) As String
    End Class
    

    你会认为你可以让属性返回一个IReadOnlyList,但如果你这样做了,那么你就不能改变列表中任何项目的值(至少在没有强制转换的情况下不能),因为实例:

    Public Sub Main()
        Dim p As New Person()
        p.PhoneNumbers(0) = "555-555-5555"  ' Compile error: Property 'Item' is 'ReadOnly'.
    End Sub
    
    Public Class Person
        Public ReadOnly Property PhoneNumbers As IReadOnlyList(Of String)
            Get
                Return _phoneNumbers
            End Get
        End Property
        Private _phoneNumbers(2) As String
    End Class
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-17
      • 2011-12-27
      相关资源
      最近更新 更多