2010-11-16 5 views
7
protected static new void WhyIsThisValidCode() 
{ 
} 

Pourquoi êtes-vous autorisé à remplacer les méthodes statiques? Rien que des bugs peuvent en découler, ça ne marche pas comme on pourrait le penser.Pourquoi la méthode statique prioritaire est-elle activée dans C#

Prenez les classes suivantes.

class BaseLogger 
{ 
    protected static string LogName { get { return null; } } 

    public static void Log(string message) { Logger.Log(message, LogName); } 
} 

class SpecificLogger : BaseLogger 
{ 
    protected static string LogName { get { return "Specific"; } } 
} 

cela est alowed, et le code

SpecificLogger.Log("test"); 

est altso alowed, mais il ne fait pas ce que vous pensez en regardant le code. Il appelle Logger.Log avec LogName = null.

Alors pourquoi est-ce autorisé?

+11

Comme d'autres l'ont dit, cela ne remplace pas - et il fait * exactement * comme je l'attendrais. S'il vous plaît, ne supposez pas que tout le monde pense de la même manière que vous. –

Répondre

24

Le mot-clé new ne remplace pas une méthode. Il crée à la place une nouvelle méthode du même nom qui est indépendante de l'original. Il n'est pas possible de remplacer une méthode statique car elle n'est pas virtuelle.

+1

Je manque parfois des méthodes de classe virtuelle en C#. Mais ils ne sont probablement pas assez utiles pour justifier de les ajouter à la langue. – CodesInChaos

15

Vous ne le surchargez pas, vous le cachez. Une méthode normale présenterait exactement le même comportement, de sorte qu'il n'y a rien de spécifique aux méthodes statiques ici.

Le masquage n'est utile que dans certains cas. Celui que j'ai rencontré le plus souvent rend le type de retour plus spécifique dans une classe dérivée. Mais je n'ai jamais eu cela avec des méthodes statiques. Un domaine où les fonctions statiques avec un certain nom peut être utile est si vous utilisez la réflexion et que vous voulez obtenir des informations sur chaque classe en le renvoyant d'une méthode. Mais bien sûr, dans la plupart des cas, un attribut correspond mieux.

Et il est peu probable de créer des bugs depuis votre code produit un compilateur d'avertissement:

Avertissement « StaticHiding.SpecificLogger.LogName » cache hérité membre « StaticHiding.BaseLogger.LogName ». Utilisez le nouveau mot-clé si la cache était destinée.

Et si vous utilisez new vous devriez savoir ce que vous faites.

2

Les méthodes et les champs statiques n'appartiennent pas aux instances de classe, mais aux définitions de classe. Les méthodes statiques ne jouent pas de rôle dans le mécanisme de répartition virtuelle et ne font pas partie de la table de méthode virtuelle.

Ce ne sont que des méthodes et des champs sur cette classe spécifique. Il peut sembler que les méthodes et les champs sont "hérités" parce que vous pouvez faire SpecificLogger.Log(), mais c'est juste quelque chose pour vous éviter de devoir vous référer à la classe de base tout le temps.

Les méthodes et les champs statiques ne sont vraiment que des méthodes et des champs globaux, juste le type OO.

0

Vous ne remplacez pas la propriété dans la classe de base, mais la masquez. La propriété réelle utilisée lors de l'exécution dépend de l'interface sur laquelle vous travaillez.L'exemple suivant illustre:

SpecificLogger a = new SpecificLogger(); 
BaseLogger b = new SpecificLogger(); 

Console.Write(a.Log); // Specific 
Console.Write(b.Log); // null 

Dans votre code la méthode Log travaille actuellement sur l'interface BaseLogger - parce que la méthode Log fait partie de la classe BaseLogger.

Les méthodes et les propriétés statiques ne peuvent pas être remplacées et lorsque vous souhaitez masquer une propriété, vous devez utiliser le mot-clé new pour indiquer que vous cachez quelque chose.

9

D'autres ont souligné que cela ne déroge pas, mais cela laisse toujours votre question initiale: pourquoi êtes-vous capable de le faire? (Mais la question est vraiment "pourquoi pouvez-vous masquer les méthodes statiques".)

Il s'agit d'une caractéristique inévitable de la prise en charge du versionnement indépendant du composant qui contient des classes de base et des composants qui utilisent ces classes de base. Par exemple, imaginons que le composant CompB contient la classe de base et qu'un autre composant CompD contienne une classe dérivée. Dans la version 1 de CompB, il n'y aurait peut-être pas eu de propriété appelée LogName. L'auteur de CompD décide d'ajouter une propriété statique appelée LogName. La chose essentielle à comprendre à ce stade est que l'auteur de v1 de CompD n'avait pas l'intention de remplacer ou de cacher une caractéristique de la classe de base - il n'y avait aucun membre appelé LogName dans la classe de base quand ils ont écrit ce code.

Imaginez maintenant qu'une nouvelle version de la bibliothèque CompB soit publiée. Dans cette nouvelle version, l'auteur a ajouté une propriété LogName. Qu'est-ce qui est censé se passer dans CompD? Les options semblent être:

  1. Compd ne fonctionne plus parce que le LogName il introduit des affrontements avec le LogName ajouté à CompB
  2. D'une certaine façon faire LogName du Compd remplacer la base CompB LogName. Traitez les deux membres du LogName comme étant des membres complètement différents qui ont le même nom. (En réalité, ils ne s'appellent pas - ils s'appellent BaseLogger.LogName et SpecificLogger.LogName, mais comme en C# nous n'avons pas toujours besoin de qualifier le nom du membre avec la classe, on dirait qu'ils ont le même nom.)

.NET choisit de le faire 3. (et il le fait avec les deux et non statique Si vous voulez statics comportement 2 -. remplacement - avec non-statique, la base doit être virtualet la classe dérivée doit marquer la méthode comme override pour indiquer qu'ils ont volontairement surchargé une méthode dans la classe de base C# ne fera jamais remplacer la méthode d'une classe dérivée par une méthode dérivée à moins que la classe dérivée ait explicitement déclaré que c'est ce qu'ils voulu.) Ceci est susceptible d'être sûr parce que les deux membres sont non ed - le LogName de base n'existait même pas au point où le dérivé a été introduit. Et c'est préférable de simplement casser parce que la dernière version de la classe de base a introduit un nouveau membre.

Sans cette fonctionnalité, il serait impossible pour les nouvelles versions de .NET Framework d'ajouter de nouveaux membres aux classes de base existantes sans que cela ne constitue une rupture.

Vous dites que le comportement n'est pas ce que vous attendez. En fait, c'est exactement ce à quoi je m'attendais, et ce que vous voudriez probablement en pratique. Le BaseLogger n'a aucune idée que le SpecificLogger a introduit sa propre propriété LogName. (Il n'y a aucun mécanisme par lequel il pourrait parce que vous ne pouvez pas substituer des méthodes statiques.) Et quand l'auteur de SpecificLogger a écrit cette propriété LogName, rappelez-vous qu'ils écrivaient contre v1 de BaseLogger qui n'avait pas de LogName, donc ils n'avaient pas l'intention de remplacer la méthode de base non plus. Puisque aucune classe ne veut le remplacement, clairement le remplacement serait la mauvaise chose.

Le seul scénario dans lequel vous devriez vous retrouver dans cette situation est que les deux classes sont dans des composants différents. (Évidemment, vous pouvez concevoir un scénario quand ils sont dans le même composant, mais pourquoi le feriez-vous un jour? Si vous possédez les deux morceaux de code et les libérez dans un seul composant, il serait fou de le faire.) Et BaseLogger devrait donc avoir sa propre propriété LogName, ce qui est exactement ce qui se passe. Vous avez peut-être écrit:

SpecificLogger.Log("test"); 

mais le compilateur C# voit que SpecificLogger ne fournit pas une méthode Log, il transforme cela en:

BaseLogger.Log("test"); 

parce que c'est où la méthode Log est définie.

Chaque fois que vous définissez une méthode dans une classe dérivée qui ne tente pas de remplacer une méthode existante, le compilateur C# l'indique dans les métadonnées. (Il y a un paramètre "newslot" dans les métadonnées de la méthode qui indique que cette méthode est nouvelle, sans rapport avec quoi que ce soit dans la classe de base.)

Mais cela vous pose un problème si vous voulez recompiler CompD. Supposons que vous ayez un rapport de bogue dû à un bit de code totalement différent et que vous ayez besoin de lancer une nouvelle version de CompD. Vous le compilez contre la nouvelle version de CompB. Si le code que vous avez écrit n'était pas autorisé, vous ne seriez pas en mesure de - l'ancien code déjà compilé fonctionnerait, mais vous ne seriez pas capable de compiler de nouvelles versions de ce code, ce qui serait un peu fou . Et ainsi, pour soutenir ce scénario (franchement quelque peu obscur), ils vous permettent de le faire. Ils génèrent un avertissement pour vous informer que vous avez un conflit de nom ici. Vous devez fournir le mot-clé new pour vous en débarrasser. Ceci est un scénario obscur, mais si vous voulez prendre en charge l'héritage à travers les limites des composants, vous en avez besoin, sinon l'ajout de nouveaux membres publics ou protégés sur une classe de base serait invariablement un changement de rupture. C'est pourquoi c'est ici. Mais c'est une mauvaise habitude de s'en remettre, d'où le fait que vous recevez un avertissement du compilateur. L'utilisation du mot-clé new pour se débarrasser de l'avertissement ne devrait jamais être qu'un palliatif. La ligne de fond est la suivante: cette fonctionnalité existe pour une raison seulement, et c'est pour vous sortir d'un trou dans les situations où une nouvelle version d'une classe de base a ajouté un membre qui n'existait pas auparavant, et qui des conflits avec un membre qui est déjà sur votre classe dérivée. Si ce n'est pas le cas, n'utilisez pas cette fonctionnalité.

(je pense qu'ils devraient effectivement émettre une erreur plutôt que d'un avertissement lorsque vous excluez nouveau, pour le rendre plus clair.)

0

pour ma grande surprise, le code suivant est permis et compile sans erreur sur .net Framework 4.5.1, VS 2013.

class A 
{ 
    public static void Foo() 
    { 
    } 
} 

class B : A 
{ 

} 

class Program 
{ 
    static void main(string[] args) 
    { 
     B.Foo(); 
    } 
} 
Questions connexes