Propriedade abstrata na classe base para forçar o programador a defini-lo

10

Estou codificando com um padrão de estado para um dispositivo incorporado. Eu tenho uma classe base / abstrata chamada State e, em seguida, cada classe de estado discreta (concreta) implementa a classe abstrata de estado.

Na classe estadual, tenho vários métodos abstratos. Se eu não implementar os métodos abstratos na classe discreta (concreta), o Visual Studio emitirá um erro semelhante a este:

... Erro 1 'myConcreteState' não implementa o membro abstrato herdado 'myAbstractState'

Agora: estou tentando criar uma propriedade String para cada estado chamado StateName. Sempre que crio uma nova classe concreta, preciso definir StateName. Quero que o VS gere um erro se não o usar. Existe uma maneira simples de fazer isso?

Eu tentei isso na classe abstract / base:

public abstract string StateName { get; set; }

Mas não preciso implementar os métodos Get e Set em cada estado.

Pergunta revisada: Em uma situação ideal, cada classe de estado teria que definir StateName e ser herdada da classe base abstrata.

StateName = "MyState1"; //or whatever the state's name is

Se essa instrução estiver faltando, o Visual Studio gerará um erro conforme descrito acima. Isso é possível e, se sim, como?

GisMofx
fonte
2
Eu não entendo a pergunta.
precisa
Eu acho que a maneira "correta" de fazer isso é ter um construtor protegido na classe base que requer o nome do estado como parâmetro.
Roman Reiner
@RomanReiner Eu pensei em fazer isso também ... mas parece redundante porque cada vez que muda / chama um estado, eu teria que digitar o nome.
GisMofx 6/06/2015
@BenAaronson Esclareço minha pergunta perto do fundo.
GisMofx 6/06/2015
O nome do estado é constante por tipo ou constante por instância?
Roman Reiner

Respostas:

16

Eu acho que a maneira "correta" de fazer isso é ter um construtor protegido na classe base que requer o nome do estado como parâmetro.

public abstract class State
{
    private readonly string _name;

    protected State(string name)
    {
        if(String.IsNullOrEmpty(name))
            throw new ArgumentException("Must not be empty", "name");

        _name = name;
    }

    public string Name { get { return _name; } }
}

Os estados concretos fornecem um construtor público que chama implicitamente o construtor da classe base com o nome apropriado.

public abstract class SomeState : State
{
    public SomeState() : base("The name of this state")
    {
        // ...
    }
}

Como a classe base não expõe outros construtores (nem protegidos nem públicos), cada classe herdada precisa passar por esse único construtor e, portanto, precisa definir um nome.

Observe que você não precisa fornecer o nome ao instanciar um estado concreto, porque o construtor cuida disso:

var someState = new SomeState(); // No need to define the name here
var name = someState.Name; // returns "The name of this state"
Roman Reiner
fonte
11
Obrigado! Na realidade, meu construtor de classe base agora usa um argumento adicional; então eu tenho duas entradas. Assim que eu configuro isso, recebo erros que precisam de um argumento adicional nos construtores da classe state ...:base(arg1, arg2)! Esta é uma solução que eu estava procurando. Isso realmente ajuda a manter a codificação do meu estado mais consistente.
GisMofx
3

A partir do C # 6 (acredito - C #: o C # 6.0 novo e aprimorado ), você é capaz de criar apenas propriedades getter. Então você pode declarar sua classe base assim -

public abstract class State
{
    public abstract string name { get; }

    // Your other functions....
}

E então, na sua subclasse, você pode implementar Stateassim -

public class SomeState : State
{
    public override string name { get { return "State_1"; } }
}

Ou até mais limpo usando o operador lambda -

public class SomeState : State
{
    public override string name => "State_1";
}

Ambos sempre retornariam "State_1" e seriam imutáveis.

John C
fonte
11
isso pode ser escrito public override string name { get; } = "State_1";e, portanto, não é necessário reavaliar o valor toda vez.
18718 Dave Cousineau #
2

Seus requisitos são uma contradição:

Estou tentando criar uma propriedade String para cada estado chamado StateName.

vs.

Mas não preciso implementar tudo isso em cada Estado.

Não há nenhum recurso de idioma que permita forçar a existência de um membro em apenas algumas subclasses. Afinal, o objetivo de usar uma superclasse é confiar no fato de que todas as subclasses terão todos os membros da superclasse (e possivelmente mais). Se você deseja criar classes que agem como Statemas não têm nome, então, por definição, elas (/ podem) não devem ser subclasses de State.

Você precisa alterar seus requisitos ou usar algo diferente da herança simples.

Uma possível solução com o código atual é tornar o método Statenão abstrato e retornar uma string vazia.

nulo
fonte
Acho que você tirou a segunda afirmação do contexto, mas vou esclarecer. Não preciso dos métodos Get / Set, simplesmente quero StateName = "MyState1"em cada Estado. Se eu não tiver essa instrução na classe state, o ideal é que o Visual Studio gere um erro.
GisMofx 6/06/2015
3
@GisMofx Se você deseja forçar o nome do estado em cada subclasse, faça o resumo, mas não exija um estado. Cada subclasse terá que fornecer return "MyState1"como implementação e pode adicionar armazenamento mutável para o valor, se necessário. Mas você precisa esclarecer os requisitos da pergunta - todo tipo de estado exige um StateName que possa ser lido e qualquer tipo de estado exige que ele seja modificado após a criação?
Pete Kirkham
@PeteKirkham cada estado requer um nome, não deve ser alterado após a criação. Os nomes dos estados são estáticos / imutáveis.
GisMofx