C#, implémenter des méthodes de type 'static abstract'
J'ai récemment rencontré un problème où il semble que j'ai besoin d'une méthode' abstraite statique'. Je sais pourquoi c'est impossible, mais comment puis-je contourner cette limitation?
, Par exemple j'ai une classe abstraite qui a une chaîne de description. Puisque cette chaîne est commune à toutes les instances, elle est marquée comme statique, mais je veux exiger que toutes les classes dérivées de cette classe fournissent leur propre propriété de Description, Donc je l'ai marquée comme abstraite:
abstract class AbstractBase
{
...
public static abstract string Description{get;}
...
}
Il ne compilera pas bien sûr. J'ai pensé d'utiliser des interfaces mais les interfaces peuvent ne pas contenir de signatures de méthode statiques.
Devrais-je le rendre simplement non statique, et toujours obtenir une instance pour obtenir cette information spécifique à la classe?
Des idées?
7 réponses
Combiner statique et abstrait est quelque peu dénué de sens, oui. L'idée derrière static est qu'il n'est pas nécessaire de présenter une instance de la classe pour utiliser le membre en question; cependant, avec abstract, on s'attend à ce qu'une instance soit d'une classe dérivée qui fournit une implémentation concrète.
Je peux voir pourquoi vous voudriez ce genre de combinaison, mais le fait est que le seul effet serait de refuser l'utilisation de 'this' ou de tout membre non statique. Qui est le parent de la classe dicter une restriction dans l'implémentation de la classe dérivée, même s'il n'y a pas de différence sous-jacente entre appeler un membre abstrait ou ' abstrait statique '(car les deux auraient besoin d'une instance concrète pour comprendre quelle implémentation utiliser)
Vous ne pouvez pas.
L'endroit pour le faire est avec des attributs.
Par exemple
[Name("FooClass")]
class Foo
{
}
Si cela ne vous dérange pas de reporter les implémentations pour implémenter raisonnablement la propriété Description, Vous pouvez simplement faire
public abstract string ClassDescription {get; }
// ClassDescription is more intention-revealing than Description
Et implémenter des classes ferait quelque chose comme ceci:
static string classDescription="My Description for this class";
override string ClassDescription { get { return classDescription; } }
Ensuite, vos classes sont tenues de suivre le contrat d'avoir une description, mais vous leur laissez le soin de le faire raisonnablement. Il n'y a aucun moyen de spécifier une implémentation de manière orientée objet (sauf par des hacks cruels et fragiles).
Cependant, dans mon esprit cette Description est-ce que les métadonnées de classe, donc je préférerais utiliser le mécanisme d'attribut comme d'autres l'ont décrit. Si vous êtes particulièrement préoccupé par les utilisations multiples de la réflexion, créez un objet qui reflète sur l'attribut qui vous intéresse et stockez un dictionnaire entre le Type et la Description. Cela minimisera la réflexion (autre que l'inspection de type d'exécution, ce qui n'est pas si mauvais). Le dictionnaire peut être stocké en tant que membre de n'importe quelle classe qui a généralement besoin de cette information, ou, si les clients du domaine l'exigent, via un objet singleton ou context.
S'il est statique, il n'y a qu'une seule instance de la variable, Je ne vois pas comment l'héritage aurait du sens si nous pouvions faire ce que vous voulez accomplir avec des variables statiques dans les classes dérivées. Personnellement, je pense que vous allez loin pour essayer d'éviter une instance var.
Pourquoi pas simplement la manière classique?
abstract class AbstractBase
{
protected string _Description = "I am boring abstract default value";
}
class Foo : AbstractBase {
public Foo() {
_Description = "I am foo!";
}
}
Ce n'est pas statique s'il doit être appelé sur une instance.
Si vous ne l'appelez pas sur une instance, il n'y a pas de polymorphisme en jeu (C'est-à-dire ChildA.Description est complètement sans rapport avec ChildB.Description en ce qui concerne la langue).
Une solution de contournement possible consiste à définir un Singleton de votre classe dérivée dans votre classe de base à l'aide de génériques.
import System;
public abstract class AbstractBase<T>
where T : AbstractBase<T>, new()
{
private static T _instance = new T();
public abstract string Description { get; }
public static string GetDescription()
{
return _instance.Description;
}
}
public class DerivedClass : AbstractBase<DerivedClass>
{
public override string Description => "This is the derived Class";
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(DerivedClass.GetDescription());
Console.ReadKey();
}
}
L'astuce consiste à indiquer à votre AbstractBase<T>
quelques détails sur la façon dont DerivedClass
est implémenté:
- Il est newable avec
where T: new()
donc il peut créer une instance Singleton - il dérive de lui-même avec
where T : AbstractBase<T>
donc il sait qu'il y aura une implémentation deDescription
De cette façon _instance
contient le champ Description
qui peut être appelé dans le méthode statique GetDescription()
.
Cela vous oblige à écraser Description
dans votre DerivedClass
et vous permet d'appeler sa valeur avec DerivedClass.GetDescription()
Vous pouvez faire en sorte que la méthode de base "abstraite" lance un Exception
, alors un développeur est "averti" s'il essaie d'invoquer cette méthode sur une classe enfant sans surcharger.
L'inconvénient est que l'on peut étendre la classe et ne pas utiliser cette méthode. Reportez-vous ensuite aux autres réponses fournies.