我创建了一个包含应用程序配置的类,以便多个线程可以访问其中的值。显然,我在属性和设置或读取这些值的方法中执行锁定。
public class Settings
{
private readonly object m_BackupServersLocker = new object();
private readonly List<Uri> m_BackupServers = new List<Uri>();
private readonly object m_ExcludedFileExtensionsLocker = new object();
private readonly List<string> m_ExcludedFileExtensions = new List<string>();
/// <summary>
/// The working threads use this property to get the addresses of the remote backup servers.
/// </summary>
public IEnumerable<Uri> RemoteBackupServers
{
get
{
lock (m_RemoteBackupServersLocker)
{
List<Uri> endpoints = new List<Uri>();
foreach (var uri in m_RemoteBackupServers)
{
string uriString = string.Copy(uri.ToString());
endpoints.Add(new Uri(uriString));
}
return endpoints;
}
}
}
/// <summary>
/// This method is invoked by the thread which reads the configuration from file.
/// </summary>
/// <param name="uri"></param>
public bool InsertRemoteBackupServer(Uri uri)
{
lock (m_RemoteBackupServersLocker)
{
if (uri == null) return false;
return m_RemoteBackupServers.Add(uri);
}
}
/// <summary>
/// This method is invoked by the thread which reads the configuration from file.
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
public bool RemoveRemoteBackupServer(Uri uri)
{
lock (m_RemoteBackupServersLocker)
{
if (uri == null) return false;
return m_RemoteBackupServers.Remove(uri);
}
}
/// <summary>
/// The working threads use the property to get the list of excluded extensions.
/// The property is also invoked by the thread which reads the configuration from file, in order to update the exclusion list.
/// </summary>
public IEnumerable<string> ExcludedFileExtensions
{
get
{
lock (m_ExcludedFileExtensionsLocker)
{
List<string> temp = new List<string>();
foreach (var extension in m_ExcludedFileExtensions)
{
string extString = string.Copy(extension);
temp.Add(extString);
}
return temp;
}
}
set
{
lock (m_ExcludedFileExtensionsLocker)
{
m_ExcludedFileExtensions.Clear();
foreach (var extension in value)
{
temp.Add(extension);
}
return temp;
}
}
}
}
为了返回IEnumerable<Uri>
和IEnumerable<string>
,我使用string
方法执行了string.Copy
的副本。但是真的有必要制作那份副本吗?我决定根据以下推理制作string
的副本:如果属性只返回成员属性(即对该属性的引用),读者线程可以更改它们,所以我决定返回深度这些清单的副本。
但是,字符串是不可变的,所以不必通过复制每个字符串和每个字符串来复制这些列表吗?换句话说,如果我按如下方式更改上述示例中的ExcludedFileExtensions
属性,那么读者线程是否可以更改string
变量中的原始m_ExcludedFileExtensions
?
public IEnumerable<string> ExcludedFileExtensions
{
get
{
lock (m_ExcludedFileExtensionsLocker)
{
return new List<string>(m_ExcludedFileExtensions);
}
}
}
答案 0 :(得分:2)
查看ReadOnlyCollection<T>
类型。
以下是MSDN文章:https://msdn.microsoft.com/en-us/library/ms132474.aspx
基本上,
return new ReadOnlyCollection<string>(m_ExcludedFileExtensions);
将在不执行深层复制的情况下提供列表的包装,但阻止任何人从外部修改基础列表。当然,字符串本身也是不可变的。
答案 1 :(得分:1)
您实施ExcludedFileExtensions
方法正在做一些您可能不需要做的事情。
您只需要在get
上返回列表内容的副本,如果
Settings
课程中的操作(即您Add
或Remove
项目)而改变,或者< / LI>
List<string>
,然后将其用于Add
或Remove
元素。否则你可以这样做:
public IEnumerable<string> ExcludedFileExtensions
{
get
{
var current = m_ExcludedFileExtensions;
return current;
// If it could change/you want to prevent client casts:
// return an immutable copy.
// return current.ToArray();
}
set
{
// Personally I find a setter replacing the entire contents
// rather odd, but then again I don't know your use case.
var newList = value != null ? value.ToList() : new List<string>();
m_ExcludedFileExtensions = newList;
}
}
您还必须将m_ExcludedFileExtensions
的声明更改为:
private volatile List<string> m_ExcludedFileExtensions = new List<string>();
是的,字符串是不可变的,所以在返回时你永远不必克隆它们。