web-dev-qa-db-fra.com

Forcer les sous-classes d'une interface à implémenter ToString

Supposons que j'ai une interface IFoo et que je veux que toutes les sous-classes de IFoo remplacent la méthode ToString de Object. Est-ce possible? 

Ajouter simplement la signature de la méthode à IFoo en tant que tel ne fonctionne pas:

interface IFoo
{
    String ToString();
}

puisque toutes les sous-classes étendent Object et fournissent une implémentation de cette façon, le compilateur ne s'en plaint pas. Aucune suggestion?

52
rjohnston

Je ne crois pas que vous puissiez le faire avec une interface. Vous pouvez utiliser une classe de base abstraite si:

public abstract class Base
{
    public abstract override string ToString(); 
}
76
Jon Skeet
abstract class Foo
{
    public override abstract string ToString();
}

class Bar : Foo
{
    // need to override ToString()
}
21
Andrew Peters

Jon & Andrew: Ce tour abstrait est vraiment utile. Je ne savais pas que vous pouviez mettre fin à la chaîne en la déclarant abstraite. À votre santé :)

Dans le passé, lorsque j'avais demandé que ToString () soit écrasé dans des classes dérivées, j'avais toujours utilisé un modèle tel que celui-ci:

public abstract class BaseClass
{
    public abstract string ToStringImpl();

    public override string ToString()
    {
        return ToStringImpl();
    }    
}
8
Mark Simpson

Je sais que cela ne répond pas à votre question, mais comme il n’existe aucun moyen de faire ce que vous demandez, je pensais partager ma propre approche à la vue des autres.

J'utilise un hybride des solutions proposées par Mark et Andrew.

Dans mon application, toutes les entités de domaine proviennent d'une classe de base abstraite:

public abstract class Entity
{
    /// <summary>
    /// Returns a <see cref="System.String"/> that represents this instance.
    /// </summary>
    public override string ToString()
    {
        return this is IHasDescription
                   ? ((IHasDescription) this).EntityDescription
                   : base.ToString();
    }
}

L’interface elle-même ne définit qu’un simple accesseur:

public interface IHasDescription : IEntity
{
    /// <summary>
    /// Creates a description (in english) of the Entity.
    /// </summary>
    string EntityDescription { get; }
}

Alors maintenant, il existe un mécanisme de secours intégré - ou, en d'autres termes, une Entity qui implémente IHasDescription doit fournir la EntityDescription, mais toute Entity peut toujours être convertie en chaîne.

Je sais que ce n'est pas radicalement différent des autres solutions proposées ici, mais j'aime bien l'idée de minimiser la responsabilité du type de base Entity, de sorte que l'implémentation de l'interface de description reste facultative, mais vous êtes obligé d'implémenter la description. -méthode si vous implémentez l'interface.

IMHO, les interfaces implémentées par la classe de base object ne devraient pas "compter" comme implémentées - il serait bien d'avoir une option de compilation pour cela, mais bon ...

1
mindplay.dk

L'implémentation d'une méthode d'interface scelle implicitement la méthode (et la redéfinit également). Donc, sauf indication contraire de votre part, la première implémentation d'une interface met fin à la chaîne de substitution en C #.

Essential .NET

Classe abstraite = ton ami

Vérifiez cette question

1
Ric Tokyo

Désolé d'enterrer ce vieux fil de la tombe, spécialement parce que notre cher @ jon-skeet a déjà fourni sa propre réponse .

Mais si vous souhaitez conserver l'interface et ne pas utiliser de classe abstraite, je suppose que cela est toujours possible en ayant simplement votre interface implémentant l'interface System.IFormattable .

interface IFoo : IFormattable
{
}

La seule chose à garder à l'esprit est que, pour implémenter correctement cet IFormattable, l'implémentation concrète doit également écraser la Object.ToString().

Ceci est clairement expliqué dans ce billet de Nice.

Votre classe de béton est maintenant comme

public class Bar : IFoo
{
    public string ToString(string format, IFormatProvider formatProvider)
    {
        return $"{nameof(Bar)}";
    }

    public override string ToString()
    {
        return ToString(null, System.Globalization.CultureInfo.CurrentCulture);
    }
}

J'espère que cela pourra toujours aider quelqu'un.

0
Seb T.

Je ne pense pas que vous puissiez forcer une sous-classe à remplacer aucune des méthodes virtuelles de la classe de base à moins que ces méthodes ne soient abstraites. 

0
Frederick The Fool