为什么ReadOnly Property是全球可变的?

时间:2016-01-11 09:28:18

标签: .net vb.net properties private

我的洞应用程序有一个Data类。此数据类可以从应用程序中的许多其他类进行访问。我在开头加载数据,并希望数据不会全局更改。

这就是它现在的样子(例子)

NotInheritable Class Data
Public NotInheritable Class Country
    Public CounCode As String = String.Empty
    Public ISOCode As String = String.Empty
    Public Name As String = String.Empty

    Public Overrides Function ToString() As String
        Return CounCode + ": " + ISOCode + " - " + Name
    End Function
End Class

Private Shared m_Countries As List(Of Country) = Nothing
Public Shared ReadOnly Property Countries As List(Of Country)
    Get
        Return m_Countries
    End Get
End Property
Shared Sub New()
    m_Countries = New List(Of Country)
    Dim Country As Country = New Country
    Country.Name = "Germany"
    m_Countries.Add(Country)
    Country.Name = "Italy"
    m_Countries.Add(Country)
    Country.Name = "Spain"
    m_Countries.Add(Country)
End Sub

结束班

但是当我喜欢这个时

    Dim countries As List(Of Data.Country) = Data.Countries
    countries(0).Name = "Hello World"

    Dim countries2 As List(Of Data.Country) = Data.Countries
    MessageBox.Show(countries(0).Name + " - " + countries2(0).Name)

我怀疑“Hello World - Germany”,但我会得到“Hello World - Hello World”。 为什么我可以 - 全球 - 改变财产?以及如何解决这个问题? 我想允许其他类更改国家/地区的名称,但不能更改全局。

此致

2 个答案:

答案 0 :(得分:0)

集合属性是只读的 - 不是集合本身,而不是集合中的项目。您尚未将集合重新分配给新集合,该集合是只读字段或属性访问器阻止的唯一事物。它运作正常。

只有一个集合属性才能使集合本身成为只读(您仍然可以添加/删除/等),并且制作项目中的项目集合只读。对于只读集合:请尝试ReadOnlyCollection<T>。对于项目本身:您必须将Country.Name设为只读。

不能做什么(在构造函数之外)是:

Data.Countries = New List(Of Country)

答案 1 :(得分:0)

正如马克所说,没有办法阻止这一点。您不能为属性分配不同的集合,但可以更改属性封装的集合(添加/删除/更改)

解决方法如下:

Imports System.Linq

Module StartupModule

    Sub Main()
        Dim users As New List(Of User) From {
            New User() With {.Name = "John"},
            New User() With {.Name = "Nick"}
        }

        Dim company As New Company(users)


        Console.WriteLine("names before change")
        For Each u In company.Users
            Console.WriteLine(u)
        Next
        Console.WriteLine()

        Console.WriteLine("change names")
        For Each u In company.Users
            u.Name = "something else"
        Next
        Console.WriteLine()

        Console.WriteLine("names after change")
        For Each u In company.Users
            Console.WriteLine(u)
        Next

        Console.ReadLine()
    End Sub


    Public Class Company
        Private _users As New List(Of User)    

        Public Sub New(users As List(Of User))
            Me.Users = users
        End Sub    

        Public Property Users As List(Of User)
            Get
                Return (From u In _users
                       Select DirectCast(u.Clone, User)).ToList
            End Get
            Private Set(value As List(Of User))
                _users = value
            End Set
        End Property
    End Class


    Public Class User
        Implements ICloneable

        Public Property Name As String

        Public Overrides Function ToString() As String
            Return Name
        End Function

        Public Function Clone() As Object Implements ICloneable.Clone
            Return New User() With {.Name = Name}
        End Function
    End Class

End Module

属性的getter总是返回原始对象的克隆集合。因此,您必须为您的类实现ICloneable,或者只是创建一个函数来返回对象的克隆。

这样,更改将在克隆集合中进行,而不是更改实际对象。

该方法的缺点是,无论何时引用集合(在示例中为Company.Users集合),都会创建一个新列表,其中包含原始项目的克隆。