Assignment in an if statement
我有一个类
1 2 3 4 5 6 |
对于变量
是否有一些语法允许我编写如下内容:
1 2 3 4 5 | if (Dog dog = animal as Dog) { dog.Name; ... } |
下面的答案是几年前写的,并随着时间的推移而更新。从C 7开始,您可以使用模式匹配:
1 2 3 4 |
注意,
不,没有。不过写这个更习惯:
1 2 3 4 5 | Dog dog = animal as Dog; if (dog != null) { // Use dog } |
考虑到"as followed by if"几乎总是用这种方式使用的,所以有一个运算符可以一次执行两个部分,这可能更有意义。这目前不在C 6中,但如果实现模式匹配建议,它可能是C 7的一部分。
问题是,不能在
1 2 3 4 5 | // EVIL EVIL EVIL. DO NOT USE. for (Dog dog = animal as Dog; dog != null; dog = null) { ... } |
真是太恶心了…(我刚试过,它确实管用。但请不要这样做。哦,你可以用
当然,您可以编写一个扩展方法:
1 2 3 4 5 6 7 8 | public static void AsIf<T>(this object value, Action<T> action) where T : class { T t = value as T; if (t != null) { action(t); } } |
然后打电话给:
1 2 3 | animal.AsIf<Dog>(dog => { // Use dog in here }); |
或者,您可以将这两者结合起来:
1 2 3 4 5 6 7 8 | 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); } } |
也可以使用不带lambda表达式的扩展方法,而不是使用for循环:
1 2 3 4 5 6 7 8 | public static IEnumerable<T> AsOrEmpty(this object value) { T t = value as T; if (t != null) { yield return t; } } |
然后:
1 2 3 4 | foreach (Dog dog in animal.AsOrEmpty<Dog>()) { // use dog } |
1您可以在
1 2 3 4 5 | string line; while ((line = reader.ReadLine()) != null) { ... } |
现在,我通常更喜欢使用一个包装器,它允许我使用
如果
1 2 3 4 5 6 | Dog dog = animal as Dog; if (dog != null) { // do stuff } |
只要变量已经存在,就可以将值赋给变量。如果这是一个问题,您也可以在变量范围内允许该变量名稍后在同一方法中再次使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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(); // ... } } } } |
假设
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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"); } } |
获取输出:
1 2 | Name is now Scopey Flying |
当从流中读取字节块时,也使用测试中的变量分配模式,例如:
1 2 3 4 5 | int bytesRead = 0; while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0) { // ... } |
然而,上面使用的变量范围模式并不是一个特别常见的代码模式,如果我看到它被广泛使用,我会寻找一种方法来重构它。
Is there some syntax that allows me to write something like:
1 | if (Dog dog = animal as Dog) { ... dog ... } |
?
C 6.0中可能有。此功能称为"声明表达式"。见
https://roslyn.codeplex.com/discussions/565640
详情。
建议的语法是:
1 2 3 | if ((var i = o as int?) != null) { … i … } else if ((var s = o as string) != null) { … s … } else if ... |
更一般地说,建议的特性是局部变量声明可以用作表达式。这种
我发现自己经常编写和使用的扩展方法之一是
1 2 3 4 5 6 7 8 | public static TResult IfNotNull<T,TResult>(this T obj, Func<T,TResult> func) { if(obj != null) { return func(obj); } return default(TResult); } |
在这种情况下可以使用
1 | string name = (animal as Dog).IfNotNull(x => x.Name); |
然后,
*我不知道这是不是在表演。它从来没有成为分析方面的瓶颈。
在这里违背了原则,但也许你一开始就做错事了。检查对象的类型几乎总是一种代码味道。在你的例子中,不是所有的动物都有名字吗?那就叫animal.name,不看是不是狗。
或者,反转该方法,以便在动物上调用根据动物的具体类型执行不同操作的方法。另请参见:多态性。
简短的陈述
1 2 | var dog = animal as Dog if(dog != null) dog.Name ...; |
下面是一些依赖于修改基类的额外的脏代码(不像jon的那样脏,尽管:-)。我认为它抓住了意图,但可能忽略了要点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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(); } } |
问题(语法)不在赋值中,因为C中的赋值运算符是有效的表达式。相反,它使用所需的声明作为声明。
如果我必须这样写代码,我有时会(取决于更大的上下文)这样写代码:
1 2 3 4 | Dog dog; if ((dog = animal as Dog) != null) { // use dog } |
上述语法(接近请求的语法)有一些优点,因为:
要真正将
1 2 3 4 | { Dog dog = ...; // or assign in `if` as per above } Bite(dog); // oops! can't access dog from above |
快乐编码。
如果您必须执行多个操作,例如一个接一个地执行ifs(并且不能选择使用多态性),请考虑使用switchontype构造。
你可以用那样的东西
//声明变量布尔温度=假;
1 2 3 | if (previousRows.Count > 0 || (temp= GetAnyThing())) { } |
另一个有扩展方法的邪恶解决方案:)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | 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; } } |
我个人更喜欢干净的方式:
1 2 3 4 5 6 | Dog dog = animal as Dog; if (dog != null) { // do stuff } |
if语句不允许这样做,但for循环允许这样做。
例如
1 2 3 4 5 | for (Dog dog = animal as Dog; dog != null; dog = null) { dog.Name; ... } |
如果它的工作方式不是很明显,那么下面是对过程的逐步解释:
- 变量狗被创建为类型狗并分配变量动物那是给狗扔的。
- 如果分配失败,则dog为空,这将阻止内容因为for循环立即中断的。
- 如果分配成功,则for循环将通过迭代。
- 在迭代结束时,dog变量被赋值为空,它从for循环中断。
1 2 3 4 5 6 7 8 9 10 | using(Dog dog = animal as Dog) { if(dog != null) { dog.Name; ... } } |
IDK如果这对任何人都有帮助,但您可以尝试使用一个台盼来分配您的变量。下面是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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; } |
total变量将在if语句之前声明。
我刚刚内联了if语句来创建一行看起来像您感兴趣的代码。它只是帮助压缩一些代码,我发现它更具可读性,尤其是在嵌套分配时:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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); } } |