if语句中的赋值

mic*_*ael 133 c# casting if-statement

我有一个类Animal及其子类Dog.我经常发现自己编写以下代码:

if (animal is Dog)
{
    Dog dog = animal as Dog;    
    dog.Name;    
    ... 
}
Run Code Online (Sandbox Code Playgroud)

对于变量Animal animal;.

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

if (Dog dog = animal as Dog)
{    
    dog.Name;    
    ... 
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 302

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

if (animal is Dog dog)
{
    // Use dog here
}
Run Code Online (Sandbox Code Playgroud)

请注意,dogif语句后仍在范围内,但未明确分配.


不,没有.写这个是更惯用的:

Dog dog = animal as Dog;
if (dog != null)
{
    // Use dog
}
Run Code Online (Sandbox Code Playgroud)

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

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

// EVIL EVIL EVIL. DO NOT USE.
for (Dog dog = animal as Dog; dog != null; dog = null)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

这只是令人讨厌的 ......(我刚试过它,它确实有用.但请不要这样做.哦,你当然可以宣布dog使用var.)

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

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

然后用:

animal.AsIf<Dog>(dog => {
    // Use dog in here
});
Run Code Online (Sandbox Code Playgroud)

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

您还可以使用没有lambda表达式的扩展方法,比for循环更简洁:

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

然后:

foreach (Dog dog in animal.AsOrEmpty<Dog>())
{
    // use dog
}
Run Code Online (Sandbox Code Playgroud)

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

string line;
while ((line = reader.ReadLine()) != null)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

这些天我通常更喜欢使用一个让我使用的包装,foreach (string line in ...)但我认为上面是一个非常惯用的模式.这通常不是很好,有一个条件中的副作用,但替代品通常涉及重复代码,当你知道这种模式很容易得到的权利.

  • 这是我曾经见过的最好的C#滥用行为.显然你是一个邪恶的天才. (96认同)
  • +1给出答案,并乞求OP不使用它.即时经典. (75认同)
  • 我前一段时间做了一个类似的扩展方法(带有一堆重载),我把它们称为"AsEither(...)",我认为它比`AsIf(...)`更清晰,所以我可以写` myAnimal.AsEither(dog => dog.Woof(),cat => cat.Meeow(),unicorn => unicorn.ShitRainbows())`. (18认同)
  • @Paul:我认为这可能是"EVIL EVIL EVIL"背后的动机,但我并不积极. (12认同)
  • @Paul:如果我试图向任何人出售*,我不会强烈建议他们不要使用它.我只是展示了什么*可能*. (8认同)
  • @Jon:关于这个问题的可怕之处在于,我实际上可以想象有人在生产代码中使用它. (7认同)
  • @Eric:我感到非常荣幸 - 我只能想象团队有时必须提出的可怕的事情:) (4认同)
  • 给JonI的+1 Jon; 来自非官方粉丝俱乐部!当然,现在我将不得不用Wayne的World变量命名方案重写我的一些生产代码,这样我就可以找到一种方法:`schyeah.AsIf`:D(哇,@ Eric和Jon你们两个在同一时间![嗯.]无论如何,只是不要太靠近彼此 - 不要让你的代表迅速批评!:P)`isExcellent = xpert [0] .AsIf < JonSkeet>(j => j.partyOn)&& xpert [1] .AsIf <EricLippert>(e => e.partyOn());`:D♡ (4认同)
  • @jonp如果你想永远循环,当然. (3认同)
  • 可以编写一个`AsIf`扩展(在`object`上),将目标类型作为类型参数,并将主体的`Action <T>`参数封装为`as`转换和null检查.(`IfIs`可能是一个更准确,虽然不那么有趣的名字.) (2认同)
  • @Jons - 可以说你可以用`狗狗; if((dog = animal as Dog)!= null){...}`.你仍然需要两条线来做到这一点; 所以你不妨去清楚. (2认同)

Pla*_*ure 48

如果as失败,则返回null.

Dog dog = animal as Dog;

if (dog != null)
{
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)


Han*_*man 12

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

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();
                // ...
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

假设

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

得到输出:

Name is now Scopey
Flying
Run Code Online (Sandbox Code Playgroud)

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

int bytesRead = 0;
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0) 
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

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


Eri*_*ert 11

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

if (Dog dog = animal as Dog) { ... dog ... }
Run Code Online (Sandbox Code Playgroud)

可能会出现在C#6.0中.此功能称为"声明表达式".看到

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 ...
Run Code Online (Sandbox Code Playgroud)

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

  • @asawyer:首先,这是一个非常频繁的请求功能.其次,其他语言的扩展名为"if"; 例如,gcc允许C++中的等价物.第三,正如我所指出的,这个特征比"if"更为通用.第四,C#中有一种趋势,因为C#3.0使得越来越多需要语句上下文的东西需要表达式上下文; 这有助于功能式编程.有关详细信息,请参阅语言设计说明. (3认同)
  • @asawyer:不客气!如果您有更多评论,请随时参与Roslyn.codeplex.com上的讨论.此外,我还要补充一点:第五,新的Roslyn基础设施降低了执行团队执行这些小型实验性功能的边际成本,这意味着"减去100"点的幅度会降低.该团队正借此机会探索长期要求的完美体面的小功能,但从未超过-100点障碍. (2认同)
  • 这些评论的读者如果对我们所谈论的“点”感到困惑,应该阅读前 C# 设计师 Eric Gunnerson 关于此主题的博客文章:http://blogs.msdn.com/b/ericgu/archive/2004/01/ 12/57985.aspx。这是一个类比;没有计算实际的“点数”。 (2认同)

Gre*_*reg 9

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

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

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

string name = (animal as Dog).IfNotNull(x => x.Name);
Run Code Online (Sandbox Code Playgroud)

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

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

  • +1的注释.如果它从未成为分析的瓶颈,那么这是一个非常好的迹象,表明它具有足够的*性能. (2认同)

fwi*_*tra 5

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

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