comment étiqueter Eric Lippert ?
Si vous souhaitez porter quelque chose à mon attention, vous pouvez utiliser le lien "contact" sur mon blog.
Je ne comprends toujours pas pourquoi le compilateur C# se donne la peine de vérifier les collisions entre les espaces de noms et les types dans des contextes où l'existence d'un espace de noms n'a aucun sens.
En effet, les règles ici sont délicates. Par coïncidence, il y a deux semaines, j'ai rédigé et publié sur mon blog une série d'articles sur certaines de ces questions étroitement liées à ce sujet ; ils seront mis en ligne début mars. Surveillez le blog pour plus de détails.
UPDATE : Les articles mentionnés ci-dessus sont ici :
http://blogs.msdn.com/b/ericlippert/archive/tags/namespaces/
Pourquoi cette erreur se produit-elle ?
Permettez-moi de reformuler la question en plusieurs questions.
Quelles sections du cahier des charges justifient la production de cette erreur ?
Je pense que cela a déjà été couvert de manière satisfaisante dans d'autres réponses. L'algorithme de résolution de type est extrêmement bien spécifié. Mais juste pour résumer : être à l'intérieur de quelque chose du bon nom "se lie plus étroitement" que en utilisant quelque chose du bon nom de la à l'extérieur de . Quand tu dis :
using XYZ;
namespace ABC.DEF
{
class GHI : DEF { }
}
qui est le même que
using XYZ;
namespace ABC
{
namespace DEF
{
class GHI : DEF { }
}
}
Nous devons donc maintenant déterminer la signification du DEF. Nous allons de l'intérieur vers l'extérieur. Y a-t-il un paramètre de type de GHI appelé DEF ? Non. Regardez le conteneur. Y a-t-il un membre de DEF qui s'appelle DEF ? Non. Regardez le conteneur. Y a-t-il un membre de ABC appelé DEF ? OUI . Nous avons terminé ; nous avons déterminé la signification de DEF, c'est un espace de nom. Nous découvrons la signification de DEF avant nous demandons "est-ce que XYZ a un membre DEF ?"
Quels principes de conception influencent cette conception ?
L'un des principes de conception est le suivant : "les noms ont la même signification, quelle que soit la façon dont vous les utilisez". Le langage n'obéit pas à 100% à ce principe ; il existe des situations dans lesquelles le même nom peut être utilisé pour faire référence à deux choses différentes dans le même code. Mais en général, nous nous efforçons de faire en sorte que lorsque vous voyez "Foo" deux fois dans le même contexte, cela signifie la même chose. (Voir mon article sur Le problème de la couleur pour quelques détails sur ce sujet, ainsi que mes articles sur identifier les violations des règles de "nom simple .)
Un principe de conception est "pas de retour en arrière". Nous ne disons jamais en C# "Je vois que vous avez utilisé un nom pour faire référence à quelque chose qui n'est pas légal dans ce contexte. Laissez-moi abandonner le résultat de la liaison par nom et recommencer, en cherchant quelque chose qui pourrait fonctionner."
Un principe plus large qui sous-tend le principe "pas de retour en arrière" est que C# n'est pas un langage "devinez ce que l'utilisateur voulait dire". Vous avez écrit un programme où la meilleure liaison possible d'un identifiant identifiait un espace de nom alors qu'un type était attendu. Il y a deux possibilités. Première possibilité : vous avez commis une erreur dont vous voulez être informé afin que le programme puisse être exécuté. vous peut prendre des mesures pour le corriger. Deuxième possibilité : vous avez voulu que ce soit une liaison moins bonne qui soit choisie, et nous devons donc devinez parmi toutes les moins bonnes fixations possibles pour trouver celle que vous vouliez probablement dire.
Il s'agit d'un bon principe de conception dans des langages comme JScript -- JScript permet de se débrouiller lorsque le développeur fait quelque chose de fou. C# n'est pas ce genre de langage ; le retour que nous recevons de nos développeurs est clair et net. me dire quand quelque chose est cassé pour que je puisse le réparer .
L'avantage de ne pas revenir en arrière est que cela rend le langage beaucoup plus facile à comprendre. Supposons que vous ayez quelque chose comme ce désordre :
namespace XYZ.DEF
{
public class GHI {}
}
namespace QRS.DEF.GHI
{
public class JKL { }
}
...
using QRS;
namespace TUV
{
using XYZ;
namespace ABC
{
namespace DEF
{
class GHI { }
class MNO : DEF.GHI.JKL { }
}
}
}
Déterminez le type de base de MNO. Sans retour en arrière, nous disons "DEF est ABC.DEF". Donc GHI est ABC.DEF.GHI. Donc JKL est ABC.DEF.GHI.JKL, qui n'existe pas, erreur. Vous devez corriger l'erreur en donnant un nom de type qui permet au compilateur d'identifier quel DEF vous vouliez dire.
Si nous avions des retours en arrière, qu'aurions-nous à faire ? Nous obtenons cette erreur, et ensuite nous revenons en arrière. Est-ce que XYZ contient un DEF ? Oui. Est-ce qu'il contient un GHI ? Oui. Contient-il un JKL ? Non. Revenez en arrière. Est-ce que le QRS contient un DEF.GHI.JKL ? Oui.
Ce travaux mais peut-on logiquement conclure du fait qu'il fonctionne que c'est celui que l'utilisateur a choisi ? signifiait ?
Qui peut bien le savoir dans cette folle siutation ? Nous avons eu toutes sortes de bonnes fixations qui ont ensuite mal tourné très tard dans le jeu. L'idée que nous soyons tombés sur la réponse souhaitée après avoir emprunté de nombreuses voies sans issue semble très suspecte.
La bonne chose à faire ici n'est pas de revenir en arrière plusieurs fois et d'essayer toutes sortes de liaisons plus mauvaises pour chaque étape de la recherche. La bonne chose à faire est de dire "mon pote, la meilleure correspondance possible pour cette recherche donne des résultats absurdes ; donnez-moi quelque chose de moins ambigu pour travailler ici s'il vous plaît".
Un fait malheureux concernant l'écriture d'un langage où le compilateur par le design se plaint bruyamment si la meilleure correspondance est quelque chose qui ne fonctionne pas, c'est que les développeurs disent souvent "bien sûr, en général Je veux que le compilateur me signale toutes mes erreurs - ou, plutôt, toutes les erreurs de mes collègues. Mais pour ce spécifique Je sais ce que je fais, alors s'il te plaît, compilateur, faire ce que je veux, pas ce que je dis ."
Le problème, c'est qu'on ne peut pas avoir le beurre et l'argent du beurre. Vous ne pouvez pas avoir à la fois un compilateur qui applique des règles rigides qui rendent très probable l'identification agressive d'un code suspect comme erroné et permettre un code fou par le biais de l'heuristique du compilateur qui détermine "ce que je voulais vraiment dire" lorsque vous écrivez quelque chose que le compilateur considère à juste titre comme ambigu ou erroné.
Pour voir comment de nombreux développeurs professionnels n'aiment pas du tout les effets d'une conception de langage qui identifie agressivement les erreurs plutôt que de supposer que le développeur a voulu que le pire résultat soit choisi, voir les 116 commentaires de cet article sur un aspect mineur et plutôt sans importance de la résolution des surcharges. :
(Notez que je ne réponds plus aux commentaires sur cette question ; j'ai expliqué ma position plus de dix fois. Si toutes ces explications ne sont pas convaincantes, c'est que je ne suis pas un très bon convaincant).
Et enfin, si vous voulez vraiment tester votre compréhension du fonctionnement des règles de résolution de noms en C#, essayez ce petit puzzle . Presque tout le monde se trompe, ou se trompe pour de mauvaises raisons. La réponse est aquí .