C# - affectation dans une déclaration if

j'ai une classe Animal , et sa sous-classe Dog . Je me retrouve souvent à coder les lignes suivantes:

if (animal is Dog)
{
    Dog dog = animal as Dog;    
    dog.Name;    
    ... 
}

pour la variable Animal animal; .

y a-t-il une syntaxe qui me permette d'écrire quelque chose comme:

if (Dog dog = animal as Dog)
{    
    dog.Name;    
    ... 
}
121
demandé sur dcharles 2011-08-18 23:57:22

15 réponses

la réponse ci-dessous a été écrite il y a des années et mise à jour au fil du temps. À partir de C# 7, vous pouvez utiliser la correspondance de modèle:

if (animal is Dog dog)
{
    // Use dog here
}

notez que dog est toujours dans la portée après l'énoncé if , mais n'est pas attribué de façon définitive.


Non, il n'y en a pas. Il est plus idiomatique d'écrire ceci cependant:

Dog dog = animal as Dog;
if (dog != null)
{
    // Use dog
}

étant Donné que "le suivie si" presque toujours utilisé de cette façon, il pourrait être plus logique d'avoir un opérateur qui exécute les deux parties en une seule fois. Ceci n'est pas actuellement dans C# 6, mais peut faire partie de C# 7, si le proposition de correspondance de modèle est mis en œuvre.

le problème est que vous ne pouvez pas déclarer une variable dans la partie de la condition d'un if déclaration 1 . L'approche la plus proche à laquelle je peux penser est celle-ci:

// EVIL EVIL EVIL. DO NOT USE.
for (Dog dog = animal as Dog; dog != null; dog = null)
{
    ...
}

C'est juste nasty ... (Je l'ai juste essayé, et ça fonctionne. Mais s'il vous plaît, s'il vous plaît ne pas le faire. OH, et vous pouvez déclarer dog en utilisant var bien sûr.)

bien sûr, vous pouvez écrire une méthode d'extension:

public static void AsIf<T>(this object value, Action<T> action) where T : class
{
    T t = value as T;
    if (t != null)
    {
        action(t);
    }
}

alors appelez - le avec:

animal.AsIf<Dog>(dog => {
    // Use dog in here
});

alternativement, vous pouvez combiner les deux:

public static void AsIf<T>(this object value, Action<T> action) where T : class
{
    // EVIL EVIL EVIL
    for (var t = value as T; t != null; t = null)
    {
        action(t);
    }
}

You peut également utiliser une méthode d'extension sans expression lambda d'une manière plus propre que la boucle for:

public static IEnumerable<T> AsOrEmpty(this object value)
{
    T t = value as T;
    if (t != null)
    {
        yield return t;
    }
}

puis:

foreach (Dog dog in animal.AsOrEmpty<Dog>())
{
    // use dog
}

1 vous pouvez assigner valeurs dans if déclarations, bien que je le fasse rarement. Ce n'est pas la même chose que déclarer des variables. Ce n'est pas terriblement inhabituel pour moi de le faire dans un while bien que lors de la lecture des flux de données. Par exemple:

string line;
while ((line = reader.ReadLine()) != null)
{
    ...
}

ces jours - ci, je préfère normalement utiliser un emballage qui me permet d'utiliser foreach (string line in ...) mais je vois ce qui précède comme un motif assez idiomatique. C'est habituellement pas agréable d'avoir des effets secondaires dans une condition, mais les alternatives impliquent généralement la duplication de code, et quand vous savez ce modèle, il est facile d'obtenir le droit.

281
répondu Jon Skeet 2017-07-15 07:20:04

si as échoue, il retourne null .

Dog dog = animal as Dog;

if (dog != null)
{
    // do stuff
}
47
répondu Platinum Azure 2011-08-18 19:58:50

Vous peut affecter la valeur de la variable, tant que la variable existe déjà. Vous pouvez également la portée de la variable cette variable nom pour être utilisé plus tard dans la même méthode, si c'est un problème.

public void Test()
{
    var animals = new Animal[] { new Dog(), new Duck() };

    foreach (var animal in animals)
    {
        {   // <-- scopes the existence of critter to this block
            Dog critter;
            if (null != (critter = animal as Dog))
            {
                critter.Name = "Scopey";
                // ...
            }
        }

        {
            Duck critter;
            if (null != (critter = animal as Duck))
            {
                critter.Fly();
                // ...
            }
        }
    }
}

supposant

public class Animal
{
}

public class Dog : Animal
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            Console.WriteLine("Name is now " + _name);
        }
    }
}

public class Duck : Animal
{
    public void Fly()
    {
        Console.WriteLine("Flying");
    }
}

obtient la sortie:

Name is now Scopey
Flying

le schéma d'assignation des variables dans le test est également utilisé pour lire les blocs de bytes des flux, pour exemple:

int bytesRead = 0;
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0) 
{
    // ...
}

le schéma de la portée variable utilisé ci-dessus, cependant, n'est pas un schéma de code particulièrement commun et si je l'ai vu être utilisé partout je serais à la recherche d'un moyen de le reformater.

11
répondu Handcraftsman 2011-08-18 21:54:23

y a-t-il une syntaxe qui me permette d'écrire quelque chose comme:

if (Dog dog = animal as Dog) { ... dog ... }

?

il y en aura probablement dans C# 6.0. Cette caractéristique est appelée "expressions de déclaration". Voir

https://roslyn.codeplex.com/discussions/565640

pour plus de détails.

la syntaxe proposée est:

if ((var i = o as int?) != null) { … i … }
else if ((var s = o as string) != null) { … s … }
else if ...

plus généralement, la caractéristique proposée est que une déclaration de variable locale peut être utilisée comme expression . Cette syntaxe if n'est qu'une belle conséquence de la caractéristique plus générale.

11
répondu Eric Lippert 2014-09-16 15:53:40

L'une des méthodes d'extension que j'écris et utilise souvent* est

public static TResult IfNotNull<T,TResult>(this T obj, Func<T,TResult> func)
{
    if(obj != null)
    {
        return func(obj);
    }
    return default(TResult);
}

Qui pourrait être utilisé dans cette situation comme

string name = (animal as Dog).IfNotNull(x => x.Name);

puis name est le nom du chien (si c'est un chien), sinon null.

*je n'ai aucune idée si c'est performant. Il n'y a jamais eu de goulot d'étranglement dans le profilage.

9
répondu Greg 2011-08-19 06:14:59

va à l'encontre du grain ici, mais peut-être que vous le faites mal en premier lieu. Vérifier le type d'objet est presque toujours une odeur de code. Tous les animaux, dans votre exemple, n'ont-ils pas un nom? Alors appelle Animal.nom, sans vérifier si c'est un chien ou pas.

alternativement, inverser la méthode de sorte que vous appelez une méthode sur L'Animal qui fait quelque chose différemment selon le type de béton de l'Animal. Voir aussi: le Polymorphisme.

5
répondu fwielstra 2011-08-22 11:45:44

Déclaration Abrégée

var dog = animal as Dog
if(dog != null) dog.Name ...;
4
répondu jmogera 2011-08-18 20:13:08

voici un peu plus de code sale (pas aussi sale que celui de Jon, cependant :-)) dépendant de la modification de la classe de base. Je pense qu'il saisit l'intention tout en peut-être manquer le point:

class Animal
{
    public Animal() { Name = "animal";  }
    public List<Animal> IfIs<T>()
    {
        if(this is T)
            return new List<Animal>{this};
        else
            return new List<Animal>();
    }
    public string Name;
}

class Dog : Animal
{
    public Dog() { Name = "dog";  }
    public string Bark { get { return "ruff"; } }
}


class Program
{
    static void Main(string[] args)
    {
        var animal = new Animal();

        foreach(Dog dog in animal.IfIs<Dog>())
        {
            Console.WriteLine(dog.Name);
            Console.WriteLine(dog.Bark);
        }
        Console.ReadLine();
    }
}
3
répondu James Ashley 2011-08-18 21:41:56

si vous devez faire plusieurs tels que-ifs un après l'autre (et l'utilisation du polymorphisme n'est pas une option), envisagez d'utiliser un SwitchOnType construire.

3
répondu Omer Raviv 2017-05-23 12:34:45

le problème (avec la syntaxe) est et non avec l'assignation, car l'opérateur d'assignation en C# est une expression valide. Il s'agit plutôt de avec la déclaration désirée car les déclarations sont des déclarations.

Si je dois écrire du code comme ça, je vais parfois (selon le contexte) écrire le code comme ceci:

Dog dog;
if ((dog = animal as Dog) != null) {
    // use dog
}

il y a des avantages avec la syntaxe ci-dessus (qui est proche à la syntaxe demandée) parce que:

  1. en utilisant dog en dehors de le if résultera en une erreur de compilation car il n'est pas assigné de valeur ailleurs. (C'est-à-dire, ne pas attribuer dog ailleurs.)
  2. cette approche peut aussi être étendue à if/else if/... (il n'y a qu'autant de as qu'il faut pour choisir une branche appropriée; c'est la grande case où je l'écris sous cette forme quand je dois.)
  3. évite la duplication de is/as . (Mais aussi fait avec le formulaire Dog dog = ... .)
  4. n'Est pas différent de "idiomatiques tout". (Ne vous laissez pas emporter: conservez le conditionnel dans une forme cohérente et simple.)

pour vraiment isoler dog du reste du monde un nouveau bloc peut être utilisé:

{
  Dog dog = ...; // or assign in `if` as per above
}
Bite(dog); // oops! can't access dog from above

Heureux de codage.

3
répondu 2011-09-19 22:22:30

un Autre MAL de la solution avec les méthodes d'extension :)

public class Tester
{
    public static void Test()
    {
        Animal a = new Animal();

        //nothing is printed
        foreach (Dog d in a.Each<Dog>())
        {
            Console.WriteLine(d.Name);
        }

        Dog dd = new Dog();

        //dog ID is printed
        foreach (Dog dog in dd.Each<Dog>())
        {
            Console.WriteLine(dog.ID);
        }
    }
}

public class Animal
{
    public Animal()
    {
        Console.WriteLine("Animal constructued:" + this.ID);
    }

    private string _id { get; set; }

    public string ID { get { return _id ?? (_id = Guid.NewGuid().ToString());} }

    public bool IsAlive { get; set; }
}

public class Dog : Animal 
{
    public Dog() : base() { }

    public string Name { get; set; }
}

public static class ObjectExtensions
{
    public static IEnumerable<T> Each<T>(this object Source)
        where T : class
    {
        T t = Source as T;

        if (t == null)
            yield break;

        yield return t;
    }
}

je préfère personnellement la voie propre:

Dog dog = animal as Dog;

if (dog != null)
{
    // do stuff
}
0
répondu Stefan Michev 2014-09-16 17:00:01

une instruction if ne permettra pas cela, mais une instruction for loop le permettra.

p.ex.

for (Dog dog = animal as Dog; dog != null; dog = null)
{
    dog.Name;    
    ... 
}

En cas la façon dont il travaille, n'est pas immédiatement évident, alors voici une explication étape par étape du processus:

  • chien Variable est créé comme chien de type et attribué l'animal variable qu'en est jeté aux Chiens.
  • si la tâche échoue alors chien est nul, ce qui empêche le contenu de la boucle de courir, parce qu'il est immédiatement éclaté de.
  • si la cession réussit alors la boucle for passe par le

    itération.
  • à la fin de l'itération, la variable dog se voit attribuer une valeur de null, qui sort de la boucle for.
0
répondu Knickerless-Noggins 2015-06-15 11:18:31
using(Dog dog = animal as Dog)
{
    if(dog != null)
    {
        dog.Name;    
        ... 

    }

}
0
répondu Knickerless-Noggins 2015-06-15 11:27:49

IDK si cela aide quelqu'un mais vous pouvez toujours essayer d'utiliser un TryParse pour assigner votre variable. Voici un exemple:

if (int.TryParse(Add(Value1, Value2).ToString(), out total))
        {
            Console.WriteLine("I was able to parse your value to: " + total);
        } else
        {
            Console.WriteLine("Couldn't Parse Value");
        }


        Console.ReadLine();
    }

    static int Add(int value1, int value2)
    {
        return value1 + value2;
    }

la variable total sera déclarée avant votre état if.

0
répondu Alejandro Garcia 2015-12-10 05:34:41

je viens inline si l'instruction pour créer une ligne de code qui ressemble à ce qui vous intéresse. Il aide juste à compresser le code un peu et je l'ai trouvé pour être plus lisible surtout quand les tâches de nidification:

var dog = animal as Dog; if (dog != null)
{
    Console.WriteLine("Parent Dog Name = " + dog.name);

    var purebred = dog.Puppy as Purebred; if (purebred != null)
    {
         Console.WriteLine("Purebred Puppy Name = " + purebred.Name);
    }

    var mutt = dog.Puppy as Mongrel; if (mutt != null)
    {
         Console.WriteLine("Mongrel Puppy Name = " + mutt.Name);
    }
 }
0
répondu user1689175 2018-08-26 21:21:30