Pergunta

Eu gostaria de ter uma classe "A" com uma (por exemplo) coleta SortedList "SrtdLst" propriedade, e dentro desta classe "A" permite a adição ou subtração de itens "SrtdLst". Mas em uma instância da classe "A", só permitem obter ou definir o conteúdo dos itens, não para adicionar novos itens ou subtrair os já existentes. No código:

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

UPDATE: Eu quero criar uma classe como System.Data.SqlClient.SqlCommand. Para os procedimentos armazenados que você pode usar os membros "DeriveParameters" que preenche uma coleção de "Parâmetros", portanto, apenas o valor de cada item pode ser modificado.

Como isso pode ser feito?

Foi útil?

Solução

Se você quer proibir as operações de modificação em tempo de compilação, você precisa de uma solução de tipo seguro.

Declare uma interface para as operações permitidas ao público. Use essa interface como o tipo de propriedade.

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

    int Count { get; }
}

Em seguida, declarar uma classe que implementa essa interface e herda da classe de coleção padrão.

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

Assumindo que você obter o direito de definição de interface, você não precisa implementar qualquer coisa à mão, como a classe base já fornece as implementações.

Use essa classe derivada como o tipo do campo que armazena o valor da propriedade.

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

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

Dentro de classe A, você pode usar _list diretamente, e assim modificar o conteúdo. Os clientes da classe A, só será capaz de usar o subconjunto de operações disponíveis via IReadOnlyList<T>.

Para o seu exemplo, você estiver usando SortedList em vez de List, de modo a interface provavelmente precisa ser

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

Eu fiz isso herdar IEnumerable bem, que é somente leitura de qualquer maneira, por isso é perfeitamente seguro. A classe seguro seria então:

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

Mas por outro lado é a mesma idéia.

Atualização: só notei que (por alguma razão eu não posso fathom) não querem proibir operações modificando - você só quer proibir algumas operações modificadoras. Muito estranho, mas ainda é a mesma solução. O que quer que as operações que deseja permitir, "abri-los" na interface:

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

É claro, esse é o nome errado para a interface agora ... porque na terra que você quer proibir a adição através de Adicionar, mas não proibi-lo através do indexador? (O indexador pode ser usado para adicionar itens, assim como o pode adicionar método.)

Atualizar

Desde o seu comentário Acho que você quer dizer que você deseja permitir atribuição para o valor de um par chave / valor existente, mas a atribuição de não permitir a uma chave até então desconhecida. Obviamente, como chaves são especificados em tempo de execução por cordas, não há nenhuma maneira de pegar que em tempo de compilação. Assim, você pode muito bem ir para a verificação de tempo de execução:

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
}

Toda vez que você tem um dicionário comum e que pretende entregá-lo a algum método que você não confia para atualizar os valores existentes, envolvê-la em um desses.

Outras dicas

EDIT: Original resposta é abaixo. Como Earwicker aponta, eu não tinha percebido que você não são pedindo para que seja somente leitura - apenas para impedir o funcionamento Add. Isso não soa como uma boa idéia para mim, como a única diferença entre Add e o indexador-setter é que Add lança uma exceção se o elemento já está presente. Isso pode ser facilmente falsificado pelo chamador de qualquer maneira.

Por que você quer para restringir apenas que uma operação?


resposta Original

Por um lado, não use campos públicos. Isso é uma maneira infalível para executar em problemas.

Parece que você quer uma classe somente leitura invólucro em torno de um IDictionary arbitrária. Você pode então ter uma propriedade pública que devolve o invólucro, enquanto você acessar a variável privada de dentro da sua classe. Por exemplo:

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

Agora você só tem que encontrar uma implementação ReadOnlyDictionary ... Eu não posso implementá-lo agora, mas eu vou estar de volta mais tarde, se necessário ...

Basta fazer a lista privada, e expô-la como um indexador:

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

}

Agora você só pode acessar os itens usando o índice:

a["KeyA"] = "ValueBBB";

No entanto, como o indexador da lista permite a criação de novos itens, você teria que adicionar código no indexador para evitar que, se você não quer que seja possível.

Se as chaves são conhecidos fora da classe, em seguida, você pode adicionar um ChangeItem (key, newValue) e ReadItem (key) para sua classe wrapper. Em seguida, manter o SortedList privada para a classe.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top