2009-11-24 36 views
26

Tengo un conjunto de interfaces que se utilizan en estrecha relación con un objeto mutable particular.C#: interfaz getter/setters de herencia

Muchos usuarios del objeto solo necesitan la capacidad de leer valores del objeto, y solo unas pocas propiedades. Para evitar la contaminación del espacio de nombres (intellisense más fácil) y para conocer el propósito del uso, me gustaría tener una pequeña interfaz base que solo exhiba unas pocas propiedades de "clave" en forma de solo lectura.

Sin embargo, casi todas las implementaciones admitirán la interfaz completa, que incluye modificabilidad.

Por desgracia, me encontré con un control de carretera que expresa ese concepto en C#:

interface IBasicProps { 
    public int Priority { get; } 
    public string Name {get;} 
    //... whatever 
} 

interface IBasicPropsWriteable:IBasicProps { 
    public int Priority { set; } //warning CS0108: [...] hides inherited member [...] 
    public string Name { set; } 
    //... whatever 
} 

Desde luego, no tenía la intención de ocultar los miembros, de manera que el aint bueno!

Por supuesto, puedo resolver esto usando métodos bien, pero ¿cuál es la opción correcta? Me gustaría mantener la interfaz "central" lo más pequeña posible, incluso si la división de las interfaces no tiene otro propósito que comunicar la intención. Con las interfaces divididas, es realmente obvio qué métodos no van a hacer ninguna actualización, y hace que la escritura del código sea un poco más clara (sin mencionar también permite simples y únicos stubs únicos estáticos que son suficientes para bastantes casos simples) .

Me gustaría evitar cualquier clase abstracta y similares; hacen que la reimplementación o calzas de un solo propósito sean más complejas y difíciles de asimilar.

Entonces, ¿ideas?

Respuesta

27

Método de ocultación en una interfaz no es tan sucio; Me gustaría ir con algo como:

interface IBasicProps { 
    int Priority { get; } 
    string Name {get;} 
    //... whatever 
} 

interface IBasicPropsWriteable:IBasicProps { 
    new int Priority { get; set; } 
    new string Name { get; set; } 
    //... whatever 
} 
class Foo : IBasicPropsWriteable { 
    public int Priority {get;set;} 
    public string Name {get;set;} 
/* optional 
    int IBasicProps.Priority {get {return Priority;}} 
    string IBasicProps.Name {get {return Name;}} 
*/ 
} 
+0

Como beneficio adicional, esto también funciona bien con co/contravariancia; Mi caso de uso real es más parecido a 'IBasicProps {/*...*/} IBasicPropsWriteable : IBasicProps {}'. Aquí la división de la interfaz es necesaria; un administrador central desea que la covarianza coloque varios objetos en una colección, pero los propietarios individuales de cada objeto saben qué tipo contiene. –

+1

¿Qué opina sobre omitir el getter en la nueva propiedad en la interfaz derivada, como se sugiere aquí? http: // stackoverflow.com/questions/3516279/interface-inheritance-is-extend-properties-possible # comment3676924_3516450 –

+0

Si bien esta es probablemente la solución menos mala posible en C#, sigue siendo insatisfactorio usar el modificador 'new' porque significa que" hay un propiedad sin relación del mismo nombre, la propiedad antigua todavía está sobreviviendo pero necesita una sintaxis más compleja para acceder a través de 'IBasicPropsWriteable'". Sería bueno si pudiera extender la "misma" propiedad con un accesorio adicional. (El mismo problema existe cuando las clases se heredan entre sí). Con su solución, el código 'static void M (IBasicPropsWriteable x) {var a = x.Name; var b = ((IBasicProps) x) .Name; } '¡lee formalmente dos miembros diferentes! –

4

Si su objetivo es hacerlo más claro cuando se lee contra escritura está permitido, entonces me gustaría utilizar los métodos getter y setter independientes en lugar de propiedades.

interface IBasicProps { 
    int GetPriority(); 
    string GetName(); 
    //... whatever 
} 

interface IBasicPropsWriteable:IBasicProps { 
    void SetPriority(int priority); 
    void SetName(string name); 
    //... whatever 
} 
+3

Este no es un ganador de concurso de belleza, pero parece que es una apuesta bastante segura que funcionará bien ... –

2

Una forma podría ser simplemente omitir la herencia de las interfaces. Hacer una interfaz y de sólo lectura uno de sólo escritura, y en su caso aplique:

interface IBasicPropsReadable { 
    int Priority { get; } 
    string Name { get; } 
} 

interface IBasicPropsWriteable { 
    int Priority { set; } 
    string Name { set; } 
} 

class SomeClassReadWrite : IBasicPropsReadable, IBasicPropsWriteable { 
    int Priority { get; set; } 
    string Name { get; set; } 
} 

class SomeClassReadOnly : IBasicPropsReadable { 
    int Priority { get; } 
    string Name { get; } 
} 
+2

Esto no me permite expresar la noción común de "un objeto de lectura/escritura". ¿Cómo puedo escribir un método que acepte solo variantes de lectura/escritura, ahora? –

1

podría dejar las interfaces sin relación y simplemente tener su clase implemente ambas interfaces. Después de todo, las interfaces simplemente definen el contrato y los contratos no necesitan estar relacionados. Parece que es solo una optimización para usted cuando codifica para que la escriturable se derive de la otra, por lo que solo debe especificar una interfaz.

public interface IBasicProps 
{ 
    int Priority { get; } 
    string Name {get;} 
    //... whatever 
} 

public interface IBasicPropsWriteable 
{ 
    int Priority { get; set; } 
    string Name { get; set; } 
    //... whatever 
} 

public class Foo : IBasicProps, IBasicPropsWriteable 
{ 
    public int Priority { get; set; } 
    public string Name { get; set; } 

    // whatever 
} 

Si realmente necesita la optimización, puede crear otra interfaz que se deriva de los dos y tener sus clases implementan eso.

public interface IBasicPropsAll : IBasicProps, IBasicPropsWriteable { } 

public class Foo : IBasicPropsAll 
{ 
    public int Priority { get; set; } 
    public string Name { get; set; } 

    // whatever 
} 
+1

El enfoque IBasicPropsAll no funciona (muy bien). '((IBasicPropsAll) new Foo()). Name' es ambiguo, y el compilador no lo aceptará. Esto significa que necesita lanzar comúnmente. –

+0

Sí. Solo usaría esto para obtener la "optimización" de no tener que escribir ambas interfaces. No lo estoy recomendando. En realidad, no lo usaría, lo lanzaría a cualquiera de IBasicProps o IBasicPropsWriteable en la práctica. – tvanfosson