2010-01-06 8 views
8

Hasta donde yo sé, en C#, no hay soporte para la palabra clave "amigo" como en C++. ¿Hay alguna manera alternativa de diseñar una clase que pueda lograr el mismo resultado final sin recurrir a la palabra clave "amigo" no disponible?Cómo crear un constructor que solo sea utilizable por una clase específica. (C++ Amigo equivalente en C#)

Para aquellos que aún no lo sepan, la palabra clave Friend permite al programador especificar que se puede acceder a un miembro de la clase "X" y usarlo solo con la clase "Y". Pero para cualquier otra clase, el miembro parece privado, por lo que no se puede acceder. La clase "Y" no tiene que heredar de la clase "X".

+0

también ... Me pregunto si Microsoft está pensando en poner una característica de este tipo en .Net 4.0 – 7wp

+0

No, Ellos no están. .NET 4 conserva los mismos modificadores de accesibilidad que las versiones anteriores. – itowlson

Respuesta

8

No, no hay forma de hacerlo en C#.

Una solución común es basar el objeto para el que desea ocultar el constructor en una interfaz. A continuación, puede usar el otro objeto para construir una clase privada anidada que implemente esa interfaz y devolverla a través de una fábrica. Esto evita que el mundo exterior construya su objeto directamente, ya que solo ven e interactúan con la interfaz.

public interface IMyObject 
{ 
    void DoSomething(); 
} 

public class MyFriendClass 
{ 
    IMyObject GetObject() { return new MyObject(); } 

    class MyObject : IMyObject 
    { 
      public void DoSomething() { // ... Do something here 
      } 
    } 
} 
+0

Esto realmente parece funcionar bastante bien para mis propósitos. Tengo que comprometerme un poco en la ubicación de los objetos y el espacio de nombre ... pero lo que quiero hacer es lo que quiero hacer, +1 – 7wp

+0

Bueno, la interfaz puede estar en un espacio de nombres diferente, por lo que no debería haber ningún compromisos allí. La ubicación de los objetos (en el código) lamentablemente cambia, siguiendo este patrón. –

+0

puede declarar 'MyFriendClass' como una clase parcial si desea la clase anidada en un archivo separado – Assimilater

1

No. Lo más cerca que lo que tienes es un constructor de internal, o un constructor private y un método de fábrica por separado (probablemente internal, por lo que no ha guardado mucho).

1

Hasta donde yo sé, la palabra clave interna es lo más parecido en .NET. Esta cuestión será arrojar más luz sobre interno: Internal in C#

2

¿Qué hay de sólo tener que explícitamente implementar una interfaz que sólo es visible a cierta clase?

Algo así como:

public void IFreindOfX.Foo() //This is a method in the class that's a 'friend' to class X. 
{ 
    /* Do Stuff */ 
} 

y luego asegurarse de IFriendOfX es visible a la clase X. En su clase X que se podría llamar el método por el primer casting X a IFriendOfX luego llamar Foo(). Otra ventaja es que es bastante auto documentada ... es decir, está muy cerca de tener la palabra clave friend en sí.

+1

Esto solo se aplica a los objetos y no funcionará para los constructores, pero honestamente no puedo ver por qué tendrían que hacer eso a menos que lo que estás diciendo es que solo hay algunos estados en los que el objeto es válido cuando lo usa el amigo. En ese caso, realmente desea obtener una nueva clase 'Amigable' de la clase en cuestión que solo sea visible para la clase 'Amigo'. –

1

Como solución, supongo que podría crear un condicional en su constructor que use la reflexión.

Por ejemplo, si el constructor de Clase 1 debe ser llamado por Clase 2:

public Class1() 
{ 
    string callingClass = new StackFrame(1).GetMethod().DeclaringType.Name; 

    if (callingClass != "Class2") 
    { 
     throw new ApplicationException(
      string.Concat("Class1 constructor can not be called by ", 
      callingClass, ".")); 
    } 
} 

EDIT:

Tenga en cuenta que yo nunca realmente hacer esto en código "real". Técnicamente funciona, pero es bastante desagradable. Solo pensé que era creativo. :)

0

Puede acceder a miembros/métodos privados utilizando Reflection.

Dado que tiene la etiqueta de diseño, nunca me gustó especialmente la palabra clave friend. Perfora la encapsulación y eso siempre me pareció sucio.

0

Esto tiene un poco de olor. Hay muchas otras formas de lograr la implementación oculta en C#. Limitar la construcción a solo clases específicas no logra mucho.

¿Podría proporcionar más información sobre el propósito de este requisito? Como ya se respondió, internal es la coincidencia más cercana para limitar el acceso a la clase.Hay formas de construir sobre eso dependiendo del propósito.

1

Lo único que se me ocurre que incluso se acercaría sería protected internal pero eso no lo restringe a una clase específica. El único amigo que conozco en C# es hacer una asamblea de amigos. Todavía no se restringe a una clase específica.

Lo único que se le ocurrió para tratar de hacerlo sería hacer algo como lo siguiente:

public class A 
{ 
    public A() {} 
    protected internal A(B b) {} 
} 

public class B 
{ 
    A myVersion; 

    public B() 
    { 
     myVersion = A(this); 
    } 
} 

La única manera de que pudiera pensar sería hacer algún tipo de Constructor de inyección utilizando reflexión que se hace dentro de tu clase de amigos. El mecanismo de inyección le permitiría limitarlo a lo que desea, pero podría ser muy engorroso. Eche un vistazo a algo como Spring.Net para algunas capacidades de inyección.

+2

Recuerde que "interno protegido" significa la combinación * más permisiva * de combinación protegida e interna, no la combinación * menos permisiva *. –

1

¿Qué tal crear una clase privada? Esto hace exactamente lo que pareces estar describiendo. Un miembro de la clase X puede ser accedido y usado solamente por la clase Y, y para cualquier otra clase que aparece privada, ya que, así, que es privada:

public class Y 
{ 
    private class X { } 

    private X Friend; 

    public Y() 
    { 
     Friend = new X(); 
    } 
} 
+0

Tenía la esperanza de definir la clase de amigo en un ensamblaje diferente. Sin embargo, ¿es posible decirle a la clase privada que se encuentre en un espacio de nombres diferente? – 7wp

1

Así es como lo resolví. No estoy seguro de si es la manera "correcta" de hacerlo, pero requería un esfuerzo mínimo:

public abstract class X 
{ 
    // "friend" member 
    protected X() 
    { 
    } 

    // a bunch of stuff that I didn't feel like shadowing in an interface 
} 

public class Y 
{ 
    private X _x; 

    public Y() 
    { 
     _x = new ConstructibleX(); 
    } 

    public X GetX() 
    { 
     return _x; 
    } 

    private class ConstructibleX : X 
    { 
     public ConstructibleX() 
      : base() 
     {} 
    } 
} 
Cuestiones relacionadas