2015-04-24 2 views
0

Я создал класс, который содержит конфигурацию приложения, так что несколько потоков могут получить доступ к значениям в нем. Очевидно, что я выполняю блокировку внутри свойств и методов, которые устанавливают или читают эти значения.Как предотвратить другие потоки, чтобы изменить строку?

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 s в соответствии со следующими соображениями: если свойства просто возвращают атрибут члена (то есть ссылку на этот атрибут), потоки читателей могут их изменить, поэтому я решил вернуть глубокую копию этих списков ,

Однако строки неизменяемы, поэтому нет необходимости делать копию этих списков путем копирования каждой строки и каждого uri из них? Другими словами, если я изменю свойство ExcludedFileExtensions в приведенном выше примере, то может ли поток считывателя изменить исходный string s в пределах переменной m_ExcludedFileExtensions?

public IEnumerable<string> ExcludedFileExtensions 
{ 
    get 
    { 
     lock (m_ExcludedFileExtensionsLocker) 
     { 
      return new List<string>(m_ExcludedFileExtensions); 
     } 
    } 
} 

ответ

2

Посмотрите на тип ReadOnlyCollection<T>.

Вот MSDN статья для него: https://msdn.microsoft.com/en-us/library/ms132474.aspx

По существу,

return new ReadOnlyCollection<string>(m_ExcludedFileExtensions); 

обеспечит обертку вокруг вашего списка, не делая глубокую копию, но чтобы никто из внешних изменений в основной список. И, конечно, сами строки неизменны.

1

Ваша реализация метода ExcludedFileExtensions делает некоторые действия, которые вам, возможно, не придется делать.

Вам нужно только вернуть копию содержимого списка на get, если

  1. Существует вероятность того, что содержание изменений списка по операциям в пределах вашего Settings класса (т.е. вы Add или Remove) или
  2. Вы хотите, чтобы клиенты не лили его на 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>(); 

И да, строки являются неизменными, так что вы никогда не будете иметь клонировать их при их возвращении.