2010-11-11 21 views
9

I tienen una situación como esta:Adición de un setter a una propiedad virtual en C#

public abstract class BaseClass 
{ 
    public abstract string MyProp { get; } 
} 

Ahora, para algunas de las clases derivadas, el valor de las propiedades es un valores sintetizados, así que no hay setter:

public class Derived1 : BaseClass 
{ 
    public override string MyProp { get { return "no backing store"; } } 
} 

Esto funciona bien. Sin embargo, parte de la clase derivada requería una tienda de respaldo más tradicional. Sin embargo, no importa cómo lo escribo, como en la propiedad automática, o con un almacén de respaldo explícito, me sale un error:

public class Derived2 : BaseClass 
{ 
    public override string MyProp { get; private set;} 
} 

public class Derived3 : BaseClass 
{ 
    private string myProp; 
    public override string MyProp 
    { 
     get { return myProp;} 
     private set { myProp = value;} 
    } 
} 

Derived2.MyProp.set ': no ​​se puede anular porque 'BaseClass.MyProp' hace no tiene un acceso de conjunto sobresaliente

¿Cómo hago para que funcione?

+1

Funcionaría si BaseClass fuera una interfaz en lugar de una clase abstracta, lo que sería adecuado para este ejemplo artificial, pero quizás no en su código real. – Mud

+0

Cierto, BaseClass necesita ser una clase. Ha implementado métodos, que no mostré. –

+0

Posible duplicado de [¿Por qué es imposible anular una propiedad getter y agregar un setter?] (Https://stackoverflow.com/questions/82437/why-is-it-impossible-to-override-a-getter -only-property-and-add-a-setter) –

Respuesta

10

Lo mejor que puede hacer es implementar la propiedad como virtual en lugar de abstract. Hacer las get y set bloques por cada lanzamiento NotSupportedException en la clase base y reemplazar el comportamiento en consecuencia en las clases derivadas:

public virtual string MyProp { 
    get { 
     throw new NotSupportedException(); 
    } 
    set { 
     throw new NotSupportedException(); 
    } 
} 
+2

Lo que esto no le proporciona es un mensaje de tiempo de compilación si olvida implementarlo en la clase derivada. Puede implementar get y set en el resumen, y throw no implementado explícitamente en la clase derivada para mostrar que ha sido considerado. – Chris

3

Básicamente, no se puede. Al agregar un setter, está cambiando la definición de la propiedad, por lo que realmente no "anula" la propiedad base. Es lo mismo que tratar de anular un método y agregarle otro parámetro; se tratarían como métodos diferentes (sobrecargados). Como las propiedades no pueden sobrecargarse, esto no funcionará.

Simplemente tendrá que agregar otro método para establecer el valor (quizás con protected accesibilidad).

+1

No creo que sea análogo. Es más como si 'BaseClass' declarase un método abstracto' GetFoo() 'y la clase derivada añadiera un método' SetFoo() '- lo extiende, pero no lo altera de tal manera que viola el LSP (a diferencia del cambio firmas de métodos). –

+0

Estoy de acuerdo con usted en teoría, pero la forma en que se ha implementado en .NET es que * es * un cambio, porque la propiedad define qué accesores tiene, separa las definiciones de los métodos get_ y set_ que implementan esos accesadores. – EMP

+1

@Ian: No estoy de acuerdo. La abstracción que las propiedades nos dan es obtener/establecer propiedades de una clase con la sintaxis del acceso de campo. Internamente se implementan como métodos, pero eso es además del punto. Al definir una propiedad, usted está diciendo si puede obtener y/o establecer un valor con accesibilidad variable. Viola el LSP ya que en una clase, una propiedad se define para tener solo un acceso de obtención, luego en una clase derivada, la misma propiedad que otro acceso. –

0

sugerencia de Bradley es buena, pero hay una cosa que he hecho en los casos en que sólo el organismo debe ser virtual es hacer algo que este presente:

public class Root 
{ 
    private string _MyProp; 
    public string MyProp 
    { 
     get { return _MyProp;} 
     set { _MyProp = SetMyProp(value); } 
    } 
    protected virtual string SetMyProp(string suggestedValue) 
    { 
     return suggestedValue; 
    } 
} 
public class Child 
    : Root 
{ 
    protected override string SetMyProp(string suggestedValue) 
    { 
     string oReturn = base.SetMyProp(suggestedValue); 
     // Do some sort of cleanup here? 
     return oReturn; 
    } 
} 

se requiere un poco de trabajo extra en la delantera, pero parece mantener un mayor grado de encapsulación (por ejemplo, se puede evitar que las subclases sustituyan el comportamiento Getter, y su subclase doesn tiene que ser consciente del miembro subyacente detrás de th propiedad e).

+0

Aunque estoy de acuerdo en que esta es una práctica mejor en general, la pregunta de James habla específicamente sobre propiedades que pueden no tener un miembro subyacente. Proporcionar uno en la clase base es, en su escenario, un desperdicio ya que no todas las clases derivadas lo requerirán. –

+0

Tienes razón. Volví a leer su pregunta, y creo que entendí mal las cosas en la primera vuelta. Estaba pensando que solo me estaba preguntando cómo tener un colocador virtual ... Me perdí la parte sobre no necesariamente tener un colocador expuesto en absoluto. – Steven

0

Sugeriría evitar las propiedades virtuales o abstractas. En su lugar, use una propiedad no virtual que encadena a métodos de obtención/configuración virtuales o abstractos protegidos. Hacer eso permitirá que las clases derivadas anulen los métodos y también sombreen la propiedad con una que tenga diferentes modificadores de acceso. Dado que la propiedad base en sí misma no será virtual, nunca habrá necesidad de anularla, por lo que el conflicto de nomenclatura con la nueva versión no tendrá importancia.

Cuestiones relacionadas