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 }
注意dog
在if
语句之后仍然在范围内,但是没有明确的分配。
不,没有。 虽然写这个比较习惯:
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 }
上述语法(接近所请求的语法)有以下优点:
- 在
if
之外使用dog
会导致编译错误,因为在别处没有赋值。 (也就是说别在别处指定dog
) - 这种方法也可以很好地扩展到
if/else if/...
(只有select一个合适的分支所需的数量是多less; 这是我必须以这种forms写入的大案例 )。 - 避免重复
is/as
。 (但也做Dog dog = ...
forms。) - 和“惯用的时候”没有什么不同。 (只是不要被带走:保持一致的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语句之前声明。