C # Как сделать публичные методы получения и установки и частные методы для коллекции?

Я хотел бы иметь класс "A" с (например) коллекцией SortedList & quot; SrtdLst & quot; свойство, и внутри этого класса & quot; A & quot; разрешить добавление или вычитание "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. Для хранимых процедур вы можете использовать член & quot; DeriveParameters & quot; который заполняет коллекцию «Параметры», так что только значение каждого элемента может быть изменено.

Как это может быть сделано?

 Alex06 июл. 2009 г., 11:34
Да, согласен, но это сообщение в пятницу вечером ...
 jcollum03 июл. 2009 г., 22:08
Пожалуйста, скажи мне, что ты не используешь запись без гласных символов ежедневно! Я предполагаю, что это было только для этого примера. Я имею дело с нотацией на работе весь день, которую я называю «разрешен один гласный, но это не всегда первый!»; это ужасно читать.

Ответы на вопрос(4)

тора:

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";

Однако, так как индексатор списка позволяет создавать новые элементы, вам нужно добавить код в индексатор, чтобы предотвратить это, если вы не хотите, чтобы это было возможно.

оригинальный ответ ниже. Как указывает Слуховик, я не заметил, что вы Не с просьбой прочитать его только для предотвращенияAdd операция. Это звучит не очень хорошая идея для меня, как единственная разница междуAdd и индексатор-установщик это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 реализация ... я не могу реализовать это прямо сейчас, но я вернусь позже, если необходимо ...

 Alex06 июл. 2009 г., 11:50
Я хочу создать класс, подобный System.Data.SqlClient.SqlCommand для хранимых процедур, с элементом, подобным DeriveParameters, и свойством «Параметры», предназначенным только для чтения. И использовать его для печати, отображения и сохранения отчетов SQL Server. Все еще думаешь, что это так странно ??
 Daniel Earwicker03 июл. 2009 г., 22:27
Я не люблю обнаруживать ошибки во время выполнения, поэтому в моем ответе используется система статических типов. Но другая проблема (я сначала тоже этого не заметил!) Заключается в том, что ОП не требует, чтобы он был только для чтения. Они просто хотят запретить метод Add, но все же разрешают присваивание через индексатор (который также может добавлять элементы). Очень странно..
 Jon Skeet03 июл. 2009 г., 22:47
Ооо - я не заметил, что это должно быть только для чтения с точки зрения Add. Не хорошо. Будет редактировать.
 jcollum03 июл. 2009 г., 22:12
Я знаю! Получить работу Скит! :
 annakata03 июл. 2009 г., 22:03
Будь ты проклят и ваш вездесущий скит: P

вы можете добавить ChangeItem (ключ, newValue) и ReadItem (ключ) в ваш класс-оболочку. Затем держите SortedList закрытым для класса.

Решение Вопроса

вам нужно решение с безопасным типом.

Declare интерфейс для общедоступных операций. Используйте этот интерфейс в качестве типа свойства.

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; }
    }
}

В классе А вы можете использовать_list напрямую, и поэтому измените содержимое. Клиенты класса А смогут использовать только подмножество операций, доступных черезIReadOnlyList<T>.

Для вашего примера вы используете SortedList вместо List, поэтому интерфейс, вероятно, должен быть

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

Я сделал так, что он наследует и IEnumerable, который в любом случае доступен только для чтения, поэтому совершенно безопасен. Безопасный класс будет тогда:

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, но не запретить его через индексатор? (Индексатор можно использовать для добавления элементов, так же как и метод 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
}

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

 Alex06 июл. 2009 г., 12:16
Я хочу запретить все, но я думал запретить индексатору добавлять элементы через сеттеры, но я не знал, как запретить добавление методом «Добавить». Как я уже говорил в своем предыдущем комментарии к Skeet, я хочу создать класс, подобный System.Data.SqlClient.SqlCommand для хранимых процедур, с элементом, подобным DeriveParameters, который заполняет коллекцию «Parameters», поэтому только значение каждого элемента может быть изменено.
 Daniel Earwicker06 июл. 2009 г., 12:41
Смотрите дальнейшие обновления.
 Mike Chaliy18 апр. 2012 г., 17:22
FYI .NET 4.5 имеет именно эти интерфейсы IReadOnlyList <T> и IReadOnlyDictionary <TKey, TValue>
 Daniel Earwicker20 апр. 2012 г., 11:25
@ MikeChaliy - да, они были очень рады этим в превью VS11!

Ваш ответ на вопрос