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;
...
}
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.
si as
échoue, il retourne null
.
Dog dog = animal as Dog;
if (dog != null)
{
// do stuff
}
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.
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.
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.
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.
Déclaration Abrégée
var dog = animal as Dog
if(dog != null) dog.Name ...;
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();
}
}
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.
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:
- en utilisant
dog
en dehors de leif
résultera en une erreur de compilation car il n'est pas assigné de valeur ailleurs. (C'est-à-dire, ne pas attribuerdog
ailleurs.) - cette approche peut aussi être étendue à
if/else if/...
(il n'y a qu'autant deas
qu'il faut pour choisir une branche appropriée; c'est la grande case où je l'écris sous cette forme quand je dois.) - évite la duplication de
is/as
. (Mais aussi fait avec le formulaireDog dog = ...
.) - 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.
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
}
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.
using(Dog dog = animal as Dog)
{
if(dog != null)
{
dog.Name;
...
}
}
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.
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);
}
}