web-dev-qa-db-fra.com

Propriété abstraite dans la classe de base pour forcer le programmeur à la définir

Je code avec un modèle d'état pour un périphérique intégré. J'ai une classe de base/abstraite appelée État, puis chaque classe d'état discrète (concrète) implémente la classe d'État abstraite.

Dans la classe d'état, j'ai plusieurs méthodes abstraites. Si je n'implémente pas les méthodes abstraites dans la classe discrète (concrète), Visual Studio donnera une erreur quelque chose comme ceci:

... Erreur 1 'myConcreteState' n'implémente pas le membre abstrait hérité 'myAbstractState'

Maintenant: j'essaie de créer une propriété String pour chaque État appelé StateName. Chaque fois que je crée une nouvelle classe concrète, je dois définir StateName. Je veux que VS génère une erreur si je ne l'utilise pas. Existe-t-il un moyen simple de procéder?

J'ai essayé cela dans la classe abstract/base:

public abstract string StateName { get; set; }

Mais je n'ai pas besoin d'implémenter les méthodes Get et Set dans chaque état.

Question révisée: dans une situation idéale, chaque classe d'état aurait besoin que StateName définisse et soit héritée de la classe de base abstraite.

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

Si cette instruction est manquante, Visual Studio générera une erreur comme décrit ci-dessus. Est-ce possible, et si oui comment?

10
GisMofx

Je suppose que la façon "correcte" de le faire est d'avoir un constructeur protégé sur la classe de base qui nécessite le nom de l'état comme paramètre.

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; } }
}

Les états concrets fournissent alors un constructeur public qui appelle implicitement le constructeur de la classe de base avec le nom approprié.

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

Étant donné que la classe de base n'expose aucun autre constructeur (ni protégé ni public), chaque classe héritée doit passer par ce constructeur unique et doit donc définir un nom.

Notez que vous n'avez pas besoin de fournir le nom lorsque vous instanciez un état concret car son constructeur s'en charge:

var someState = new SomeState(); // No need to define the name here
var name = someState.Name; // returns "The name of this state"
16
Roman Reiner

À partir de C # 6 (je crois - C #: Le nouveau et amélioré C # 6. ) vous pouvez créer des propriétés getter uniquement. Vous pouvez donc déclarer votre classe de base comme ceci -

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

    // Your other functions....
}

Et puis dans votre sous-classe, vous pouvez implémenter State comme ça -

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

Ou encore plus net en utilisant l'opérateur lambda -

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

Les deux renverraient toujours "State_1" et seraient immuables.

3
John C

Vos exigences sont une contradiction:

J'essaie de créer une propriété String pour chaque État appelé StateName.

vs.

Mais je pas besoin pour implémenter tout cela dans chaque état.

Il n'y a pas de fonctionnalité de langue qui vous permet de forcer l'existence d'un membre dans seulement quelques sous-classes. Après tout, l'intérêt d'utiliser une super classe est de s'appuyer sur le fait que toutes les sous-classes auront tous les membres de la super classe (et éventuellement plus). Si vous voulez créer des classes qui agissent comme State mais n'ont pas de nom, alors par définition, elles ne doivent pas (/ peuvent) être des sous-classes de State.

Vous devez soit modifier vos besoins, soit utiliser autre chose qu'un simple héritage.

Une solution possible avec le code tel qu'il est pourrait être de rendre la méthode dans State non abstraite et de renvoyer une chaîne vide.

2
null