C# – 在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 } 

注意dogif语句之后仍然在范围内,但是没有明确的分配。


不,没有。 虽然写这个比较习惯:

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

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

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

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

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

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

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

然后用以下方式调用它

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

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

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

您也可以使用不带lambdaexpression式的扩展方法,而不是使用for循环:

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

然后:

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

1你可以在if语句中赋值 ,尽pipe我很less这样做。 这不同于声明variables。 尽pipe在阅读数据stream的时候,暂时这样做并不奇怪。 例如:

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

这些天我通常喜欢使用一个包装,它让我使用foreach (string line in ...)但我认为上述是一个很好的习惯模式。 在一个条件下有副作用通常是不好的,但是替代方法通常涉及代码重复,而当你知道这个模式时,很容易就可以得到正确的答案。

如果失败,则返回null

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

只要variables已经存在,您就可以将值赋给variables。 如果这是一个问题,您也可以范围variables来允许稍后在相同的方法中使用该variables名称。

 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 

当从stream中读取字节块时,还会使用testing中的variables赋值模式,例如:

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

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

有没有一些语法可以让我写下如下的东西:

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

有可能会在C#6.0中。 这个特性被称为“声明expression式”。 看到

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

了解详情。

build议的语法是:

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

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

我发现自己常常使用的扩展方法之一就是

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

这可以用在这种情况下

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

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

*我不知道这是否是高性能的。 它从来没有成为剖析的瓶颈。

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

或者,反转该方法,以便根据动物的具体types调用Animal上的方法来做不同的事情。 另见:多态性。

较短的陈述

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

这里有一些额外的脏代码(不像Jon的那么脏,尽pipe:-))依赖于修改基类。 我认为它抓住了意图,也许错过了这一点:

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

如果你必须一个接一个(如果使用多态并不是一个选项),那么考虑使用一个SwitchOnType构造。

这个问题(与语法有关) 不在赋值中,因为C#中的赋值运算符是一个有效的expression式。 相反,它是与所需的声明作为声明是陈述。

如果我必须写这样的代码,我会有时(取决于更大的上下文)写这样的代码:

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

上述语法(接近所请求的语法)有以下优点:

  1. if 之外使用dog会导致编译错误,因为在别处没有赋值。 (也就是说在别处指定dog
  2. 这种方法也可以很好地扩展到if/else if/... (只有select一个合适的分支所需的数量是多less; 这是我必须以这种forms写入的大案例 )。
  3. 避免重复is/as 。 (但也做Dog dog = ...forms。)
  4. 和“惯用的时候”没有什么不同。 (只是不要被带走:保持一致的forms和简单的条件。)

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

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

快乐的编码。

另一个扩展方法的EVIL解决scheme:)

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

我个人比较喜欢干净的方式:

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

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

例如

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

如果它的工作方式不是立即明显的,那么这里是对这个过程的一步一步的解释:

  • variables狗被创build为types狗,并将赋予该variables的variables动物分配给狗。
  • 如果赋值失败,那么dog为空,这会阻止for循环的内容运行,因为它立即被分解出来。
  • 如果赋值成功,则for循环遍历整个
    迭代。
  • 在迭代结束时,dogvariables被分配一个null值,从而跳出for循环。
 using(Dog dog = animal as Dog) { if(dog != null) { dog.Name; ... } } 

IDK,如果这有助于任何人,但你总是可以尝试使用TryParse分配你的variables。 这里是一个例子:

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

variables将在if语句之前声明。