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

3

Я хотел бы иметь класс "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; который заполняет коллекцию «Параметры», так что только значение каждого элемента может быть изменено.

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

Пожалуйста, скажи мне, что ты не используешь запись без гласных символов ежедневно! Я предполагаю, что это было только для этого примера. Я имею дело с нотацией на работе весь день, которую я называю «разрешен один гласный, но это не всегда первый!»; это ужасно читать. jcollum
Да, согласен, но это сообщение в пятницу вечером ... Alex

Ваш Ответ

4   ответа
7

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

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
}

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

Я хочу запретить все, но я думал запретить индексатору добавлять элементы через сеттеры, но я не знал, как запретить добавление методом «Добавить». Как я уже говорил в своем предыдущем комментарии к Skeet, я хочу создать класс, подобный System.Data.SqlClient.SqlCommand для хранимых процедур, с элементом, подобным DeriveParameters, который заполняет коллекцию «Parameters», поэтому только значение каждого элемента может быть изменено. Alex
Смотрите дальнейшие обновления. Daniel Earwicker
FYI .NET 4.5 имеет именно эти интерфейсы IReadOnlyList <T> и IReadOnlyDictionary <TKey, TValue> Mike Chaliy
@ MikeChaliy - да, они были очень рады этим в превью VS11! Daniel Earwicker
6

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

Будь ты проклят и ваш вездесущий скит: P annakata
Я знаю! Получить работу Скит! : jcollum
Я не люблю обнаруживать ошибки во время выполнения, поэтому в моем ответе используется система статических типов. Но другая проблема (я сначала тоже этого не заметил!) Заключается в том, что ОП не требует, чтобы он был только для чтения. Они просто хотят запретить метод Add, но все же разрешают присваивание через индексатор (который также может добавлять элементы). Очень странно.. Daniel Earwicker
Ооо - я не заметил, что это должно быть только для чтения с точки зрения Add. Не хорошо. Будет редактировать. Jon Skeet
Я хочу создать класс, подобный System.Data.SqlClient.SqlCommand для хранимых процедур, с элементом, подобным DeriveParameters, и свойством «Параметры», предназначенным только для чтения. И использовать его для печати, отображения и сохранения отчетов SQL Server. Все еще думаешь, что это так странно ?? Alex
2

тора:

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

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

0

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

Похожие вопросы