.NET - cómo hacer una clase tal que sólo una otra clase específica puede crear instancias de ella?

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

Pregunta

Me gustaría tener la siguiente configuración:

class Descriptor
{
    public string Name { get; private set; }
    public IList<Parameter> Parameters { get; private set; } // Set to ReadOnlyCollection

    private Descrtiptor() { }
    public Descriptor GetByName(string Name) { // Magic here, caching, loading, parsing, etc. }
}

class Parameter
{
    public string Name { get; private set; }
    public string Valuie { get; private set; }
}

Una vez cargado desde un archivo XML Toda la estructura será de sólo lectura. Me gustaría que así sea, que sólo la clase de descriptor puede crear una instancia de un parámetro.

Una forma de hacer esto sería hacer una interfaz IParameter y luego hacer la clase Parameter privado en la clase de descriptor, pero en el uso del mundo real del parámetro tendrá varias propiedades, y me gustaría evitar la redefinición de ellos dos veces.

¿Es esto de alguna manera posible?

¿Fue útil?

Solución

Que sea una clase anidada privada que implementa una interfaz en particular. Entonces, sólo la clase exterior puede crear instancias de ella, pero cualquier persona puede consumir (a través de la interfaz). Ejemplo:

interface IParameter
{ 
    string Name { get; } 
    string Value { get; }
}

class Descriptor
{
    public string Name { get; private set; }
    public IList<IParameter> Parameters { get; private set; }

    private Descriptor() { }
    public Descriptor GetByName(string Name) { ... }

    class Parameter : IParameter
    {
        public string Name { get; private set; }
        public string Value { get; private set; }
    }
}

Si realmente debe evitar la interfaz, se puede crear una clase abstracta pública que tiene todas las propiedades, pero declara un constructor protegido. A continuación, puede crear una clase anidada privada que hereda a partir del resumen público que sólo puede ser creada por la clase externa y los casos de regreso si lo que el tipo base. Ejemplo:

public abstract AbstractParameter
{ 
    public string Name { get; protected set; } 
    public string Value { get; protected set; }
}

class Descriptor
{
    public string Name { get; private set; }
    public IList<AbstractParameter> Parameters { get; private set; }

    private Descriptor() { }
    public Descriptor GetByName(string Name) { ... }

    private class NestedParameter : AbstractParameter
    {
        public NestedParameter() { /* whatever goes here */ }
    }
}

Otros consejos

LBushkin tiene la idea correcta. Si se quiere evitar tener que volver a escribir todas las propiedades sólo haga clic en el nombre de la clase y elegir la opción "Refactor"> "Extraer Interface", que debería darle una interfaz que contiene todas esas propiedades. (Esto funciona en VS 2008, no sé acerca de las versiones anteriores.)

C # generalmente adopta el enfoque de que en vez de evitar código redundante, VS simplemente le ayudará a escribir más rápido.

Se puede usar un constructor de marcado interno.

De esta manera es pública a clases en el montaje y privado a clases fuera de ella.

Marcar la clase para ser "protegido" de instanciación (parámetro) con el rel StrongNameIdentityPermission atributo y la opción SecurityAction.LinkDemand :

[StrongNameIdentityPermission(SecurityAction.LinkDemand, PublicKey="...")]
class Parameter
{
    ...
}

Usted tendrá que proporcionar la clave pública apropiada. Debido a que está exigiendo un tiempo de enlace (JIT-tiempo, de hecho) comprobar el Parameterclass, esto significa que sólo se puede utilizar desde un ensamblado que se firmó con un nombre seguro que utiliza la clave privada correspondiente la clave pública que se suministro en el constructor atributo anteriormente. Por supuesto, usted tendrá que poner la clase Descriptor en un ensamblado independiente y darle un nombre seguro en consecuencia.

He utilizado esta técnica en un par de aplicaciones y ha funcionado muy bien.

Espero que esto ayude.

Hay otra manera: comprobar la pila de llamadas para el tipo de llamada

.

Si desea que sólo la clase de descriptores de crear una instancia de un parámetro, entonces usted puede hacer la clase de descriptores de una clase anidada de parámetro. (NO al revés) Esto es contrario a la intuición como la clase de contenedor o padre es la clase anidada.

public class Parameter  
{  
  private Parameter() { }
  public string Name { get; private set; }
  public string Value { get; private set; }
  public static Parameter.Descriptor GetDescriptorByName(string Name)
  {
    return Parameter.Descriptor.GetByName(Name);
  }
  public class Descriptor
  { // Only class with access to private Parameter constructor
    private Descriptor() { // Initialize Parameters }
    public IList<Parameter> Parameters { get; private set; } // Set to ReadOnlyCollection
    public string Name { get; private set; }
    public static Descriptor GetByName(string Name) { // Magic here, caching, loading, parsing, etc. }
  }  
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top