C # Cómo hacer públicos getters y setters y métodos particulares para una colección?

StackOverflow https://stackoverflow.com/questions/1080644

  •  22-08-2019
  •  | 
  •  

Pregunta

Me gustaría tener una clase "A" con un (por ejemplo) de recogida SortedList propiedad "SrtdLst", y dentro de esta clase "A" permitir la adición o sustracción de elementos "SrtdLst". Pero en una instancia de la clase "A", sólo se permitirá para obtener o establecer el contenido de los artículos, no para añadir nuevos elementos o restar los ya existentes. En 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";
   }
}

ACTUALIZACIÓN: Quiero crear una clase como System.Data.SqlClient.SqlCommand. Para los procedimientos almacenados se pueden utilizar los miembros "DeriveParameters" que llena una colección de "Parámetros", por lo que sólo el valor de cada elemento puede ser modificado.

¿Cómo puede hacerse esto?

¿Fue útil?

Solución

Si desea prohibir las operaciones de modificación en tiempo de compilación, se necesita una solución de tipo seguro.

Declarar una interfaz para las operaciones permitidas públicamente. Usar esa interfaz como tipo de propiedad.

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

    int Count { get; }
}

A continuación, declarar una clase que implementa la interfaz y hereda de la clase de colección estándar.

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

Suponiendo que obtiene la derecha definición de interfaz, no será necesario para poner en práctica cualquier cosa a mano, como la clase base ya proporciona las implementaciones.

El uso que deriva clase como el tipo de campo que almacena el valor de la propiedad.

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

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

Dentro de la clase A, puede utilizar _list directamente, y así modificar el contenido. Los clientes de la clase A sólo podrán utilizar el subconjunto de operaciones disponibles a través de IReadOnlyList<T>.

Para su ejemplo, que está utilizando SortedList lugar de la lista, por lo que la interfaz probablemente tiene que ser

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

Lo he hecho heredar IEnumerable, así, que es de sólo lectura de todos modos, por lo que es perfectamente seguro. La clase de seguro sería entonces:

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

Pero por lo demás es la misma idea.

Actualización: acabo de notar que (por alguna razón no puedo comprender) que no desea prohibir las operaciones de modificación - lo que desea es prohibir algunas operaciones de modificación. Muy extraño, pero sigue siendo la misma solución. Sea cual sea las operaciones que desea permitir, "abrirlos" en la interfaz:

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

Por supuesto, ese es el nombre de la interfaz ahora ... ¿por qué le quieren prohibir la adición mediante la opción Agregar pero no prohibirlo a través del controlador paso a paso? (El controlador paso a paso se puede utilizar para agregar elementos, al igual que el método Add lata.)

Actualizar

Desde su comentario Creo que significa que desea permitir la asignación al valor de un par clave / valor existente, pero no permitir la asignación de una clave previamente desconocido. Obviamente como claves se especifican en tiempo de ejecución por las cuerdas, no hay manera de coger que en tiempo de compilación. Así que es posible que también vaya para la comprobación en tiempo de ejecución:

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
}

Cada vez que tienes un diccionario ordinario y desea que darlo a algún método que no confía para actualizar los valores existentes, lo envuelve en una de ellas.

Otros consejos

EDIT: Respuesta original está por debajo. Como Earwicker señala, no me había dado cuenta de que ¿verdad pidiendo que sea de sólo lectura - sólo para prevenir la operación Add. Eso no suena como una buena idea para mí, ya que la única diferencia entre Add y el indexador sets es que Add lanza una excepción si el elemento ya está presente. Eso podría ser fácilmente fingido por la persona que llama de todos modos.

¿Por qué quiere restringir sólo esa operación?


Respuesta original

Por un lado, no utilice campos públicos. Esa es una manera segura de tener problemas.

Parece que usted quiere una clase contenedora de sólo lectura alrededor de un IDictionary arbitraria. A continuación, puede tener una propiedad pública que devuelve la envoltura, mientras accede a la variable privada desde dentro de su clase. Por ejemplo:

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

Ahora sólo tienes que encontrar una aplicación ReadOnlyDictionary ... No puedo ponerlo en práctica en este momento, pero voy a estar de nuevo más tarde si es necesario ...

Al igual que la lista es privada, y exponerlo como un 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;
      }
   }

}

Ahora sólo se puede acceder a los elementos utilizando el índice:

a["KeyA"] = "ValueBBB";

Sin embargo, como el indexador de la lista permite la creación de nuevos artículos, usted tendría que agregar el código en el indexador para evitar que si usted no quiere que lo hacen posible.

Si se conocen las llaves fuera de la clase a continuación, se puede añadir un ChangeItem (clave, nuevoValor) y ReadItem (clave) a su clase de contenedor. A continuación, mantener SortedList privado a la clase.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top