为什么 ReadOnly 属性 是全局可变的?
Why ReadOnly Property are globally changeable?
我的孔应用程序有一个数据 class。此数据 class 可从应用程序中的许多其他 class 访问。我一开始就加载数据,希望数据全局不可更改。
这是现在的样子(示例)
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
结束Class
但是当我这样做的时候
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"。
为什么我可以 - 全局 - 更改 属性?以及如何解决这个问题?
我想允许其他 classes 更改国家名称,但不能更改全球名称。
此致
集合 属性 即 read-only - 不是集合本身,也不是集合中的项目。您尚未将集合重新分配给新集合,这是 only 字段或 属性 访问器阻止的事情。它运行正常。
拥有一个集合 属性 只读 不会 使集合本身 read-only(您仍然可以 add/remove/etc),并且 不 使集合中的项目read-only。对于 read-only 系列:尝试 ReadOnlyCollection<T>
。对于项目本身:您必须 Country.Name
read-only.
你不能做的(在构造函数之外)是:
Data.Countries = New List(Of Country)
正如 Marc 所说,没有办法阻止这种情况。您不能为 属性 分配不同的集合,但可以更改 属性 封装的集合 (add/remove/change)
解决方法如下:
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总是return原始对象的克隆集合。所以你必须为你的 class 实现 ICloneable
或者只是创建一个函数来 return 你的对象的克隆。
这样,更改是在克隆集合中进行的,而不是更改实际对象。
该方法的问题在于,每当您引用集合(在示例中为 Company.Users
集合)时,都会创建一个新列表,其中填充了原始项目的克隆。
我的孔应用程序有一个数据 class。此数据 class 可从应用程序中的许多其他 class 访问。我一开始就加载数据,希望数据全局不可更改。
这是现在的样子(示例)
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
结束Class
但是当我这样做的时候
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"。 为什么我可以 - 全局 - 更改 属性?以及如何解决这个问题? 我想允许其他 classes 更改国家名称,但不能更改全球名称。
此致
集合 属性 即 read-only - 不是集合本身,也不是集合中的项目。您尚未将集合重新分配给新集合,这是 only 字段或 属性 访问器阻止的事情。它运行正常。
拥有一个集合 属性 只读 不会 使集合本身 read-only(您仍然可以 add/remove/etc),并且 不 使集合中的项目read-only。对于 read-only 系列:尝试 ReadOnlyCollection<T>
。对于项目本身:您必须 Country.Name
read-only.
你不能做的(在构造函数之外)是:
Data.Countries = New List(Of Country)
正如 Marc 所说,没有办法阻止这种情况。您不能为 属性 分配不同的集合,但可以更改 属性 封装的集合 (add/remove/change)
解决方法如下:
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总是return原始对象的克隆集合。所以你必须为你的 class 实现 ICloneable
或者只是创建一个函数来 return 你的对象的克隆。
这样,更改是在克隆集合中进行的,而不是更改实际对象。
该方法的问题在于,每当您引用集合(在示例中为 Company.Users
集合)时,都会创建一个新列表,其中填充了原始项目的克隆。