2009-09-09 13 views
6

Entiendo cómo la palabra clave "nueva" puede ocultar métodos en una clase derivada. Sin embargo, ¿qué implicaciones tiene para las clases que implementan interfaces que usan la palabra clave?¿Se puede usar la nueva palabra clave C# para expandir las propiedades en una interfaz?

Considere este ejemplo, donde decido expandir una interfaz haciendo sus propiedades de lectura/escritura.

public interface IReadOnly { 

    string Id { 
     get; 
    } 
} 

public interface ICanReadAndWrite : IReadOnly { 

    new string Id { 
     get; 
     set; 
    } 
} 

entonces usted es capaz de hacer cosas como esta:

public IReadOnly SomeMethod() { 
    // return an instance of ICanReadAndWrite 
} 

Es este mal diseño? ¿Causará problemas para mis clases implementar ICanReadAndWrite?

Editar: Aquí está un ejemplo artificioso de por qué yo podría querer hacer algo como esto:

Decir que tengo una clase de fábrica que devuelve un IShoppingCartItemReadWrite. Luego puedo tener una capa de servicio que manipula precios, cambia cosas, etc. Entonces, puedo pasar estos objetos como IShoppingCartItemReadOnly a algún tipo de capa de presentación que no los cambie. (Sí, lo sé técnicamente can cambiarlos-- esta es una pregunta de diseño, no de seguridad, etc.)

+0

Su ejemplo específico no tiene mucho sentido. Si puedes leer y escribir, entonces no es de solo lectura. –

+0

Se puede cambiar un IShoppingCartItemReadWrite, pero si tengo una referencia a IShoppingCartItemReadOnly, es de solo lectura a menos que lo lances a otra cosa. – user10789

+0

Tenga en cuenta que en realidad no necesita la palabra clave "nueva". –

Respuesta

19

No es una idea particularmente mala. Debe tener en cuenta que el implementador puede (si implícitamente implementa la interfaz, entonces una sola lectura/escritura de propiedad podría satisfacer ambas interfaces) proporcionar dos implementaciones distintas:

class Test : ICanReadAndWrite { 
    public string Id { 
     get { return "100"; } 
     set { } 
    } 
    string IReadOnly.Id { 
     get { return "10"; } 
    } 
} 

Test t = new Test(); 
Console.WriteLine(t.Id); // prints 100 
Console.WriteLine(((IReadOnly)t).Id); // prints 10 

Por cierto, en general, la new El modificador de herencia no hace nada excepto decirle al compilador que se calle y no arroje una advertencia de "usted está ocultando ese miembro". Omitirlo no tendrá ningún efecto en el código compilado.

+1

¡Buen punto para silenciar la advertencia del compilador! Sin embargo, se queja porque hacer algo como esto para las definiciones de clase puede hacer que se comporten de una manera que podría no esperar. Para este caso particular, no puedo pensar en ningún comportamiento involuntario ... de ahí la pregunta. – user10789

+1

Sí. Por supuesto, la advertencia es por una razón. El único posible inconveniente es, como dije en la respuesta, el hecho de que se trata de dos ranuras de método distintas que pueden resolverse en diferentes implementaciones. Esta es la trampa de usar 'new'. Para 'interfaces' no es dañino la mayor parte del tiempo ya que no proporcionan ninguna implementación y el que llama espera una implementación arbitraria del método de todos modos. –

+1

la nueva palabra clave no es para silenciar el compilador, es para indicar claramente que "no quiero anular ningún miembro, mi intención es ocultarlo, y soy consciente del hecho". La advertencia es evitar que "oculte a un miembro sin intención". –

0

No estoy seguro de si compila o no, pero no es un patrón aconsejable. Con la capacidad de realizar una implementación de interfaz explícita, en teoría podría proporcionar dos implementaciones completamente diferentes para el IReadOnly y el modelo ICanReadAndWrite de la propiedad Id. Considere la posibilidad de modificar la interfaz ICanReadAndWrite agregando un método setter para la propiedad en lugar de reemplazar la propiedad.

+0

Definitivamente compila. Si NO agrega la nueva palabra clave, obtendrá la siguiente advertencia de compilación: 'ICanReadAndWrite.Id' oculta el miembro heredado 'IReadOnly.Id'. Usa la nueva palabra clave si te intentabas esconder. – user10789

+0

En cuanto a la implementación explícita de la interfaz, supongo que si lo hace, dos implementaciones completamente diferentes de la propiedad Id es exactamente lo que usted desearía. Estoy más interesado en los inconvenientes ocultos o problemas menos que obvios que pueden ser causados ​​por este enfoque. – user10789

+1

Lamentablemente, simplemente agregar un colocador a la propiedad no funcionará. Si el colocador sombrea el captador, la lectura de la propiedad requiere un encasillado al tipo con el captador no sombreado. Si el colocador no sombrea el captador, es decir, un eliminador y un colocador independientes están dentro del alcance, ninguno será utilizable dentro de C# o vb.net. – supercat

1

Esto es perfectamente legal y las implicaciones para su clase que implementa la interfaz ICanReadAndWrite sería simplemente que cuando se trata como IReadOnly solo puede leer, pero cuando se trata como ICanReadAndWrite podría hacer ambas cosas.

0

Puede hacerlo, pero no estoy seguro de lo que espera lograr al hacerlo.

public IReadOnly SomeMethod() { 
    // return an instance of ICanReadAndWrite 
} 

Este método devolverá una referencia a un IReadOnly lo que significa que no importa que haya devuelto un ICanReadAndWrite. ¿No sería este enfoque mejor?

public interface IReadOnly 
{ 
    String GetId(); 
} 

public interface ICanReadAndWrite : IReadOnly 
{ 
    String SetId(); 
} 
+0

Me gustaría usar una propiedad simple. – user10789

7

Usted no debe poner en práctica el ICanReadWrite basado en IReadOnly, pero en lugar de hacer los separan.

es decir.de esta manera:

public interface IReadOnly 
{ 
    string Id 
    { 
     get; 
    } 
} 

public interface ICanReadAndWrite 
{ 
    string Id 
    { 
     get; 
     set; 
    } 
} 

Aquí está una clase de usarlos:

public class SomeObject : IReadOnly, ICanReadWrite 
{ 
    public string Id 
    { 
     get; 
     set; 
    } 
} 

Tenga en cuenta que la misma propiedad en la clase puede soportar ambas interfaces.

Tenga en cuenta que, según el comentario, la única forma de obtener una solución robusta sería tener también un objeto contenedor.

En otras palabras, esto no es bueno:

public class SomeObject : IReadOnly, ICanReadWrite 
{ 
    public string Id 
    { 
     get; 
     set; 
    } 

    public IReadOnly AsReadOnly() 
    { 
     return this; 
    } 
} 

como la persona que llama simplemente puede hacer esto:

ICanReadWrite rw = obj.AsReadOnly() as ICanReadWrite; 
rw.Id = "123"; 

para conseguir una solución robusta, necesita un objeto envoltorio, como este:

public class SomeObject : IReadOnly, ICanReadWrite 
{ 
    public string Id 
    { 
     get; 
     set; 
    } 

    public IReadOnly AsReadOnly() 
    { 
     return new ReadOnly(this); 
    } 
} 

public class ReadOnly : IReadOnly 
{ 
    private IReadOnly _WrappedObject; 

    public ReadOnly(IReadOnly wrappedObject) 
    { 
     _WrappedObject = wrappedObject; 
    } 

    public string Id 
    { 
     get { return _WrappedObject.Id; } 
    } 
} 

Esto funcionará, y será robusto, hasta el punto donde la persona que llama utiliza la reflexión.

+1

Pero luego no puedes convertir desde un ICanReadAndWrite a un IReadOnly.Esto es muy útil cuando tiene una clase que está trabajando con objetos de lectura/escritura, y luego solo desea devolver un objeto de solo lectura al consumidor. – user10789

+0

Sin embargo, el consumidor puede rechazarlo. Esto no es robusto. – recursive

+1

Por supuesto, no es robusto, pero tampoco fue la solución original, si hubiera funcionado. La única forma de deshacerse de la capacidad de escritura es devolver un objeto de envoltura que explícitamente lo impida, es decir. solo implementa IReadOnly. –

Cuestiones relacionadas