C #: mot-clé 'is' et vérification de Not

287

C'est une question stupide, mais vous pouvez utiliser ce code pour vérifier si quelque chose est d'un type particulier ...

if (child is IContainer) { //....

Existe-t-il un moyen plus élégant de vérifier l'instance "NOT"?

if (!(child is IContainer)) { //A little ugly... silly, yes I know...

//these don't work :)
if (child !is IContainer) {
if (child isnt IContainer) { 
if (child aint IContainer) { 
if (child isnotafreaking IContainer) { 

Oui, oui ... question idiote ....

Parce qu'il y a une question sur l'apparence du code, c'est juste un simple retour au début d'une méthode.

public void Update(DocumentPart part) {
    part.Update();
    if (!(DocumentPart is IContainer)) { return; }
    foreach(DocumentPart child in ((IContainer)part).Children) {
       //...etc...
Hugoware
la source
105
Personnellement, j'aime "l'enfant ne parle pas ...". Je vote pour que ce mot-clé soit mis en C # 5
Joseph
Je suis intéressé de connaître la situation que vous utiliseriez? À quoi ressemble la partie "else" de ce code et ne pouvez-vous pas simplement inverser le test? Si votre code dit "si l'enfant n'est pas un IContainer, alors lancez des exceptions" ou "si l'enfant n'est pas un IContainer alors c'est peut-être un IFoo alors j'essaierai cela ensuite" alors n'y a-t-il pas une instruction else implicite? Il me manque probablement quelque chose.
Martin Peck
1
@MartinPeck, il pourrait ne pas y avoir de clause else. C'est la raison pour laquelle j'ai cherché cela.
Joshua Walsh
@MartinPeck, voici un exemple: if (!(argument is MapsControlViewModel vm)) { return; }- Je pourrais inverser le if et mettre le reste de la méthode dans les crochets if, mais j'obtiendrais alors le code de l'arbre de Noël, avec beaucoup de crochets de fermeture à la fin de la méthode. C'est beaucoup moins lisible.
ANeves
peut-être que ce dont nous avons besoin en général, ce sont des ifnotdéclarations
Dave Cousineau

Réponses:

301
if(!(child is IContainer))

est le seul opérateur à aller (il n'y a pas d' IsNotopérateur).

Vous pouvez créer une méthode d'extension qui le fait:

public static bool IsA<T>(this object obj) {
    return obj is T;
}

puis l'utiliser pour:

if (!child.IsA<IContainer>())

Et vous pouvez suivre votre thème:

public static bool IsNotAFreaking<T>(this object obj) {
    return !(obj is T);
}

if (child.IsNotAFreaking<IContainer>()) { // ...

Mise à jour (compte tenu de l'extrait de code de l'OP):

Puisque vous effectuez en fait un cast après la valeur, vous pouvez simplement utiliser à la asplace:

public void Update(DocumentPart part) {
    part.Update();
    IContainer containerPart = part as IContainer;
    if(containerPart == null) return;
    foreach(DocumentPart child in containerPart.Children) { // omit the cast.
       //...etc...
Mehrdad Afshari
la source
1
ck: Je voulais dire dans le sens des opérateurs, il n'y a IsNotrien.
Mehrdad Afshari
5
Oui. Je plaisante au cas où ce ne serait pas évident.
Mehrdad Afshari
111

Vous pouvez le faire de cette façon:

object a = new StreamWriter("c:\\temp\\test.txt");

if (a is TextReader == false)
{
   Console.WriteLine("failed");
}
cjk
la source
2
@Frank - oui, le mot clé is donne un booléen, que vous pouvez comparer à false
cjk
32
@Frank cela fonctionne car isa une priorité plus élevée par rapport à ==. La seule raison pour laquelle vous ne pouvez pas l'utiliser !x is fest qu'elle a moins de priorité que !.
Mehrdad Afshari
J'aime ça, mais cela ne semble pas fonctionner correctement lors de l'introduction d'une variable, même si cela devrait. if (a is TextReader reader == false)"devrait" fonctionner, mais il ne vous permettra pas d'utiliser la variable dans le vrai chemin en disant qu'elle n'a peut-être pas été initialisée.
Dave Cousineau
@DaveCousineau - Normalement, vous devez vérifier et introduire une variable lorsque vous souhaitez utiliser la variable introduite. Je ne sais pas en quoi la variable serait utile si la vérification de type échouait. (Avertissement - Je trouve que la fonction "Pattern Matching" est à la fois mal nommée et aussi mauvaise qu'une odeur de code que l'utilisation de outparamètres)
StingyJack
@StingyJack, il y a une sorte de problème où, dans le vrai chemin , la variable est considérée comme non initialisée. même si vous dites if (a is TextReader reader == true)qu'il pense que la variable n'est pas initialisée.
Dave Cousineau
11

Pourquoi ne pas simplement utiliser le reste?

if (child is IContainer)
{
  //
}
else
{
  // Do what you want here
}

Son propre familier et simple?

Mark Broadhurst
la source
3
Rien de mal à cela - ce n'est qu'une question de choix. Je voulais quitter immédiatement une fonction si quelque chose n'était pas d'un type particulier. Je l'ai fait (! (L'enfant est quelque chose)) pour toujours maintenant, mais je pensais que je m'assurerais qu'il n'y avait pas de meilleure façon.
Hugoware
1
Avec l'exemple de code dans la question, cela signifierait un support if vide. Cela ne semble pas être une alternative sensée.
ANeves
9

La façon dont vous l'avez est très bien, mais vous pouvez créer un ensemble de méthodes d'extension pour créer "un moyen plus élégant de vérifier l'instance" NOT "".

public static bool Is<T>(this object myObject)
{
    return (myObject is T);
}

public static bool IsNot<T>(this object myObject)
{
    return !(myObject is T);
}

Ensuite, vous pourriez écrire:

if (child.IsNot<IContainer>())
{
    // child is not an IContainer
}
Robert Cartaino
la source
7

Cela n'a pas encore été mentionné. Ça marche et je pense que ça a l'air mieux que d'utiliser!(child is IContainer)

if (part is IContainer is false)
{
    return;
}

issyntaxe expr is constant :, où expr est l'expression à évaluer et constante est la valeur à tester.

Todd Skelton
la source
3
De même, vous pourriez le faire if (part as IContainer is null). Honnêtement, je ne sais pas ce qui est mieux.
Flynn1179
5

Laid? Je ne suis pas d'accord. La seule autre façon (je pense personnellement que c'est "plus laid"):

var obj = child as IContainer;
if(obj == null)
{
   //child "aint" IContainer
}
BFree
la source
@Mehrdad - Nullable? lui permettrait de fonctionner, pas que cela devrait être utilisé. Ce n'est qu'un exemple d'une manière plus laide.
stevehipwell
@ Steveo3000: Oui, mais vous devez le mentionner explicitement? est la asclause. obj as intest toujours une erreur de temps de compilation.
Mehrdad Afshari
@Mehrdad - D'accord, BFree pourrait modifier son message pour refléter cela. Nous donnant «obj as int?».
stevehipwell
@ Stevo3000: Cependant, je ne pense pas que quelque chose cloche. IContainer ressemble à une interface plutôt qu'à un type de valeur. Je voulais juste souligner qu'il nécessite une attention particulière au type de valeur et n'est pas toujours une traduction directe de la isforme.
Mehrdad Afshari
vous pourriez éventuellement faire si (obj == default (IContainer)), qui prendrait en charge les types de valeur et les types de référence
Joseph
3

L' isopérateur évalue à un résultat booléen, vous pouvez donc faire tout ce que vous pourriez autrement faire sur un booléen. Pour le nier, utilisez l' !opérateur. Pourquoi voudriez-vous avoir un opérateur différent juste pour cela?

Brian Rasmussen
la source
5
Ce n'est pas un opérateur différent. Je me demandais s'il y avait un mot clé qui me permettrait de supprimer l'ensemble supplémentaire de parens. C'est un gros coup de pioche, mais j'étais curieux.
Hugoware
Ok, je comprends. D'après vos exemples, j'ai eu l'impression que vous recherchiez un nouvel opérateur dédié.
Brian Rasmussen,
Je pense qu'avoir un tel opérateur spécial est mauvais, car nous aurons cette façon (expliqué cela ans, de toute façon), et si nous avions un autre op, alors il y a deux façons de réaliser la même chose, peut être déroutant.
BuddhiP
3

La méthode d'extension IsNot<T>est un bon moyen d'étendre la syntaxe. Gardez à l'esprit

var container = child as IContainer;
if(container != null)
{
  // do something w/ contianer
}

fonctionne mieux que de faire quelque chose comme

if(child is IContainer)
{
  var container = child as IContainer;
  // do something w/ container
}

Dans votre cas, peu importe que vous reveniez de la méthode. En d'autres termes, veillez à ne pas effectuer à la fois la vérification de type et la conversion de type immédiatement après.

Jeff
la source
3

Bien que cela n'évite pas le problème des parenthèses, pour le bien des gens qui arrivent ici via Google, il convient de mentionner qu'une syntaxe plus récente existe (à partir de C # 7) pour rendre le reste de votre code un peu plus propre:

if (!(DocumentPart is IContainer container)) { return; }
foreach(DocumentPart child in container.Children) {
    ...

Cela évite la double conversion, la vérification nulle et la disponibilité d'une variable dans les étendues où elle pourrait être nulle.

StriplingWarrior
la source
2

Bien que l'opérateur IS soit normalement le meilleur moyen, il existe une alternative que vous pouvez utiliser dans certaines circonstances. Vous pouvez utiliser l'opérateur as et tester null.

MyClass mc = foo as MyClass;
if ( mc == null ) { }
else {}
Muad'Dib
la source
2

C # 9 (à paraître avec .NET 5) inclura les modèles logiques and, oret not, ce qui nous permet d'écrire ceci plus élégamment:

if (child is not IContainer) { ... }

De même, ce modèle peut être utilisé pour vérifier la valeur null:

if (child is not null) { ... }

Vous pouvez trouver plus de détails sur le problème de Github qui suit ce changement.

Thorkil Holm-Jacobsen
la source
-2
if (child is IContainer ? false : true)
Ternaire
la source