2013-12-02 4 views
1

У меня возникла ситуация, когда мне нужно создать класс 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 класс, в котором содержится список фиксированной длины элементов, где значение элементы могут быть изменены, но количество элементов не может.

ответ

2

Это правда, что в VB.NET все массивы изменяются по размеру, используя ReDim, но это на самом деле иллюзия. Основной MSIL фактически не поддерживает масштабируемые массивы. Когда вы вызываете ReDim Preserve на массив в VB.NET, он не изменяет размер существующего массива - он фактически создает новый массив, копирует данные из старого массива в новый, а затем указывает вашу переменную массива на новый массив , Имейте в виду, что массивы в .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 
2

Другой вариант, который следует учитывать, - сделать свойство 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

Интересная идея.Я стараюсь избегать параметров по свойствам, поскольку они плохо поддерживаются другими языками, но это хороший момент. Я предполагаю, что другой подход к этому подходу состоял бы в том, что потребительский код не смог бы получить счет, не добавляя для этого другого свойства, хотя обычно это была бы известная константа в этих ситуациях. Спасибо за ввод +1 –

Смежные вопросы