簡體   English   中英

C#如何為集合創建公共getter和setter以及私有方法?

[英]C# How to make public getters and setters and private methods for a collection?

我想要一個帶有(例如)SortedList集合“SrtdLst”屬性的類“A”,並且在這個類“A”中允許添加或減去“SrtdLst”項。 但是在類“A”的實例中,只允許獲取或設置項目的內容,而不是添加新項目或減去現有項目。 在代碼中:

class A
{
    public SortedList<string, string> SrtdLst = new SortedList<string, string>();

    public A()
    {
        // This must work:
        SrtdLst.Add("KeyA", "ValueA");
        // This too:
        SrtdLst["KeyA"] = "ValueAAA";
    }
}

class B
{
    public A a = new A();
    public B()
    {
        // I want the following code to fail:
        a.SrtdLst.Add("KeyB", "ValueB");
        // But this must work:
        a.SrtdLst["KeyA"] = "ValueBBB";
   }
}

更新:我想創建一個類System.Data.SqlClient.SqlCommand。 對於存儲過程,您可以使用填充“參數”集合的成員“DeriveParameters”,因此只能修改每個項目的值。

如何才能做到這一點?

如果要在編譯時禁止修改操作,則需要類型安全的解決方案。

為公開允許的操作聲明接口。 使用該接口作為屬性類型。

public interface IReadOnlyList<T>
{
    T this[int index] { get; }

    int Count { get; }
}

然后聲明一個實現該接口的類,並繼承自標准集合類。

public class SafeList<T> : List<T>, IReadOnlyList<T> { }

假設您獲得了正確的接口定義,您不需要手動實現任何東西,因為基類已經提供了實現。

使用該派生類作為存儲屬性值的字段的類型。

public class A
{
    private SafeList<string> _list = new SafeList<string>();

    public IReadOnlyList<string>
    {
        get { return _list; }
    }
}

在A類中,您可以直接使用_list ,因此修改內容。 A類客戶端只能使用IReadOnlyList<T>提供的操作子集。

對於您的示例,您使用的是SortedList而不是List,因此接口可能需要

public interface IReadOnlyDictionary<K, V> : IEnumerable<KeyValuePair<K, V>>
{
    V this[K index] { get; }        
}

我已經讓它繼承了IEnumerable,無論如何都是readonly,所以非常安全。 安全課程將是:

public class SafeSortedList<K, V> : SortedList<K, V>, IReadOnlyDictionary<K, V> { }

但除此之外它也是一樣的想法。

更新:剛剛注意到(由於某種原因,我無法理解)你不想禁止修改操作 - 你只是想禁止一些修改操作。 很奇怪,但它仍然是相同的解決方案。 無論您想要允許哪些操作,請在界面中“打開它們”:

public interface IReadOnlyDictionary<K, V> : IEnumerable<KeyValuePair<K, V>>
{
    V this[K index] { get; set; }        
}

當然,現在這個界面的名稱是錯誤的...為什么你想要禁止通過添加添加但不通過索引器禁止它? (索引器可用於添加項目,就像Add方法一樣。)

更新

從您的評論中我認為您的意思是您希望允許分配現有鍵/值對的值,但不允許分配給以前未知的鍵。 顯然,因為鍵是在運行時通過字符串指定的,所以在編譯時無法捕獲它。 所以你也可以去運行時檢查:

public class FixedSizeDictionaryWrapper<TKey, TValue> : IDictionary<TKey, TValue>
{
    IDictionary<TKey, TValue> _realDictionary;

    public FixedSizeDictionaryWrapper(IDictionary<TKey, TValue> realDictionary)
    {
        _realDictionary = realDictionary;
    }

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

        set 
        {
            if (!_realDictionary.Contains(key))
                throw new InvalidOperationException();

            _realDictionary[key] = value;
        }
    }

    // Implement Add so it always throws InvalidOperationException

    // implement all other dictionary methods to forward onto _realDictionary
}

任何時候你有一個普通的字典,並且你想把它交給一些你不信任更新現有值的方法,請將其包裝在其中一個中。

編輯:原始答案如下。 正如耳語所指出的那樣,我沒有注意到你並不是要求它只讀 - 只是為了防止Add操作。 這對我來說聽起來不是一個好主意,因為Add和indexer-setter之間的唯一區別是如果元素已經存在,則Add拋出異常。 無論如何,這很容易被呼叫者偽造。

為什么要限制一個操作?


原始答案

首先,不要使用公共字段。 這是遇到問題的絕對方法。

看起來你想要一個圍繞任意IDictionary的只讀包裝類。 然后,您可以擁有一個返回包裝器的公共屬性,同時從類中訪問私有變量。 例如:

class A
{
    private SortedList<string, string> sortedList = new SortedList<string, string>();

    public IDictionary<string, string> SortedList 
    {
        get { return new ReadOnlyDictionaryWrapper(sortedList);
    }

    public A()
    {
        sortedList.Add("KeyA", "ValueA");
        sortedList["KeyA"] = "ValueAAA";
    }
}

現在你必須找到一個ReadOnlyDictionary實現......我現在無法實現它,但如果有必要,我會稍后再回來......

只需將列表設為私有,並將其作為索引器公開:

class A {

   private SortedList<string, string> _list;

   public A() {
      _list = new SortedList<string, string>()
   }

   public string this[string key] {
      get {
         return _list[key];
      }
      set {
         _list[key] = value;
      }
   }

}

現在您只能使用索引訪問項目:

a["KeyA"] = "ValueBBB";

但是,由於列表的索引器允許創建新項目,因此您必須在索引器中添加代碼,以防止在您不希望這樣做的情況下執行此操作。

如果密鑰在類之外是已知的,那么您可以將ChangeItem(key,newValue)和ReadItem(key)添加到包裝類中。 然后將SortedList保持為類的私有。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM