Có một từ điển chung chỉ đọc có sẵn trong .NET không?


185

Tôi đang trả lại một tham chiếu đến một từ điển trong tài sản chỉ đọc của tôi. Làm cách nào để ngăn người tiêu dùng thay đổi dữ liệu của tôi? Nếu đây là một IListtôi chỉ có thể trả lại nó AsReadOnly. Có điều gì tương tự tôi có thể làm với một từ điển?

Private _mydictionary As Dictionary(Of String, String)
Public ReadOnly Property MyDictionary() As Dictionary(Of String, String)
    Get
        Return _mydictionary
    End Get
End Property

4
Phải có một số cách để làm điều đó, nếu không sẽ không có thuộc tính IsReadOnly trên IDadata ... ( msdn.microsoft.com/en-us/l
Library / bb338949.aspx

2
Nhiều lợi ích khái niệm của tính bất biến có thể đạt được mà không cần thời gian chạy thi hành nó. Nếu đây là một dự án tư nhân, hãy xem xét một phương pháp kỷ luật, không chính thức. Nếu bạn phải cung cấp dữ liệu cho người tiêu dùng, bạn nên nghiêm túc xem xét các bản sao sâu. Khi bạn cho rằng một bộ sưu tập bất biến đòi hỏi 1) tham chiếu bất biến cho bộ sưu tập 2) ngăn chặn việc tự biến đổi trình tự và 3) ngăn chặn sửa đổi các thuộc tính trên các mục của bộ sưu tập và một số trong số này có thể bị vi phạm bởi sự phản chiếu, việc thực thi thời gian chạy là không thực tế
Sprague

26
Kể từ .NET 4.5, có System.Collections.ObjectModel.ReadOnlyDixi ^ _ ^
Smartkid

2
Hiện tại cũng có Bộ sưu tập bất biến của Microsoft thông qua NuGet msdn.microsoft.com/en-us/l
Library / dn385366% 28v = vs.110% 29.aspx

Câu trả lời:


156

Đây là một cách thực hiện đơn giản bao bọc một từ điển:

public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
    private readonly IDictionary<TKey, TValue> _dictionary;

    public ReadOnlyDictionary()
    {
        _dictionary = new Dictionary<TKey, TValue>();
    }

    public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
    {
        _dictionary = dictionary;
    }

    #region IDictionary<TKey,TValue> Members

    void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
    {
        throw ReadOnlyException();
    }

    public bool ContainsKey(TKey key)
    {
        return _dictionary.ContainsKey(key);
    }

    public ICollection<TKey> Keys
    {
        get { return _dictionary.Keys; }
    }

    bool IDictionary<TKey, TValue>.Remove(TKey key)
    {
        throw ReadOnlyException();
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        return _dictionary.TryGetValue(key, out value);
    }

    public ICollection<TValue> Values
    {
        get { return _dictionary.Values; }
    }

    public TValue this[TKey key]
    {
        get
        {
            return _dictionary[key];
        }
    }

    TValue IDictionary<TKey, TValue>.this[TKey key]
    {
        get
        {
            return this[key];
        }
        set
        {
            throw ReadOnlyException();
        }
    }

    #endregion

    #region ICollection<KeyValuePair<TKey,TValue>> Members

    void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
    {
        throw ReadOnlyException();
    }

    void ICollection<KeyValuePair<TKey, TValue>>.Clear()
    {
        throw ReadOnlyException();
    }

    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        return _dictionary.Contains(item);
    }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        _dictionary.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return _dictionary.Count; }
    }

    public bool IsReadOnly
    {
        get { return true; }
    }

    bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
    {
        throw ReadOnlyException();
    }

    #endregion

    #region IEnumerable<KeyValuePair<TKey,TValue>> Members

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        return _dictionary.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion

    private static Exception ReadOnlyException()
    {
        return new NotSupportedException("This dictionary is read-only");
    }
}

11
+1 để đăng mã hoàn chỉnh và không chỉ là một liên kết, nhưng tôi tò mò, điểm của một nhà xây dựng trống trong ReadOnlyDipedia là gì? :-)
Samuel Neff

20
Coi chừng người xây dựng đó Nếu bạn thực hiện một bản sao tham chiếu của từ điển được truyền vào, một đoạn mã bên ngoài có thể sửa đổi từ điển "Chỉ đọc" của bạn. Hàm tạo của bạn nên thực hiện một bản sao đầy đủ, sâu sắc của đối số.
hỏi thăm

25
@askematves: Quan sát tốt, nhưng thực sự khá hữu ích khi sử dụng tham chiếu ban đầu trong các loại Chỉ đọc - giữ nguyên biến riêng của bạn và thay đổi nó cho người tiêu dùng bên ngoài. Ví dụ: kiểm tra các đối tượng ReadOnlyObservableCollection hoặc các đối tượng ReadOnlyCollection được tích hợp sẵn: Thomas đã cung cấp một cái gì đó hoạt động chính xác như những gì vốn có cho khung .Net. Cảm ơn Thomas! +1
Matt DeKrey

13
@ user420667: cách thức triển khai, đó là "chế độ xem chỉ đọc của từ điển không chỉ đọc". Một số mã khác có thể thay đổi nội dung của từ điển gốc và những thay đổi này sẽ được phản ánh trong từ điển chỉ đọc. Đó có thể là hành vi mong muốn, hoặc không, tùy thuộc vào những gì bạn muốn đạt được ...
Thomas Levesque

6
@Thomas: Điều đó giống như ReadOnlyCollection trong .NET BCL. Đây là chế độ chỉ đọc trên bộ sưu tập có thể thay đổi. ReadOnly không có nghĩa là bất biến và bất biến không nên được mong đợi.
Jeff Yates

229

.NET 4.5

.NET Framework 4.5 BCL giới thiệu ReadOnlyDictionary<TKey, TValue>( nguồn ).

Vì .NET Framework 4.5 BCL không bao gồm AsReadOnlytừ điển, bạn sẽ cần phải viết từ điển của riêng bạn (nếu bạn muốn). Nó sẽ giống như sau, sự đơn giản có lẽ làm nổi bật lý do tại sao nó không phải là ưu tiên của .NET 4.5.

public static ReadOnlyDictionary<TKey, TValue> AsReadOnly<TKey, TValue>(
    this IDictionary<TKey, TValue> dictionary)
{
    return new ReadOnlyDictionary<TKey, TValue>(dictionary);
}

.NET 4.0 trở xuống

Trước .NET 4.5, không có lớp .NET framework nào Dictionary<TKey, TValue>giống như ReadOnlyCollection bao bọc một Danh sách . Tuy nhiên, không khó để tạo ra một.

Dưới đây là một ví dụ - có nhiều người khác nếu bạn Google cho ReadOnlyDipedia .


7
Không có vẻ như họ nhớ thực hiện một AsReadOnly()phương pháp thông thường Dictionary<,>, vì vậy tôi tự hỏi có bao nhiêu người sẽ khám phá ra loại mới của họ. Chủ đề Stack Overflow này sẽ giúp, mặc dù.
Jeppe Stig Nielsen

@Jeppe: Tôi nghi ngờ nó có liên quan gì đến việc ghi nhớ. Mọi chi phí tính năng và tôi nghi ngờ AsReadOnly nằm trong danh sách ưu tiên cao, đặc biệt là vì nó rất dễ viết.
Jeff Yates

1
Lưu ý rằng đây chỉ đơn giản là một trình bao bọc; những thay đổi đối với từ điển cơ bản (từ được chuyển cho hàm tạo) vẫn sẽ làm thay đổi từ điển chỉ đọc. Xem thêm stackoverflow.com/questions/139592/
Mạnh

1
@JeffYates Xem xét nó đơn giản như thế nào, viết nó sẽ mất ít thời gian hơn so với việc quyết định có dành thời gian để viết nó hay không. Do đó, cá cược của tôi là "họ đã quên".
Dan Bechard

Như TrueWill đã nêu, từ điển cơ bản vẫn có thể bị đột biến. Bạn có thể muốn xem xét chuyển một bản sao của từ điển gốc cho nhà xây dựng nếu bạn muốn tính bất biến thực sự (giả sử khóa và loại giá trị cũng không thay đổi.)
user420667

19

Nó đã được công bố trong hội nghị BUILD gần đây rằng kể từ .NET 4.5, giao diện System.Collections.Generic.IReadOnlyDictionary<TKey,TValue>được bao gồm. Bằng chứng là ở đây (Mono) và ở đây (Microsoft);)

Không chắc chắn nếu ReadOnlyDictionaryđược bao gồm quá, nhưng ít nhất với giao diện, không khó để tạo ra một triển khai hiển thị giao diện chung .NET chính thức :)


5
ReadOnlyDictionary<TKey, TValue>(.Net 4.5) - msdn.microsoft.com/en-us/l
Library / gg712875.aspx

18

Hãy sử dụng trình bao bọc đơn giản của tôi. Nó KHÔNG thực hiện IDadata, vì vậy nó không phải ném ngoại lệ vào thời gian chạy cho các phương thức từ điển sẽ thay đổi từ điển. Thay đổi phương pháp đơn giản là không có. Tôi đã tạo giao diện của riêng mình cho nó được gọi là IReadOnlyDipedia.

public interface IReadOnlyDictionary<TKey, TValue> : IEnumerable
{
    bool ContainsKey(TKey key);
    ICollection<TKey> Keys { get; }
    ICollection<TValue> Values { get; }
    int Count { get; }
    bool TryGetValue(TKey key, out TValue value);
    TValue this[TKey key] { get; }
    bool Contains(KeyValuePair<TKey, TValue> item);
    void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex);
    IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator();
}

public class ReadOnlyDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
{
    readonly IDictionary<TKey, TValue> _dictionary;
    public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
    {
        _dictionary = dictionary;
    }
    public bool ContainsKey(TKey key) { return _dictionary.ContainsKey(key); }
    public ICollection<TKey> Keys { get { return _dictionary.Keys; } }
    public bool TryGetValue(TKey key, out TValue value) { return _dictionary.TryGetValue(key, out value); }
    public ICollection<TValue> Values { get { return _dictionary.Values; } }
    public TValue this[TKey key] { get { return _dictionary[key]; } }
    public bool Contains(KeyValuePair<TKey, TValue> item) { return _dictionary.Contains(item); }
    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { _dictionary.CopyTo(array, arrayIndex); }
    public int Count { get { return _dictionary.Count; } }
    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return _dictionary.GetEnumerator(); }
    IEnumerator IEnumerable.GetEnumerator() { return _dictionary.GetEnumerator(); }
}

4
+1 vì không vi phạm IDictionaryhợp đồng. Tôi nghĩ nó đúng hơn từ góc độ OOP IDictionaryđể kế thừa từ IReadOnlyDictionary.
Sam

@Sam Đồng ý, và nếu chúng tôi có thể quay lại, tôi nghĩ rằng đó là điều tốt nhất và đúng đắn nhất để có IDictionary(cho hiện tại IReadOnlyDictionary) và IMutableDictionary(cho hiện tại IDictionary).
MasterMastic

1
@MasterMastic Đó là một đề xuất kỳ quái. Tôi không nhớ bất kỳ lớp tích hợp nào khác dựa trên giả định ngược lại rằng một bộ sưu tập bất biến là những gì người dùng mong đợi theo mặc định.
Dan Bechard

11

IsReadOnly on IDictionary<TKey,TValue>được kế thừa từ ICollection<T>( IDictionary<TKey,TValue>kéo dài ICollection<T>như ICollection<KeyValuePair<TKey,TValue>>). Nó không được sử dụng hoặc thực hiện theo bất kỳ cách nào (và trên thực tế là "ẩn" thông qua việc sử dụng thực hiện rõ ràng các ICollection<T>thành viên liên quan ).

Có ít nhất 3 cách tôi có thể thấy để giải quyết vấn đề:

  1. Thực hiện chỉ đọc tùy chỉnh IDictionary<TKey, TValue>và bọc / ủy quyền cho một từ điển bên trong như đã được đề xuất
  2. Trả về một ICollection<KeyValuePair<TKey, TValue>>bộ dưới dạng chỉ đọc hoặc IEnumerable<KeyValuePair<TKey, TValue>>tùy thuộc vào việc sử dụng giá trị
  3. Sao chép từ điển bằng cách sử dụng hàm tạo sao chép .ctor(IDictionary<TKey, TValue>)và trả về một bản sao - theo cách đó người dùng có thể tự do làm điều đó khi họ muốn và nó không ảnh hưởng đến trạng thái của đối tượng lưu trữ từ điển nguồn. Lưu ý rằng nếu từ điển bạn đang nhân bản chứa các loại tham chiếu (không phải chuỗi như trong ví dụ), bạn sẽ cần thực hiện sao chép "thủ công" và sao chép các loại tham chiếu.

Như một bên; khi trưng ra các bộ sưu tập, hãy nhắm đến việc phơi bày giao diện nhỏ nhất có thể - trong trường hợp ví dụ, nó phải là IDadata vì điều này cho phép bạn thay đổi việc triển khai cơ bản mà không phá vỡ hợp đồng công khai mà loại đó lộ ra.


8

Từ điển chỉ đọc có thể được thay thế ở một mức độ nào đó Func<TKey, TValue>- Tôi thường sử dụng từ này trong API nếu tôi chỉ muốn mọi người thực hiện tra cứu; thật đơn giản và đặc biệt, thật đơn giản để thay thế phần phụ trợ mà bạn muốn. Nó không cung cấp danh sách các khóa, tuy nhiên; cho dù vấn đề đó phụ thuộc vào những gì bạn đang làm.


4

Không, nhưng nó sẽ dễ dàng để cuộn của riêng bạn. IDadata không định nghĩa một thuộc tính IsReadOnly. Chỉ cần bọc Từ điển và ném NotSupportedException từ các phương thức thích hợp.


3

Không có sẵn trong BCL. Tuy nhiên, tôi đã xuất bản một ReadOnlyDipedia (có tên là ImmutableMap) trong Dự án Extras BCL của tôi

Ngoài việc là một từ điển hoàn toàn bất biến, nó còn hỗ trợ tạo ra một đối tượng proxy thực hiện IDadata và có thể được sử dụng ở bất kỳ nơi nào mà IDadata được sử dụng. Nó sẽ đưa ra một ngoại lệ bất cứ khi nào một trong các API đột biến được gọi

void Example() { 
  var map = ImmutableMap.Create<int,string>();
  map = map.Add(42,"foobar");
  IDictionary<int,string> dictionary = CollectionUtility.ToIDictionary(map);
}

9
Bản đồ bất biến của bạn được triển khai như một cây cân bằng. Vì, trong .NET, mọi người thường mong đợi một "từ điển" được triển khai thông qua băm - và thể hiện các thuộc tính phức tạp tương ứng - bạn có thể muốn cẩn thận về việc quảng bá ImmutableMap dưới dạng "từ điển".
Glenn Slayden

có vẻ như các trang web code.msdn.com không còn tồn tại. BCLextras hiện tại đây github.com/scottwis/tiny/tree/master/third-party/BclExtras
BozoJoe

1

Bạn có thể tạo một lớp chỉ thực hiện triển khai một phần từ điển và ẩn tất cả các hàm add / remove / set.

Sử dụng một từ điển bên trong mà lớp bên ngoài chuyển tất cả các yêu cầu đến.

Tuy nhiên, vì từ điển của bạn có khả năng giữ các loại tham chiếu, nên không có cách nào bạn ngăn người dùng thiết lập giá trị trên các lớp được giữ bởi từ điển (trừ khi chính các lớp đó chỉ được đọc)


1

Tôi không nghĩ rằng có một cách dễ dàng để làm điều đó ... nếu từ điển của bạn là một phần của lớp tùy chỉnh, bạn có thể đạt được nó với một người lập chỉ mục:

public class MyClass
{
  private Dictionary<string, string> _myDictionary;

  public string this[string index]
  {
    get { return _myDictionary[index]; }
  }
}

Tôi cần có khả năng để lộ toàn bộ từ điển cũng như một người lập chỉ mục.
Rob Sobers

Đây có vẻ là một giải pháp rất tốt. Tuy nhiên, các máy khách của lớp MyClass có thể cần biết thêm về từ điển, ví dụ, để lặp qua nó. Và điều gì xảy ra nếu một khóa không tồn tại (có thể làm lộ ra TryGetValue () dưới một hình thức nào đó là một ý tưởng hay)? Bạn có thể làm cho câu trả lời và mã mẫu của bạn đầy đủ hơn?
Peter Mortensen

1

+1 Công việc tuyệt vời, Thomas. Tôi đã đọc ReadOnlyDixi một bước nữa.

Giống như giải pháp của Dale, tôi muốn loại bỏ Add(), Clear(), Remove(), vv từ IntelliSense. Nhưng tôi muốn các đối tượng dẫn xuất của tôi thực hiện IDictionary<TKey, TValue>.

Hơn nữa, tôi muốn phá vỡ đoạn mã sau: (Một lần nữa, giải pháp của Dale cũng làm điều này)

ReadOnlyDictionary<int, int> test = new ReadOnlyDictionary<int,int>(new Dictionary<int, int> { { 1, 1} });
test.Add(2, 1);  //CS1061

Dòng Add () cho kết quả:

error CS1061: 'System.Collections.Generic.ReadOnlyDictionary<int,int>' does not contain a definition for 'Add' and no extension method 'Add' accepting a first argument 

Người gọi vẫn có thể chuyển nó tới IDictionary<TKey, TValue>, nhưng NotSupportedExceptionsẽ được nâng lên nếu bạn cố gắng sử dụng các thành viên không chỉ đọc (từ giải pháp của Thomas).

Dù sao, đây là giải pháp của tôi cho bất cứ ai cũng muốn điều này:

namespace System.Collections.Generic
{
    public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
    {
        const string READ_ONLY_ERROR_MESSAGE = "This dictionary is read-only";

        protected IDictionary<TKey, TValue> _Dictionary;

        public ReadOnlyDictionary()
        {
            _Dictionary = new Dictionary<TKey, TValue>();
        }

        public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
        {
            _Dictionary = dictionary;
        }

        public bool ContainsKey(TKey key)
        {
            return _Dictionary.ContainsKey(key);
        }

        public ICollection<TKey> Keys
        {
            get { return _Dictionary.Keys; }
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            return _Dictionary.TryGetValue(key, out value);
        }

        public ICollection<TValue> Values
        {
            get { return _Dictionary.Values; }
        }

        public TValue this[TKey key]
        {
            get { return _Dictionary[key]; }
            set { throw new NotSupportedException(READ_ONLY_ERROR_MESSAGE); }
        }

        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return _Dictionary.Contains(item);
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            _Dictionary.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return _Dictionary.Count; }
        }

        public bool IsReadOnly
        {
            get { return true; }
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return _Dictionary.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (_Dictionary as IEnumerable).GetEnumerator();
        }

        void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
        {
            throw new NotSupportedException(READ_ONLY_ERROR_MESSAGE);
        }

        bool IDictionary<TKey, TValue>.Remove(TKey key)
        {
            throw new NotSupportedException(READ_ONLY_ERROR_MESSAGE);
        }

        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
        {
            throw new NotSupportedException(READ_ONLY_ERROR_MESSAGE);
        }

        void ICollection<KeyValuePair<TKey, TValue>>.Clear()
        {
            throw new NotSupportedException(READ_ONLY_ERROR_MESSAGE);
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
        {
            throw new NotSupportedException(READ_ONLY_ERROR_MESSAGE);
        }
    }
}


0
public IEnumerable<KeyValuePair<string, string>> MyDictionary()
{
    foreach(KeyValuePair<string, string> item in _mydictionary)
        yield return item;
}

2
Hoặc bạn có thể làm:public IEnumerable<KeyValuePair<string, string>> MyDictionary() { return _mydictionary; }
Pat

0

Đây là một giải pháp tồi, xem ở phía dưới.

Đối với những người vẫn đang sử dụng .NET 4.0 trở về trước, tôi có một lớp hoạt động giống như câu trả lời được chấp nhận, nhưng nó ngắn hơn nhiều. Nó mở rộng đối tượng Từ điển hiện có, ghi đè (thực sự ẩn) một số thành viên nhất định để họ ném ngoại lệ khi được gọi.

Nếu người gọi cố gắng gọi Thêm, Xóa hoặc một số thao tác đột biến khác mà Từ điển tích hợp có, trình biên dịch sẽ đưa ra lỗi. Tôi sử dụng các thuộc tính lỗi thời để nâng cao các lỗi biên dịch này. Bằng cách này, bạn có thể thay thế một Từ điển bằng ReadOnlyDipedia này và ngay lập tức xem mọi vấn đề có thể xảy ra mà không cần phải chạy ứng dụng của bạn và chờ ngoại lệ trong thời gian chạy.

Hãy xem:

public class ReadOnlyException : Exception
{
}

public class ReadOnlyDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
    public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
        : base(dictionary) { }

    public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
        : base(dictionary, comparer) { }

    //The following four constructors don't make sense for a read-only dictionary

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary() { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary(IEqualityComparer<TKey> comparer) { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary(int capacity) { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary(int capacity, IEqualityComparer<TKey> comparer) { throw new ReadOnlyException(); }


    //Use hiding to override the behavior of the following four members
    public new TValue this[TKey key]
    {
        get { return base[key]; }
        //The lack of a set accessor hides the Dictionary.this[] setter
    }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public new void Add(TKey key, TValue value) { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public new void Clear() { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public new bool Remove(TKey key) { throw new ReadOnlyException(); }
}

Giải pháp này có một vấn đề được chỉ ra bởi @supercat được minh họa ở đây:

var dict = new Dictionary<int, string>
{
    { 1, "one" },
    { 2, "two" },
    { 3, "three" },
};

var rodict = new ReadOnlyDictionary<int, string>(dict);
var rwdict = rodict as Dictionary<int, string>;
rwdict.Add(4, "four");

foreach (var item in rodict)
{
    Console.WriteLine("{0}, {1}", item.Key, item.Value);
}

Thay vì đưa ra lỗi thời gian biên dịch như tôi mong đợi hoặc ngoại lệ thời gian chạy như tôi hy vọng, mã này chạy không có lỗi. Nó in bốn số. Điều đó làm cho ReadOnlyDixi của tôi trở thành ReadWriteDipedia.


Vấn đề với cách tiếp cận đó là một đối tượng như vậy có thể được chuyển đến một phương thức mong đợi Dictionary<TKey,TValue>mà không có bất kỳ khiếu nại nào của trình biên dịch, và truyền hoặc ép buộc một tham chiếu đến loại từ điển đơn giản sẽ loại bỏ bất kỳ sự bảo vệ nào.
supercat

@supercat, tào lao, bạn nói đúng. Tôi nghĩ rằng tôi cũng có một giải pháp tốt.
2023861

Tôi nhớ làm một đạo hàm Dictionaryvới một Clonephương pháp được xâu chuỗi MemberwiseClone. Thật không may, mặc dù có thể sao chép từ điển một cách hiệu quả bằng cách sao chép các cửa hàng sao lưu, thực tế là các cửa hàng sao lưu privatethay vì protectedcó nghĩa là không có cách nào để một lớp dẫn xuất sao chép chúng; sử dụng MemberwiseClonemà không nhân bản các cửa hàng sao lưu sẽ có nghĩa là các sửa đổi tiếp theo được thực hiện cho từ điển gốc sẽ phá vỡ bản sao và các sửa đổi được thực hiện cho bản sao sẽ phá vỡ bản gốc.
supercat
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.