if语句中的赋值

我有一个类Animal ,它的子类Dog 。 我经常发现自己编写以下代码:

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

对于变量Animal animal;

是否有一些语法允许我写如下:

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

以下答案是几年前写的,随着时间的推移而更新。 从C#7开始,您可以使用模式匹配:

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

请注意, dog仍然在if语句之后的范围内,但未明确分配。


不,没有。 尽管如此写它更惯用:

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

鉴于“as if by if”几乎总是以这种方式使用,因此有一个操作员可以一次执行两个部分更有意义。 如果实现了模式匹配提议 ,这目前不在C#6中,但可能是C#7的一部分。

问题是你不能在if语句1的条件部分声明一个变量。 我能想到的最接近的方法是:

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

这只是令人讨厌的 ……(我刚试过它,它确实有效。但是请不要这样做。哦,你可以用var来声明dog 。)

当然你可以写一个扩展方法:

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

然后用:

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

或者,您可以将两者结合起来:

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

您还可以使用没有lambda表达式的扩展方法,而不是for循环:

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

然后:

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

1您可以在if语句中指定值,但我很少这样做。 这与声明变量不同。 虽然在阅读数据流时,我在一段while做这件事并不是特别不寻常。 例如:

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

这些天我通常更喜欢使用一个包装器,它允许我使用foreach (string line in ...)但我将上面视为一个非常惯用的模式。 在一个条件下产生副作用通常并不好,但替代方案通常涉及代码重复,当你知道这种模式时,很容易做到正确。

如果失败,则返回null

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

只要变量已存在, 就可以将值赋给变量。 您还可以对变量进行范围调整,以允许稍后在同一方法中再次使用该变量名称(如果这是一个问题)。

 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(); // ... } } } } 

假设

 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"); } } 

得到输出:

 Name is now Scopey Flying 

当从流中读取字节块时,也会使用测试中的变量赋值模式,例如:

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

然而,上面使用的变量范围的模式并不是一个特别常见的代码模式,如果我看到它被遍地使用,我会寻找一种方法来重构它。

是否有一些语法允许我写如下:

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

可能会出现在C#6.0中。 此function称为“声明表达式”。 看到

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

详情。

建议的语法是:

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

更一般地,所提出的特征是局部变量声明可以用作表达式 。 这个if语法只是更一般特性的一个很好的结果。

我发现自己经常编写和使用的一种扩展方法是

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

在这种情况下可以使用哪种方式

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

然后name是狗的名字(如果是狗),否则为null。

*我不知道这是否有效。 它从未成为分析的瓶颈。

在这里反对谷物,但也许你首先做错了。 检查对象的类型几乎总是代码味道。 在您的示例中,不是所有动物都有名字吗? 然后只需调用Animal.name,而不检查它是否是狗。

或者,反转方法,以便在Animal上调用一个方法,该方法根据Animal的具体类型执行不同的操作。 另见:多态性。

更短的声明

 var dog = animal as Dog if(dog != null) dog.Name ...; 

这里有一些额外的脏代码(不像Jon那样脏,但是:-))依赖于修改基类。 我认为它捕获了意图,而可能忽略了这一点:

 class Animal { public Animal() { Name = "animal"; } public List IfIs() { if(this is T) return new List{this}; else return new List(); } 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()) { Console.WriteLine(dog.Name); Console.WriteLine(dog.Bark); } Console.ReadLine(); } } 

如果必须一个接一个地执行多个这样的as-ifs(并且不能使用多态),请考虑使用SwitchOnType构造。

问题(使用语法) 不是赋值,因为C#中的赋值运算符是一个有效的表达式。 相反,它是所需的声明,因为声明是声明。

如果我必须编写类似的代码,我有时会(根据更大的上下文)编写如下代码:

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

上述语法有优点(接近请求的语法),因为:

  1. if 之外使用dog会导致编译错误,因为它没有在其他地方分配值。 (也就是说, 不要在别处分配dog 。)
  2. 这种方法也可以很好地扩展到if/else if/... (只需要选择合适的分支所需的数量; 这是我必须以这种forms写它的大案例 。)
  3. 避免重复is/as 。 (但也用Dog dog = ...表格完成。)
  4. 与“惯用时”没有什么不同。 (只是不要忘掉:以一致的forms保持条件而且简单。)

要真正将dog与世界其他地方隔离,可以使用新的块:

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

快乐的编码。

另一种带扩展方法的EVIL解决方案:)

 public class Tester { public static void Test() { Animal a = new Animal(); //nothing is printed foreach (Dog d in a.Each()) { Console.WriteLine(d.Name); } Dog dd = new Dog(); //dog ID is printed foreach (Dog dog in dd.Each()) { 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 Each(this object Source) where T : class { T t = Source as T; if (t == null) yield break; yield return t; } } 

我个人更喜欢干净的方式:

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

if语句不会允许,但for循环会。

例如

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

如果它的工作方式不是很明显,那么这里是对该过程的逐步解释:

  • 可变狗被创建为类型狗并且被分配给施加给狗的变量动物。
  • 如果赋值失败则dog为null,这会阻止for循环的内容运行,因为它会立即被中断。
  • 如果赋值成功,则for循环遍历
    迭代。
  • 在迭代结束时,dog变量被赋值为null,从而突破for循环。
 using(Dog dog = animal as Dog) { if(dog != null) { dog.Name; ... } } 

IDK如果这对任何人有帮助,但你总是可以尝试使用TryParse来分配你的变量。 这是一个例子:

 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; } 

变量将在if语句之前声明。

我刚刚编写了if语句来创建一行看起来像你感兴趣的代码。它只是帮助压缩代码一些,我发现它更易读,特别是在嵌套赋值时:

 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); } }