45 votes

CompanyName.Foo' est un 'espace de nom' mais est utilisé comme un 'type'.

Reformulation de la question

Je ressuscite cette question parce que je suis tombé sur cette erreur aujourd'hui, et je ne comprends toujours pas pourquoi le compilateur C# s'embête à 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.

Si j'ai...

public Foo MyFoo { get; set; }

...pourquoi le compilateur se soucierait-il de ce que Foo est à la fois un espace de nom et un type ? Peut-on déclarer une propriété comme un espace de nom au lieu d'un type ?

Quelle est la logique derrière l'erreur de compilation "namespace used like type" ? De quel problème cela me préserve-t-il ?

[Et comment étiqueter Eric Lippert ? :)]


Question originale


Le problème

J'ai un projet "Foo" avec un espace de nom par défaut. CompanyName.Foo . J'ai une base de données qui s'appelle aussi "Foo".

Et quand je lance SqlMetal.exe sur la base de données, il génère une classe CompanyName.Foo.Models.Foo .

Ensuite, lorsque j'essaie de créer une propriété avec cette classe comme type, comme ceci...

using CompanyName.Foo.Models;
...
public Foo DataContext { get; set; }

...je reçois l'erreur :

CompanyName.Foo " est un " espace de nom " mais est utilisé comme un " type ".

Je suis obligé de faire...

public CompanyName.Foo.Models.Foo Foo { get; set; } // :-(

Questions :

  1. Pourquoi cette erreur se produit-elle ? Ma déclaration de propriété ne contient pas CompanyName Alors pourquoi est-ce un problème ? C'est simple : Foo != CompanyName.Foo . Aussi, juste pour être sûr, j'ai fait une recherche de ma solution entière pour namespace Foo et je n'ai trouvé aucun résultat (si j'avais utilisé un espace de nommage Foo je pourrais comprendre que l'on obtienne une erreur).

  2. [répondu] Y a-t-il un moyen de contourner la qualification complète Foo à chaque fois que je veux l'utiliser ?

  3. [répondu] Y a-t-il un moyen de faire en sorte que SqlMetal nomme la classe autrement que Foo (sans changer le nom de ma base de données) ? Je peux modifier l'espace de noms à l'aide d'un commutateur, mais je ne connais pas de moyen de modifier le nom de la classe proprement dite.

Mise à jour

Je cherche toujours une réponse à la question (1).

O.K.W. a cloué (2) & (3).

Usages

Une demande a été faite pour que tous mes using déclarations :

using System;
using System.ComponentModel;
using System.Data.Linq;
using System.Linq;
using MyCompany.Foo.Models;

72voto

Eric Lippert Points 300275

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í .

11voto

o.k.w Points 15721
  1. Le conflit se situe entre l'espace de nom CompanyName.Foo y CompanyName.Foo.Models.Foo et non Foo . Je ne sais pas exactement comment/pourquoi le compilateur ne peut pas distinguer les deux.

  2. Vous pouvez essayer d'utiliser alias de l'espace de nom pour raccourcir la qualification complète Foo
    par exemple using coyModels = CompanyName.Foo.Models

  3. Desde el référence il semble que vous puissiez utiliser /context:<type> y /namespace:<name> pour spécifier la classe du contexte de données (au lieu d'utiliser le nom de la table) et l'espace de noms.

5voto

Igor Zevaka Points 32586

Le compilateur C# ne compile pas quand il y a une ambiguïté entre une classe et un espace de nom avec le même nom. Malheureusement, vous devez simplement nommer la classe explicitement ou renommer la base de données. Dans votre cas, le compilateur n'est même pas arrivé au conflit, il est mort après avoir résolu Foo comme un espace de nom.

Chaque fois que vous avez quelque chose comme ça :

using CompanyName.Foo.Models;

namespace CompanyName.Foo {
    class Test {
        public Foo Model { get; set; } // error CS0118: 'CompanyName.Foo' is a 'namespace' but is used like a 'type'
        public Foo1 Model { get; set; } //OK
    }
}

namespace CompanyName.Foo.Models {
    class Foo1 {
    }
    class Foo {
    }
}

Ce qui se passe en fait, c'est que chaque niveau précédent de l'espace de noms est implicitement importé à chaque niveau. Cela est logique puisque la syntaxe des espaces de noms imbriqués utilisant le point est la même que celle des espaces de noms imbriqués :

namespace CompanyName {
    using CompanyName; //<--using1 - Implicit using, since we should be able to access anything within CompanyName implicitly.
    namespace Foo {
        using CompanyName.Foo; //<-- using2 Same as above
        class Test {
            public Foo Model { get; set; } //At this stage due to using1 Foo is actually CompanyName.Foo, hence the compiler error
        }
    }
}

Donc, à l'intérieur de la classe Test, il y a deux implicites using s :

using CompanyName;
using CompanyName.Foo;

Par conséquent, Foo est résolu dans l'espace de nom, d'où l'erreur.

EDIT Bon point. J'ai trouvé ça dans MSDN :

La signification d'un espace-nom ou d'un nom de type est déterminée comme suit :

  • Si l'espace de noms ou le nom de type consiste en un seul identifiant :

    • Si le nom de l'espace de noms ou du type apparaît à l'intérieur de le corps d'une classe ou d'une structure alors, à partir de cette déclaration de déclaration de classe ou de structure et en continuant avec chaque déclaration de classe ou struct englobante (le cas échéant), si un membre membre portant le nom donné existe, est accessible, et qu'il désigne un type, alors l'espace de nom ou le nom de type fait référence à ce membre. Notez que les membres non typés (constantes, champs, méthodes, propriétés, indexeurs, opérateurs, constructeurs et destructeurs d'instance, et constructeurs statiques) sont ignorés lors de la détermination de la signification d'un nom d'espace-nom ou de type.

    • Sinon, en commençant par l'espace de nom dans lequel le fichier nom d'espace de noms ou de type se produit, puis avec chaque (s'il y en a un), et se terminant par l'espace de noms global, les étapes suivantes suivantes sont évaluées jusqu'à ce qu'une entité soit localisée :

    • Si l'espace de noms contient un membre de l'espace de noms avec le nom donné nom alors l'espace de noms ou le nom de type fait référence à ce membre et, selon membre, est classé comme un espace de noms ou un type.

    • Sinon, si l'espace de noms a un correspondant qui englobe l'emplacement où se trouve le nom d'espace de noms ou de type, alors :

    • Si la déclaration de l'espace de noms contient une directive using-alias qui associe le nom donné avec un espace de nom ou un importé, alors l'espace de noms ou le type nom d'espace de noms ou de type se réfère à ce cet espace de noms ou ce type.

    • Sinon, si les espaces de noms importés par l'option using-namespace-directives de la déclaration d'espace de noms contient exactement un type avec le nom donné, alors le nom d'espace de noms ou de type fait référence à ce type.
      ...

(Les caractères gras sont les miens) Cela signifie que lorsque l'on résout Foo en le comparant à CompanyName.Foo (premier bit en gras) se produit avant de le comparer à la base de données des using directive (deuxième construction en gras).

1voto

alpha Points 147

Pourquoi tu ne peux pas juste faire

using CompanyName.Foo; 
... 
public Models.Foo DataContext { get; set; }

1voto

David Marchelya Points 960

Ce problème est apparu lorsque j'ai référencé une classe dans une bibliothèque de classes distincte, dont le type portait le même nom que la racine de l'espace de noms. Initialement, lorsque je faisais référence à ce type dans un projet d'application console séparé, il n'y avait aucun problème, tout se compilait bien. Cependant, la référence à partir d'un projet de service Windows générait le message suivant is a 'namespace' but is used like a 'type'. message. Il s'est avéré que le projet de service Windows avait son cadre cible défini sur ".NET Framework 4 Client Profile". En remplaçant ce paramètre par ".NET Framework 4", l'erreur a disparu. J'espère que cela aidera quelqu'un dans une situation similaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X