2008-12-15 15 views
27

tengo una lista de objetos implementando una interfaz, y una lista de esa interfaz:miembro interno de una interfaz de

public interface IAM 
{ 
    int ID { get; set; } 
    void Save(); 
} 

public class concreteIAM : IAM 
{ 
    public int ID { get; set; } 
    internal void Save(){ 
    //save the object 
    } 

    //other staff for this particular class 
} 

public class MyList : List<IAM> 
{ 
    public void Save() 
    { 
     foreach (IAM iam in this) 
     { 
      iam.Save(); 
     } 
    } 

    //other staff for this particular class 
} 

El código anterior no se compila porque el compilador requiere que todos los elementos de interfaz que ser público .

internal void Save(){ 

pero no desea permitir que el de fuera de mi DLL para guardar el ConcreteIAM, sólo sea salvo por el MyList.

¿Alguna forma de hacerlo?

Actualización # 1: Hola a todos, gracias por las respuestas hasta el momento, pero ninguno de ellos es exactamente lo que necesito:

La interfaz tiene que ser público, ya que es la firma del cliente desde fuera del dll usará, junto con ID y otras propiedades que no me molesté en escribir en el ejemplo para mantenerlo simple.

Andrew, no creo que la solución sea crear una fábrica para crear otro objeto que contenga los IAM miembros + Guardar. Todavía estoy pensando ... ¿Alguna otra idea?

+2

Si desea mantener el método Save en la clase concreteAM su mejor opción es probablemente ir con una clase base abstracta con un método interno abstracto Guardar como sugiere Nathan W. –

+0

gracias por las respuestas hasta ahora, pero ninguna de ellos son exactamente lo que necesito: la interfaz debe ser pública porque es la firma que utilizará el cliente que no pertenece al dll, junto con la identificación y otras propiedades que no me molesté en escribir en el ejemplo para mantenerlo simple. Andrew, No creo que la solución sea crear una fábrica para crear otro objeto que contenga los miembros de IAM + Guardar. Todavía estoy pensando ... ¿Alguna otra idea? –

+0

mejor tarde que nunca, pero esto puede ayudar: http://stackoverflow.com/a/18944374/492 –

Respuesta

9

No creo que usted debe utilizar una interfaz de aquí tal vez usted debe utilizar una base algo abstracto como .:

public abstract class AM 
{ 
    public int ID { get; set; } 
    internal abstract void Save(); 
} 

public class concreteIAM : AM 
{ 
    internal override void Save() 
    { 
     //Do some save stuff 
    } 
} 

todavía permitirá que hagas esto:

public class AMList : List<AM> 
{ 
    public void SaveItems() 
    { 
     foreach (var item in this) 
     { 
      item.Save(); 
     } 
    } 
} 
1

Si no desea que los llamadores externos puedan llamar a su método Save(), ¿por qué no hacer que toda la clase concreteIAM sea interna?

O, si desea que la clase sea pública, pero no la interfaz, haga que toda la interfaz sea interna. Creo que se puede agregar una interfaz interna a una clase pública (pero no lo he probado ...)

1

Quizás desee separar el guardado de sus elementos en un conjunto diferente de clases que son internas para su ensamblaje :

internal interface IAMSaver { void Save(IAM item); } 

internal class AMSaverFactory { 
    IAMSaver GetSaver(Type itemType) { ... } 
} 

public class MyList : List<IAM> 
{ 
    public void Save() 
    { 
    foreach (IAM itemin this) 
    { 
     IAMSaver saver = SaverFactory.GetSaver(item.GetType()); 
     saver.Save(item) 
    } 
    } 
} 
1

Se supone que los miembros de la interfaz son públicos ... cualquier otra cosa y usted debería pensar si necesita algo más. En este caso,

  • desea Guardar para ser un miembro de interfaz
  • quieren Guardar para ser sólo permitió a través de otra clase llamada Mi lista que se encuentra en el mismo conjunto
  • no permitir salvarlo de ser llamada externa (de otra clases fuera del ensamblaje primario)

Guardando las ideas de diseño a un lado, puede hacerlo a través de la Implementación de interfaz explícita.

internal interface IPersist 
    { 
     void Save(); 
    } 
    public class Concrete : IPersist 
    { 
     void IPersist.Save() 
     { 
      Console.WriteLine("Yeah!"); 
     } 
    } 

// Mylist.cs in the same assembly can still call save like 
public void SaveItems() 
    { 
     foreach (IPersist item in this) 
     { 
      item.Save(); 
     } 
    } 

IPersist es interno, no disponible fuera del ensamblaje padre y ya que se implementa de manera explícita, que no puede ser llamado sin una referencia IPersist.

new Concrete().Save();  // doesn't compile. 'Concrete' does not contain a definition for 'Save' 

actualización Desde su última respuesta, tenemos más limitaciones. La interfaz debe ser pública, debe contener el método Guardar, que no debe estar a disposición del público. Yo diría que volver a la mesa de dibujo ... algo no parece correcto. En mi humilde opinión, no puede hacer esto con una sola interfaz. ¿Qué tal dividir en 2 interfaces, la pública y la interna?

34

Creo que no entiende para qué es una interfaz. La interfaz es un contrato . Especifica que un objeto se comporta de cierta manera. Si un objeto implementa una interfaz, significa que puede confiar en que tiene implementados todos los métodos de la interfaz.

Ahora, considere qué pasaría si hubiera una interfaz como la que está pidiendo, pública, pero con un miembro interno. ¿Qué significa eso? Un objeto externo podría implementar solo los métodos públicos, pero no el interno. Cuando obtendría un objeto externo de este tipo, podría llamar solo a los métodos públicos, pero no a los internos, porque el objeto no podría implementarlo. En otras palabras, el contrato no se cumpliría. No todos los métodos se implementarán.

Creo que la solución en este caso es dividir la interfaz en dos. Una interfaz sería pública, y eso es lo que implementarían sus objetos externos. La otra interfaz sería interna y contendría su Save() y otros métodos internos. Quizás esta segunda interfaz podría incluso heredarse de la primera. Sus propios objetos internos implementarían ambas interfaces. De esta forma, incluso podría distinguir entre objetos externos (los que no tienen la interfaz interna) y objetos internos.

+0

¡Uf! Alguien realmente retrocediendo y preguntando por qué, en lugar de intentar codificar en círculos ... –

+3

Escenario simple: un ensamblado tiene clases Thing1 y Thing2 que, por diversas razones, no pueden compartir de manera útil un tipo base, pero comparten una interfaz IThing. Desea dar objetos de código externo que puedan transmitirse al código que necesita un IThing, pero quiere garantizar que cualquier IThing que reciba realmente implementará el contrato IThing correctamente, prohibiendo cualquier implementación fuera del ensamblado. Uno podría lograr un efecto similar mediante el uso de una clase contenedora o struct, pero el código sería más claro si uno no tuviera que hacer eso. – supercat

+0

Me temo que no puedo pensar en una forma de lograr esto en C#. Sin embargo, hay muchas cosas que no se pueden lograr en C# (o en cualquier otro idioma, para el caso). Es por eso que existen contratos de código. –

0

Ir con dos interfaces:

public interface IAM 
{ 
     int ID { get; set; } 
} 


internal interface IAMSavable 
{ 
     void Save(); 
} 

public class concreteIAM : IAM, IAMSavable 
{ 
     public int ID{get;set;} 
     public void IAMSavable.Save(){ 
     //save the object 
     } 

     //other staff for this particular class 
} 

public class MyList : List<IAM> 
{ 
     public void Save() 
     { 
       foreach (IAM iam in this) 
       { 
         ((IAMSavable)iam).Save(); 
       } 
     } 

     //other staff for this particular class 
} 

La aplicación de Save() debe ser explícita a evitar que los clientes llamándolo.

1

¿Por qué no utiliza clases internas para controlar el acceso a sus métodos?

Ejemplo:

Su ensamblaje principal

public abstract class Item 
{ 
    public int ID { get; set; } 
    protected abstract void Save(); 

    public class ItemCollection : List<Item> 
    { 
     public void Save() 
     { 
      foreach (Item item in this) item.Save(); 
     } 
    } 
} 

Su montaje secundaria

public sealed class NiceItem : Item 
{ 
    protected override void Save() 
    { 
     // do something 
    } 
} 

Este patrón se sigue permitirle implementar Save() método en otras asambleas pero sólo ItemCollection cuales es interior c muchacha de Item puede llamarlo. Brillante, ¿no?

20

Cree otra interfaz que sea interna y use la implementación explícita para el método.

internal interface InternalIAM 
{ 
    void Save(); 
} 

public class concreteIAM : InternalIAM 
{ 
    void InternalIAM.Save() 
    { 
    } 
} 
+1

Gracias, siempre he querido saber cómo hacer que una clase pública implemente una interfaz interna. – yoyo

+0

vea también http://stackoverflow.com/a/18944374/492 –

+0

Un problema con esto es que no puede tener métodos públicos que usen 'InternalIAM' como tipo de parámetro. –

4

Creo que la mejor cosa a hacer sería romper los miembros internos y públicos en dos interfaces separadas. Si hereda las interfaces, puede declarar públicamente a los miembros, pero la visibilidad de los miembros internos dependerá de la visibilidad de la interfaz.

using System; 

public interface IPublic 
{ 
    void Public(); 
} 

internal interface IInternal : IPublic 
{ 
    void Internal(); 
} 

public class Concrete : IInternal 
{ 
    public void Internal() { } 

    public void Public() { } 
} 
+0

la visibilidad de los miembros está dictada por la implementación del miembro. Si instancia un objeto concreto, puede llamar a su método público. –

+0

que es un buen punto - Imaginé que algún tipo de método de fábrica que devolvería Concrete como IPublic ocultando así todos los demás miembros –

-2

Me preguntaba sobre el mismo tema aquí, y tropezamos con esta pregunta ...

como pensaba en esto, he entendido yo no necesito el método internoen la interfaz en primer lugar.

Puedo acceder a ella a través de mi Concrete Class, y abandonar el contrato para el código externo.

En su ejemplo:

public interface IAM 
    { 
      int ID { get; set; } 
    } 

    public class concreteIAM : IAM 
    { 
      public int ID{get;set;} 
      internal void Save(){ 
      //save the object 
      } 

      //other staff for this particular class 
    } 

    public class MyList : List<IAM> 
    { 
      public void Save() 
      { 
        foreach (concreteIAM iam in this) 
        { 
          iam.Save(); 
        } 
      } 
      //other staff for this particular class 
    } 
+0

-1, da como resultado un error de complicación.'concreteIAM 'no implementa el miembro de la interfaz' IAM.Save() ' –

+0

Cada vez que alguien tiene que decir "espero que esto ayude", generalmente no es así. Como aquí. – nathanchere

+0

según la demanda popular, solucionó el problema ... la interfaz nunca debería haber definido Guardar en primer lugar –

3

Es exactamente lo que estoy hablando en mi artículo Friends and internal interface members at no cost with coding to interfaces.

La esencia del artículo de

public class Internal 
{ 
    internal Internal() { } 
} 

public interface IAM 
{ 
    int ID { get; set; } 
    void Save(Internal access); 
} 

A partir de ahora solo su conjunto puede llamar al método Save(), ya que una instancia de la clase Internal sólo puede ser creado por su montaje.

Cuestiones relacionadas