Por que é impossível substituir uma propriedade somente getter e adicionar um setter? [fechadas]

141

Por que o seguinte código C # não é permitido:

public abstract class BaseClass
{
    public abstract int Bar { get;}
}

public class ConcreteClass : BaseClass
{
    public override int Bar
    {
        get { return 0; }
        set {}
    }
}

CS0546 'ConcreteClass.Bar.set': não é possível substituir porque 'BaseClass.Bar' não possui um acessador de conjunto substituível

ripper234
fonte
9
O StackOverflow tem capacidade limitada para responder perguntas em nome da Microsoft. Considere reformular sua pergunta.
Jay Bazuzi 18/09/08
1
Aliás, um remédio simples para esse comportamento irritante no .net é ter uma propriedade somente leitura não virtual Fooque não faz nada além de encapsular um GetFoométodo abstrato protegido . Um tipo derivado abstrato pode sombrear a propriedade com uma propriedade de leitura / gravação não virtual que não faz nada além de agrupar o GetFoométodo mencionado acima , juntamente com um SetFoométodo abstrato ; um tipo derivado concreto poderia fazer o acima, mas fornecer definições para GetFooe SetFoo.
Supercat 30/10
1
Apenas para adicionar combustível ao fogo, o C ++ / CLI permite isso.
ymett
1
A capacidade de adicionar um acessador ao substituir uma propriedade foi proposta como uma alteração para uma versão futura do C # ( github.com/dotnet/roslyn/issues/9482 ).
Brandon Bonds
1
Para ser franco, essa pergunta é frequentemente levantada quando é necessário estender um tipo de biblioteca existente que usa o seguinte padrão: classe base somente leitura, classe mutável derivada. Como a biblioteca já usa um padrão incorreto (como o WPF), surge a necessidade de solucionar esse padrão incorreto. As palestras não impedem que você implemente uma solução alternativa no final do dia.
Rwong #

Respostas:

-4

Como o escritor da Baseclass declarou explicitamente que Bar deve ser uma propriedade somente leitura. Não faz sentido que derivações violem este contrato e o façam com leitura / gravação.

Eu estou com a Microsoft neste.
Digamos que eu sou um novo programador que foi instruído a codificar contra a derivação da classe base. Eu escrevo algo que pressupõe que Bar não pode ser gravado (uma vez que a Baseclass afirma explicitamente que é uma propriedade get only). Agora, com sua derivação, meu código pode quebrar. por exemplo

public class BarProvider
{ BaseClass _source;
  Bar _currentBar;

  public void setSource(BaseClass b)
  {
    _source = b;
    _currentBar = b.Bar;
  }

  public Bar getBar()
  { return _currentBar;  }
}

Como Bar não pode ser definido de acordo com a interface BaseClass, o BarProvider assume que o cache é uma coisa segura a se fazer - Como Bar não pode ser modificado. Mas se o conjunto fosse possível em uma derivação, essa classe poderia estar servindo valores antigos se alguém modificasse a propriedade Bar do objeto _source externamente. O ponto é ' Seja aberto, evite fazer coisas sorrateiras e surpreender as pessoas '

Atualização : Ilya Ryzhenkov pergunta: 'Por que as interfaces não seguem as mesmas regras?' Hmm .. isso fica mais confuso como eu penso sobre isso.
Uma interface é um contrato que diz 'espere que uma implementação tenha uma propriedade de leitura denominada Bar'. Pessoalmente , é muito menos provável que eu faça essa suposição de somente leitura se vi uma interface. Quando vejo uma propriedade get-only em uma interface, eu a leio como 'Qualquer implementação expõe esse atributo Bar' ... em uma classe base, clica em 'Bar é uma propriedade somente leitura'. Claro que tecnicamente você não está quebrando o contrato. Está fazendo mais. Então, você está certo em um sentido. Gostaria de dizer 'dificultar o máximo possível para que surjam mal-entendidos'.

Gishu
fonte
94
Eu ainda não estou convencido. Mesmo se não houver um setter explícito, isso não garante que a propriedade sempre retorne o mesmo objeto - ainda pode ser alterada por algum outro método.
Ripper234 17/09/08
34
O mesmo não deveria ser verdade para interfaces? Você pode ter propriedade readonly na interface e leitura / gravação no tipo de implementação.
Ilya Ryzhenkov 19/09/08
49
Não concordo: a classe base declarou apenas a propriedade como não tendo setter; isso não é o mesmo que a propriedade sendo somente leitura. "Somente leitura" é apenas um significado que você atribui a ele. Não há garantias relevantes incorporadas ao C #. Você pode ter uma propriedade get-only que muda sempre, ou uma propriedade get / set em que o setter lança um InvalidOperationException("This instance is read-only").
Roman Starkov
21
Olhando getters e setters como métodos, não vejo por que deveria ser uma "quebra de contrato" mais do que adicionar um novo método. Um contrato de classe base não tem nada a ver com a funcionalidade que não está presente, apenas com o que está presente.
precisa
37
-1 Não concordo com esta resposta e a considero equivocada. Como uma classe base define o comportamento que qualquer classe deve obedecer (consulte o Princípio de substituição de Liskov), mas não restringe (e não deve) restringir a adição de comportamento. Uma classe base pode apenas definir qual deve ser o comportamento e não pode (e não deve) especificar qual o comportamento 'não deve ser' (ou seja, 'não deve ser gravável em níveis concretos)'.
Marcel Valdez Orozco
39

Eu acho que a principal razão é simplesmente que a sintaxe é muito explícita para que isso funcione de outra maneira. Este código:

public override int MyProperty { get { ... } set { ... } }

é bastante explícito que tanto o getquanto o setsão substituições. Não existe setna classe base, portanto o compilador reclama. Assim como você não pode substituir um método que não está definido na classe base, também não pode substituir um setter.

Você pode dizer que o compilador deve adivinhar sua intenção e aplicar apenas a substituição ao método que pode ser substituído (por exemplo, o getter nesse caso), mas isso contraria um dos princípios de design do C # - que o compilador não deve adivinhar suas intenções , porque isso pode parecer errado sem você saber.

Acho que a sintaxe a seguir pode funcionar bem, mas como Eric Lippert continua dizendo, implementar até um recurso menor como esse ainda é uma grande quantidade de esforço ...

public int MyProperty
{
    override get { ... }
    set { ... }
}

ou, para propriedades autoimplementadas,

public int MyProperty { override get; set; }
Roman Starkov
fonte
19

Eu me deparei com o mesmo problema hoje e acho que tenho uma razão muito válida para querer isso.

Primeiro, eu gostaria de argumentar que ter uma propriedade get-only não se traduz necessariamente em somente leitura. Eu interpreto como "A partir desta interface / abtract você pode obter esse valor", isso não significa que alguma implementação dessa interface / classe abstrata não precise do usuário / programa para definir esse valor explicitamente. As classes abstratas servem ao propósito de implementar parte da funcionalidade necessária. Não vejo absolutamente nenhuma razão para que uma classe herdada não possa adicionar um setter sem violar nenhum contrato.

A seguir, é apresentado um exemplo simplificado do que eu precisava hoje. Acabei tendo que adicionar um setter na minha interface apenas para contornar isso. O motivo para adicionar o setter e não adicionar, digamos, um método SetProp é que uma implementação específica da interface usava o DataContract / DataMember para serialização do Prop, que seria desnecessariamente complicado se fosse necessário adicionar outra propriedade apenas para esse fim de serialização.

interface ITest
{
    // Other stuff
    string Prop { get; }
}

// Implements other stuff
abstract class ATest : ITest
{
    abstract public string Prop { get; }
}

// This implementation of ITest needs the user to set the value of Prop
class BTest : ATest
{
    string foo = "BTest";
    public override string Prop
    {
        get { return foo; }
        set { foo = value; } // Not allowed. 'BTest.Prop.set': cannot override because 'ATest.Prop' does not have an overridable set accessor
    }
}

// This implementation of ITest generates the value for Prop itself
class CTest : ATest
{
    string foo = "CTest";
    public override string Prop
    {
        get { return foo; }
        // set; // Not needed
    }
}

Sei que este é apenas um post "meus 2 centavos", mas sinto que com o pôster original e tentar racionalizar que isso é uma coisa boa me parece estranho, especialmente considerando que as mesmas limitações não se aplicam ao herdar diretamente de um interface.

Além disso, a menção sobre o uso de novo em vez de substituição não se aplica aqui, ele simplesmente não funciona e, mesmo que não, não daria o resultado desejado, ou seja, um getter virtual, conforme descrito pela interface.

JJJ
fonte
2
Ou, no meu caso, {get; conjunto privado; } Não estou violando nenhum contrato, pois o conjunto permanece privado e só se preocupa com a classe derivada.
precisa saber é o seguinte
16

É possível

tl; dr - Você pode substituir um método get-only por um setter, se desejar. É basicamente apenas:

  1. Crie uma newpropriedade que tenha a gete a setusando o mesmo nome.

  2. Se você não fizer mais nada, o getmétodo antigo ainda será chamado quando a classe derivada for chamada por meio de seu tipo base. Para corrigir isso, adicione uma abstractcamada intermediária que use overrideno getmétodo antigo para forçá-lo a retornar o getresultado do novo método.

Isso nos permite substituir propriedades por get/ setmesmo que não possuam uma em sua definição básica.

Como bônus, você também pode alterar o tipo de retorno, se desejar.

  • Se a definição de base fosse getapenas, você poderá usar um tipo de retorno mais derivado.

  • Se a definição base fosse setapenas, você pode usar um tipo de retorno menos derivado.

  • Se a definição base já era get/ set, então:

    • você pode usar um tipo de retorno mais derivado se o fizer setapenas;

    • você pode usar um tipo de retorno menos derivado se o fizer getapenas.

Em todos os casos, você pode manter o mesmo tipo de retorno, se desejar. Os exemplos abaixo usam o mesmo tipo de retorno para simplificar.

Situação: getpropriedade unicamente pré-existente

Você tem alguma estrutura de classe que não pode modificar. Talvez seja apenas uma classe ou uma árvore de herança pré-existente. Seja qual for o caso, você deseja adicionar um setmétodo a uma propriedade, mas não pode.

public abstract class A                     // Pre-existing class; can't modify
{
    public abstract int X { get; }          // You want a setter, but can't add it.
}
public class B : A                          // Pre-existing class; can't modify
{
    public override int X { get { return 0; } }
}

Problema: Não é possível overridea get-apenas com get/set

Você deseja overridecom um get/ setproperty, mas ele não será compilado.

public class C : B
{
    private int _x;
    public override int X
    {
        get { return _x; }
        set { _x = value; }   //  Won't compile
    }
}

Solução: use uma abstractcamada intermediária

Embora você não possa diretamente overridecom um get/ setproperty, você pode :

  1. Crie uma propriedade new get/ setcom o mesmo nome.

  2. overrideo getmétodo antigo com um acessador para o novo getmétodo para garantir consistência.

Então, primeiro você escreve a abstractcamada intermediária:

public abstract class C : B
{
    //  Seal off the old getter.  From now on, its only job
    //  is to alias the new getter in the base classes.
    public sealed override int X { get { return this.XGetter; }  }
    protected abstract int XGetter { get; }
}

Em seguida, você escreve a classe que não seria compilada anteriormente. Desta vez, ele será compilado porque você não está realmente overridena getpropriedade -only; em vez disso, você está substituindo-o usando a newpalavra - chave.

public class D : C
{
    private int _x;
    public new virtual int X { get { return this._x; } set { this._x = value; } }

    //  Ensure base classes (A,B,C) use the new get method.
    protected sealed override int XGetter { get { return this.X; } }
}

Resultado: Tudo funciona!

Obviamente, isso funciona como planejado D.

var test = new D();
Print(test.X);      // Prints "0", the default value of an int.

test.X = 7;
Print(test.X);      // Prints "7", as intended.

Tudo ainda funciona como pretendido ao visualizar Dcomo uma de suas classes base, por exemplo, Aou B. Mas, a razão pela qual funciona pode ser um pouco menos óbvia.

var test = new D() as B;
//test.X = 7;       // This won't compile, because test looks like a B,
                    // and B still doesn't provide a visible setter.

No entanto, a definição de classe base getainda é substituída pela definição de classe derivada de get, portanto ainda é completamente consistente.

var test = new D();
Print(test.X);      // Prints "0", the default value of an int.

var baseTest = test as A;
Print(test.X);      // Prints "7", as intended.

Discussão

Este método permite adicionar setmétodos às getpropriedades -only. Você também pode usá-lo para fazer coisas como:

  1. Altere qualquer propriedade para uma propriedade get-only, set-lyly ou get-and- set, independentemente do que estava em uma classe base.

  2. Altere o tipo de retorno de um método nas classes derivadas.

As principais desvantagens são que há mais codificação a ser feita e um extra abstract classna árvore de herança. Isso pode ser um pouco irritante para os construtores que aceitam parâmetros porque eles precisam ser copiados / colados na camada intermediária.

Nat
fonte
Deu a sua própria pergunta: stackoverflow.com/questions/22210971
Nat Nat
Boa abordagem (+1). Duvido, no entanto, se isso funcionará sem problemas com a estrutura da entidade.
Mike de Klerk
9

Concordo que não ser capaz de substituir um getter em um tipo derivado é um anti-padrão. Somente leitura especifica falta de implementação, não um contrato de puro funcional (implícito na resposta do voto principal).

Suspeito que a Microsoft tenha essa limitação porque o mesmo equívoco foi promovido ou talvez devido à simplificação da gramática; No entanto, agora que o escopo pode ser aplicado para obter ou definir individualmente, talvez possamos esperar que a substituição também seja.

O equívoco indicado pelo voto superior responde que uma propriedade somente leitura deve ser de alguma forma mais "pura" do que uma propriedade de leitura / gravação é ridícula. Basta olhar para muitas propriedades comuns de somente leitura na estrutura; o valor não é constante / puramente funcional; por exemplo, DateTime.Now é somente leitura, mas qualquer coisa, exceto um valor funcional puro. Uma tentativa de 'cache' de um valor de uma propriedade somente leitura, assumindo que ele retornará o mesmo valor da próxima vez que for arriscado.

De qualquer forma, usei uma das seguintes estratégias para superar essa limitação; ambos são menos que perfeitos, mas permitem que você manque além dessa deficiência de idioma:

   class BaseType
   {
      public virtual T LastRequest { get {...} }
   }

   class DerivedTypeStrategy1
   {
      /// get or set the value returned by the LastRequest property.
      public bool T LastRequestValue { get; set; }

      public override T LastRequest { get { return LastRequestValue; } }
   }

   class DerivedTypeStrategy2
   {
      /// set the value returned by the LastRequest property.
      public bool SetLastRequest( T value ) { this._x = value; }

      public override T LastRequest { get { return _x; } }

      private bool _x;
   }
T.Tobler
fonte
DateTime.Now é puramente funcional, pois não tem efeitos colaterais (o fato de levar tempo para ser executado pode ser chamado de efeito colateral, mas como o tempo de execução - pelo menos quando limitado e curto - não é considerado um efeito colateral de qualquer outro método, eu também não consideraria um aqui).
Supercat 30/10
1
Oi supercat. Você está correto quanto aos efeitos colaterais observáveis ​​externamente, impedindo que uma função seja pura, mas a outra restrição é que um valor de resultado de função pura deve depender apenas dos parâmetros. Uma propriedade estática pura, como uma função, deve, portanto, ser imutável e constante. Um compilador sofisticado pode substituir todas as chamadas subseqüentes para uma função pura sem parâmetros com o resultado retornado pela primeira chamada.
T.Tobler
Você está certo de que minha terminologia era imprecisa, mas acho que a adequação de algo ser uma propriedade somente leitura, em vez de um método, depende da falta de efeitos colaterais, e não da pureza.
supercat
2

Talvez você possa solucionar o problema criando uma nova propriedade:

public new int Bar 
{            
    get { return 0; }
    set {}        
}

int IBase.Bar { 
  get { return Bar; }
}
Hallgrim
fonte
4
Mas você pode fazer isso apenas ao implementar uma interface, não ao herdar de uma classe base. Você teria que escolher outro nome.
Svick
Suponho que a classe de exemplo implemente o IBase (algo como a interface pública IBase {int Bar {get;}}); caso contrário, na classe de implementação, int IBase.Bar {...} não é permitido. Em seguida, você pode simplesmente criar uma propriedade regular Bar com get e set, mesmo que a propriedade Bar da interface não exija um conjunto.
Ibrahim ben Salah
1

Eu posso entender todos os seus pontos de vista, mas efetivamente as propriedades automáticas do C # 3.0 ficam inúteis nesse caso.

Você não pode fazer nada assim:

public class ConcreteClass : BaseClass
{
    public override int Bar
    {
        get;
        private set;
    }
}

IMO, C # não deve restringir esses cenários. É de responsabilidade do desenvolvedor usá-lo adequadamente.

Thomas Danecker
fonte
Eu não entendo o seu ponto. Você concorda que o C # deve permitir adicionar um setter ou você está com a maioria das outras pessoas que responderam a essa pergunta?
Ripper234 20/09/08
2
Como eu disse, IMO, C # deve permitir adicionar um setter. É de responsabilidade do desenvolvedor usá-lo adequadamente.
Thomas Danecker 23/09/08
1

O problema é que, por qualquer motivo, a Microsoft decidiu que deveria haver três tipos distintos de propriedades: somente leitura, somente gravação e leitura e gravação, apenas uma das quais pode existir com uma determinada assinatura em um determinado contexto; propriedades podem ser substituídas apenas por propriedades declaradas de forma idêntica. Para fazer o que você deseja, seria necessário criar duas propriedades com o mesmo nome e assinatura - uma das quais era somente leitura e uma das quais era leitura e gravação.

Pessoalmente, desejo que todo o conceito de "propriedades" possa ser abolido, exceto que a sintaxe property-ish pode ser usada como açúcar sintático para chamar os métodos "get" e "set". Isso não apenas facilitaria a opção 'adicionar conjunto', mas também permitiria que 'get' retornasse um tipo diferente de 'conjunto'. Embora essa capacidade não seja usada com muita frequência, às vezes pode ser útil fazer com que um método 'get' retorne um objeto de wrapper, enquanto o 'set' pode aceitar um wrapper ou dados reais.

supercat
fonte
0

Aqui está uma solução alternativa para conseguir isso usando o Reflection:

var UpdatedGiftItem = // object value to update;

foreach (var proInfo in UpdatedGiftItem.GetType().GetProperties())
{
    var updatedValue = proInfo.GetValue(UpdatedGiftItem, null);
    var targetpropInfo = this.GiftItem.GetType().GetProperty(proInfo.Name);
    targetpropInfo.SetValue(this.GiftItem, updatedValue,null);
}

Dessa forma, podemos definir o valor do objeto em uma propriedade que é somente leitura. Pode não funcionar em todos os cenários!

user2514880
fonte
1
Tem certeza de que pode chamar um setter por meio de reflexão que não existe (como acontece nas propriedades descritas somente para getter)?
OR Mapper
0

Você deve alterar o título da sua pergunta para detalhar que sua pergunta é unicamente em relação à substituição de uma propriedade abstrata ou que sua pergunta é sobre a substituição geral da propriedade get-only de uma classe.


Se o primeiro (substituindo uma propriedade abstrata)

Esse código é inútil. Uma classe base sozinha não deve lhe dizer que você é forçado a substituir uma propriedade Get-Only (talvez uma interface). Uma classe base fornece funcionalidade comum que pode exigir entrada específica de uma classe de implementação. Portanto, a funcionalidade comum pode fazer chamadas para abstrair propriedades ou métodos. Nesse caso, os métodos de funcionalidade comuns devem solicitar que você substitua um método abstrato, como:

public int GetBar(){}

Mas se você não tem controle sobre isso, e a funcionalidade da classe base lê de sua própria propriedade pública (estranha), faça o seguinte:

public abstract class BaseClass
{
    public abstract int Bar { get; }
}

public class ConcreteClass : BaseClass
{
    private int _bar;
    public override int Bar
    {
        get { return _bar; }
    }
    public void SetBar(int value)
    {
        _bar = value;
    }
}

Quero ressaltar o comentário (estranho): eu diria que uma prática recomendada é que uma classe não use suas próprias propriedades públicas, mas use seus campos privados / protegidos quando existirem. Portanto, este é um padrão melhor:

public abstract class BaseClass {
    protected int _bar;
    public int Bar { get { return _bar; } }
    protected void DoBaseStuff()
    {
        SetBar();
        //Do something with _bar;
    }
    protected abstract void SetBar();
}

public class ConcreteClass : BaseClass {
    protected override void SetBar() { _bar = 5; }
}

Se o último (substituindo a propriedade get-only de uma classe)

Toda propriedade não abstrata tem um setter. Caso contrário, é inútil e você não deve se importar em usá-lo. A Microsoft não precisa permitir que você faça o que deseja. Razão: o levantador existe de uma forma ou de outra e você pode realizar o que deseja Veerryy facilmente.

A classe base ou qualquer classe com a qual você possa ler uma propriedade {get;}possui ALGUM tipo de setter exposto para essa propriedade. Os metadados ficarão assim:

public abstract class BaseClass
{
    public int Bar { get; }
}

Mas a implementação terá duas extremidades do espectro de complexidade:

Menos complexo:

public abstract class BaseClass
{
    private int _bar;
    public int Bar { 
        get{
            return _bar;
        }}
    public void SetBar(int value) { _bar = value; }
}

Mais complexo:

public abstract class BaseClass
{
    private int _foo;
    private int _baz;
    private int _wtf;
    private int _kthx;
    private int _lawl;

    public int Bar
    {
        get { return _foo * _baz + _kthx; }
    }
    public bool TryDoSomethingBaz(MyEnum whatever, int input)
    {
        switch (whatever)
        {
            case MyEnum.lol:
                _baz = _lawl + input;
                return true;
            case MyEnum.wtf:
                _baz = _wtf * input;
                break;
        }
        return false;
    }
    public void TryBlowThingsUp(DateTime when)
    {
        //Some Crazy Madeup Code
        _kthx = DaysSinceEaster(when);
    }
    public int DaysSinceEaster(DateTime when)
    {
        return 2; //<-- calculations
    }
}
public enum MyEnum
{
    lol,
    wtf,
}

Meu argumento é que, de qualquer maneira, você tem o levantador exposto. No seu caso, você pode substituir, int Barporque não deseja que a classe base lide com ela, não tenha acesso para revisar como ela está lidando com ela ou foi encarregado de colocar algum código muito rapidamente contra a sua vontade .

Nos Últimos e Antigos (Conclusão)

Longa história: Não é necessário que a Microsoft mude nada. Você pode escolher como sua classe de implementação é configurada e, sem o construtor, usar toda ou nenhuma da classe base.

Suamere
fonte
0

Solução para apenas um pequeno subconjunto de casos de uso, mas, no entanto: no C # 6.0, o setter "somente leitura" é adicionado automaticamente para propriedades substituídas somente de getter.

public abstract class BaseClass
{
    public abstract int Bar { get; }
}

public class ConcreteClass : BaseClass
{
    public override int Bar { get; }

    public ConcreteClass(int bar)
    {
        Bar = bar;
    }
}
lxa
fonte
-1

Porque isso quebraria o conceito de encapsulamento e ocultação de implementação. Considere o caso ao criar uma classe, enviá-la e, em seguida, o consumidor da sua classe poderá definir uma propriedade para a qual você originalmente fornece apenas um getter. Isso afetaria efetivamente quaisquer invariantes da sua classe dos quais você pode confiar em sua implementação.

petr k.
fonte
1
-1: Ter um levantador não permite automaticamente que um descendente quebre quaisquer invariantes. O levantador ainda pode alterar apenas as coisas que já podem ser alteradas pelo descendente.
Roman Starkov
@RomanStarkov Isso é verdade. Mas a questão deste post é; Por que não é possível adicionar um setter a uma abstração que você não contrata. Seu comentário seria apropriado na pergunta oposta; Por que a Microsoft permite que você crie uma propriedade ReadOnly em uma abstração se o descendente tem controle total de seus campos particulares?
Suamere 17/03/19
+1: Esta pergunta é sobre por que a Microsoft não permite que você quebre um contrato. Embora o motivo das suas votações negativas seja mais o motivo pelo qual a Microsoft permite que um contrato inclua apenas propriedades somente leitura. Portanto, esta resposta está correta no contexto.
Suamere 17/03/19
@Suamere o contrato não declara que a propriedade não pode ser alterada. Tudo o que afirma é que existe uma propriedade que pode ser lida. Adicionar um levantador não quebrará este contrato. Você pode interpretá- lo como uma intenção de criar uma propriedade somente leitura, mas não pode garantir no C # 6 que não há como alterá-lo, portanto, o contrato em que você está pensando não está realmente lá. Pense em uma interface: ela pode oferecer um contrato em uma propriedade gettable (não get-only!). Essa interface pode ser implementada por uma propriedade get + set muito bem.
Roman Starkov
Você está abordando isso da perspectiva do autor concreto. Ele pode mudar como quiser. Estou abordando isso da perspectiva do autor da classe base -> Abordando como o consumidor final da classe de implementação. O autor da classe base não deseja que nenhum consumidor das futuras classes concretas possa alterar essa propriedade sem que o autor da classe concreta exponha um método especial para tratar caso a caso. Porque na classe base, essa propriedade é tratada como imutável e comunicada apenas expondo-a como tal.
Suamere
-1

Isto não é impossível. Você simplesmente precisa usar a palavra-chave "nova" em sua propriedade. Por exemplo,

namespace {
    public class Base {
        private int _baseProperty = 0;

        public virtual int BaseProperty {
            get {
                return _baseProperty;
            }
        }

    }

    public class Test : Base {
        private int _testBaseProperty = 5;

        public new int BaseProperty {
            get {
                return _testBaseProperty;
            }
            set {
                _testBaseProperty = value;
            }
        }
    }
}

Parece que essa abordagem satisfaz os dois lados desta discussão. O uso de "novo" quebra o contrato entre a implementação da classe base e a implementação da subclasse. Isso é necessário quando uma classe pode ter vários contratos (via interface ou classe base).

Espero que isto ajude

mbolt35
fonte
29
Esta resposta está incorreta porque a propriedade marcada newnão substitui mais a base virtual. Em particular, se a propriedade base for abstrata , como na postagem original, é impossível usar a newpalavra - chave em uma subclasse não abstrata, pois é necessário substituir todos os membros abstratos.
Timwi
Isso é fenomenalmente perigoso, porque os seguintes retornam resultados diferentes, apesar de serem o mesmo objeto: Test t = new Test(); Base b = t; Console.WriteLine(t.BaseProperty)dão 5, Console.WriteLine(b.BaseProperty)dão 0 e, quando seu sucessor precisa depurar isso, é melhor você ter se mudado para muito longe.
Mark Sowul
Concordo com Mark, isso é extremamente desagradável. Se ambas as implementações da BaseProperty fossem apoiadas pela mesma variável, seria bom que o IMHO. IE: Torne _baseProperty protegido e abole _testBaseProperty. Além disso, a implementação do AFAIK no Base não precisa ser virtual, pois você não a substitui.
Steazy
A maioria das respostas às perguntas pode ter opinião e opinião válidas. Mas, além dos comentários anteriores, minha principal crítica a essa resposta é que acredito que a pergunta real possa ser literalmente sobre propriedades abstratas em uma base de código que o consumidor não tem controle. Portanto, minha crítica é que essa resposta pode não se encaixar na pergunta.
Suamere 22/10/2015
-1

Uma propriedade somente leitura na classe base indica que essa propriedade representa um valor que sempre pode ser determinado de dentro da classe (por exemplo, um valor de enumeração que corresponde ao contexto (db-) de um objeto). Portanto, a responsabilidade de determinar o valor permanece dentro da classe.

Adicionar um setter causaria um problema estranho aqui: Um erro de validação deve ocorrer se você definir o valor para qualquer outra coisa que não seja o único valor possível que ele já possui.

As regras geralmente têm exceções, no entanto. É muito bem possível que, por exemplo, em uma classe derivada, o contexto reduza os possíveis valores de enum para 3 em 10, mas o usuário desse objeto ainda precisa decidir qual é o correto. A classe derivada precisa delegar a responsabilidade de determinar o valor para o usuário deste objeto. É importante perceber que o usuário desse objeto deve estar ciente dessa exceção e assumir a responsabilidade de definir o valor correto.

Minha solução nesse tipo de situação seria deixar a propriedade somente leitura e adicionar uma nova propriedade de leitura e gravação à classe derivada para suportar a exceção. A substituição da propriedade original simplesmente retornará o valor da nova propriedade. A nova propriedade pode ter um nome próprio, indicando o contexto dessa exceção corretamente.

Isso também apóia a observação válida: "torne o mais difícil possível que ocorram mal-entendidos" por Gishu.

Remco te Wierik
fonte
1
Nenhum constrangimento seria implícito por uma ReadableMatrixclasse (com uma propriedade indexada somente leitura de dois argumentos) tendo subtipos ImmutableMatrixe MutableMatrix. O código que precisa apenas ler uma matriz e não se importa se pode mudar algum tempo no futuro, pode aceitar um parâmetro do tipo ReadableMatrixe ficar perfeitamente satisfeito com os subtipos mutáveis ​​ou imutáveis.
Supercat 30/10
Um setter adicional pode modificar uma variável privada dentro da classe. A chamada propriedade "somente leitura" da classe base ainda determinaria o valor "de dentro da classe" (lendo a nova variável privada). Não vejo o problema lá. Observe também, por exemplo, o que List<T>.Countfaz: Ele não pode ser atribuído, mas isso não significa que seja "somente leitura", pois não pode ser modificado pelos usuários da classe. Pode muito bem ser modificado, embora indiretamente, adicionando e removendo itens da lista.
OR Mapper
-2

Como no nível de IL, uma propriedade de leitura / gravação se traduz em dois métodos (getter e setter).

Ao substituir, você deve continuar suportando a interface subjacente. Se você pudesse adicionar um setter, estaria efetivamente adicionando um novo método, que permaneceria invisível para o mundo exterior, no que diz respeito à interface de suas classes.

É verdade que adicionar um novo método não quebraria a compatibilidade propriamente dita, mas, como permaneceria oculto, a decisão de proibir isso faz todo o sentido.

Ishmaeel
fonte
2
Eu não me importo muito com "o mundo exterior" aqui. Quero adicionar funcionalidade a uma classe, sempre visível apenas para o código que conhece a classe específica e não a base.
Ripper234 17/09/08
@ ripper234 Veja a resposta de Matt bolt: se você deseja tornar a nova propriedade visível apenas para usuários da classe derivada, você deve usar em newvez de override.
Dan Berindei
1
Não permanece oculto. Você está dizendo que não pode adicionar um novo método a uma classe infantil porque o novo método será "permanecer oculto do mundo exterior". É claro que ele permanece oculto da perspectiva da classe base, mas é acessível a partir dos clientes da classe filho. (Ie para afetar o que o getter deve retornar)
Sheepy
Sheepy, não é isso que estou dizendo. Quero dizer, exatamente como você apontou, ele permaneceria oculto "da perspectiva da classe base". Por isso, acrescentei "no que diz respeito à interface de suas aulas ". Se você estiver subclassificando uma classe base, é quase certo que o "mundo externo" estará se referindo às suas instâncias de objeto com o tipo de classe base. Se eles usassem diretamente sua classe concreta, você não precisaria de compatibilidade com a classe base e poderia, por todos os meios, usar newcom suas adições.
Ishmaeel
-2

Porque uma classe que possui uma propriedade somente leitura (sem setter) provavelmente tem uma boa razão para isso. Pode não haver nenhum armazenamento de dados subjacente, por exemplo. Permitir que você crie um levantador quebra o contrato estabelecido pela classe. É apenas ruim OOP.

Joel Coehoorn
fonte
Up-Vote. Isso é muito sucinto. Por que a Microsoft deve permitir que o consumidor de uma classe base quebre o contrato? Concordo com esta versão muito curta da minha resposta muito longa e confusa. Ri muito. Se você é consumidor de uma classe base, não deve querer quebrar o contrato, mas PODE implementá-lo de duas maneiras diferentes.
Suamere 17/03/19