¿Cómo puede un tipo acceder a un creador privado de la propiedad de otro tipo?

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

  •  04-07-2019
  •  | 
  •  

Pregunta

Todo lo que necesito es una manera de hacer que una propiedad de una clase solo sea "configurable" desde otra clase (una especie de clase de administrador).

¿Es esto posible en C#?

Mi colega me informa "de manera confiable" que tengo un defecto de diseño, pero creo que al menos debería preguntarle a la comunidad antes de admitir la derrota.

¿Fue útil?

Solución

No, no es realmente posible hacer esto de manera limpia en C #. Probablemente tenga un defecto de diseño ;-)

Otros consejos

Puede usar el modificador internal, que permite que todos los tipos en el mismo ensamblado accedan a los datos (o ensamblados nominados si usa [InternalsVisibleTo] - pero no: no hay un equivalente friend en C #.

Por ejemplo:

public string Foo {get; internal set;}

Tienes un defecto de diseño. Además, no seas paranoico sobre la ocultación de datos. Aquí está la forma de hacerlo de 3.5:

class Program
    {
        static void Main(string[] args)
        {
            Managed m = new Managed();
            Console.WriteLine(m.PrivateSetter);
            m.Mgr.SetProperty("lol");
            Console.WriteLine(m.PrivateSetter);
            Console.Read();
        }
    }

    public class Managed
    {
        private Manager _mgr;
        public Manager Mgr
        {
            get { return _mgr ?? (_mgr = new Manager(s => PrivateSetter = s)); }
        }
        public string PrivateSetter { get; private set; }
        public Managed()
        {
            PrivateSetter = "Unset";
        }
    }

    public class Manager
    {
        private Action<string> _setPrivateProperty;
        public Manager(Action<string> setter)
        {
            _setPrivateProperty = setter;
        }
        public void SetProperty(string value)
        {
            _setPrivateProperty(value);
        }
    }

Así es como lo haríamos en días anteriores a la lambda:

public class Managed
{
    private Manager _mgr;
    public Manager Mgr
    {
        get { return _mgr ?? (_mgr = new Manager(this)); }
    }
    public string PrivateSetter { get; private set; }
    public Managed()
    {
        PrivateSetter = "Unset";
    }
    public class Manager
    {
        public void SetProperty(string value)
        {
            m.PrivateSetter = value;
        }
        private Managed m;
        public Manager(Managed man)
        {
            m = man;
        }
    }
}

La mejor manera de hacerlo sería:

/// <summary>
/// Gets or sets foo
/// <b>Setter should only be invoked by SomeClass</b>
/// </summary>    
public Object Foo
{
    get { return foo; }
    set { foo = value; }
}

Cuando tiene un acceso complejo o una restricción de herencia, y su aplicación exige demasiada complejidad en el código, a veces la mejor manera de hacerlo es simplemente comentarlo correctamente.

Sin embargo, tenga en cuenta que no puede confiar en esto si esta restricción tiene algunas implicaciones de seguridad, ya que depende de la buena voluntad del desarrollador que utilizará este código.

No puede hacerlo de esa manera, pero puede acceder al método de establecimiento de una propiedad desde una clase derivada, por lo que puede usar la herencia para ese propósito. Todo lo que tiene que hacer es colocar un modificador de acceso protegido . Si intentas hacerlo, tu colega tiene razón :). Puedes intentar hacerlo así:

public string Name
{
    get{ return _name; }
    protected set { _name = value; }
}

tenga en cuenta que solo se puede acceder al método set de la propiedad desde la clase derivada.

O podría tener estas dos clases en un ensamblaje solo y tener el setter como interno. Sin embargo, votaría por el defecto de diseño, a menos que la respuesta anterior de milot (heredada y protegida) tenga sentido.

Podrías hacer:

public void setMyProperty(int value, Object caller)
{
    if(caller is MyManagerClass)
    {
        MyProperty = value;
    }
}

Esto significaría que podría usar un puntero 'this' de la clase de llamada. Yo cuestionaría la lógica de lo que está intentando lograr, pero sin conocer el escenario, no puedo aconsejar más. Lo que diré es esto: si es posible refactorizar su código para hacerlo más claro, entonces a menudo vale la pena hacerlo.

Pero esto es bastante desordenado y ciertamente NO es infalible ... ¡has sido advertido!

Alternativamente ...

Puede pasar un delegado de la Clase con la Propiedad (Clase A) a la Clase Gerente (Clase B). El delegado puede referirse a una función privada dentro de A para permitir que B llame a ese delegado como cualquier función normal. Esto impide que A sepa de B y potencialmente que A se haya creado antes que B. De nuevo ... ¡desordenado y no infalible!

Puede lograr esto haciendo una propiedad pública en su " clase configurable " eso heredará de la clase real que tendrá una propiedad protegida ... de esta manera solo la clase heredada puede SET y no la clase que no hereda. Pero el inconveniente es que necesitará tener una clase de herencia ...

Reflexión, aunque estaría de acuerdo en que tener que hacer esto solo para evitar un modificador de acceso es probablemente una indicación de un mal diseño.

public class Widget
{
   private int count;
   public int Count
   {
      get { return this.count; }
      private set { this.count = value; }
   }
}

public static class WidgetManager
{
    public static void CatastrophicErrorResetWidgetCount( Widget widget )
    {
       Type type = widget.GetType();
       PropertyInfo info = type.GetProperty("Count",BindingFlags.Instance|BindingFlags.NonPublic);
       info.SetValue(widget,0,null);
    }
}

La razón por la que se trata de una falla de diseño es porque parece confusa entre el alcance de los dos objetos.

Las propiedades de una clase deben ser accesibles en el contexto de esa clase, al menos internamente.

Parece que la propiedad configurable en su clase de elemento es realmente una propiedad de la clase de administrador.

Puede hacer algo similar a lo que desea al acoplar estrechamente las dos clases:

public class MyItem {

    internal MyItemManager manager { get;set; }

    public string Property1 { 
        get { return manager.GetPropertyForItem( this ); } 
    }
}

Desafortunadamente, este tampoco es un gran diseño.

Lo que buscas es lo que C ++ llama una clase Friend pero ni c # ni vb tienen esta funcionalidad. Existe mucho debate sobre el mérito de dicha funcionalidad, ya que casi fomenta un acoplamiento muy fuerte entre las clases. La única forma en que podría implementar esto en C # sería con la reflexión.

Si tu objetivo es tener una clase Foo dejar alguna propiedad (p. ej. Bar, de tipo Biz) para ser cambiado por algún otro objeto, sin exponerlo públicamente, una forma sencilla de hacerlo es tener una instancia de Foo que se supone que es modificable por algún otro objeto para pasar ese otro objeto un Action<Biz> que apunta a un método privado que cambia Bar al valor pasado.El otro objeto puede usar ese delegado para cambiar el Bar valor del objeto que lo suministró.

Si uno desea dar todas las instancias de algún tipo Woozle la capacidad de establecer el Bar valor de cualquier instancia de Foo, en lugar de exponer tales habilidades caso por caso, se puede requerir que Woozle tener un método estático público Woozle.InstallFooBarSetter que toma un parámetro de tipo Action<Foo, Biz> y uno de tipo Object. Foo entonces debería tener un método estático WoozleRequestBarSetter que toma un Object, y se lo pasa a Woozle.InstallFooBarSetter junto con un Action<Foo,Biz>.El inicializador de clase para Woozle debería generar un nuevo Object, y páselo a Foo.RequestBarSetter;que pasará el objeto a Woozle.InstallFooBarSetter junto con un delegado. Woozle Luego puede confirmar que el objeto pasado es el que generó y, si es así, instalar el delegado apropiado.Hacer las cosas de esta manera garantizará que nadie excepto Woozle puede obtener el delegado (ya que el delegado solo se pasa a Woozle.InstallFooBarSetter), y Woozle puede estar seguro de que su delegado proviene de Foo (ya que nadie más tendría acceso al objeto que Woozle creado, y Woozle.InstallFooBarSetter no hará nada sin él).

si es un defecto de diseño depende de lo que quieras hacer. Puede usar la clase StackTrace de System.Diagnostics para obtener el Tipo de clase que configura su propiedad y luego compararla con el tipo que desea permitir establecer su propiedad ... pero tal vez haya mejores formas de realizar algo como esto (por ejemplo, el boxeo)

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