【问题标题】:What's the benefit of var patterns in C#7?C#7 中的 var 模式有什么好处?
【发布时间】:2018-02-23 11:01:42
【问题描述】:

我不明白 C#7 中 var 模式的用例。 MSDN:

var 模式的模式匹配总是成功的。它的语法是

expr is var varname

其中 expr 的值总是分配给一个名为的局部变量 varnamevarname 是与expr 相同类型的静态变量。

我认为 MSDN 上的示例毫无用处,尤其是因为 if 是多余的:

object[] items = { new Book("The Tempest"), new Person("John") };
foreach (var item in items) {
  if (item is var obj)
    Console.WriteLine($"Type: {obj.GetType().Name}, Value: {obj}"); 
}

在这里我看不到任何好处,如果您直接访问也是Object 类型的循环变量item,您可能会有同样的好处。 if 也令人困惑,因为它从来都不是 false

我可以直接使用var otherItem = itemitem。 有人可以更好地解释用例吗?

【问题讨论】:

  • IMO 这必须被视为 is type 模式的扩展,如果 typevar
  • 可以用来引入临时变量:if (s.FirstOrDefault(o => o != null) is var v && int.TryParse(v, out var n))
  • @Dennis_E 这是一个很好的例子,为什么我要避免这种模式。看起来它阻止了 TryParse 方法中的空引用异常,但它并没有因为模式总是成功。将var 替换为实际类型,例如string 会改变这种行为,如果不是说不好的话,这真的是违反直觉的。

标签: c# pattern-matching c#-7.0


【解决方案1】:

var 模式C# language repository 中被非常频繁地讨论,因为它的用例并不完全清楚,并且考虑到 is var x 不执行空检查而 @987654326 @ 确实如此,使它显得相当无用。

但是,它实际上并不打算用作obj is var x。它适用于左侧不是本身的变量时使用。

以下是来自the specification 的一些示例。它们都使用了 C# 中没有的特性,但这只是表明 var 模式的引入主要是为这些事情做准备,所以他们以后不必再接触它。

以下示例声明了一个函数Deriv,以在表达式树上使用结构模式匹配构造函数的the derivative

Expr Deriv(Expr e)
{
    switch (e) {
        // …
        case Const(_): return Const(0);
        case Add(var Left, var Right):
            return Add(Deriv(Left), Deriv(Right));
        // …
}

在这里,var 模式可以在结构内部使用以从结构中“拉出”元素。同样,以下示例简化了表达式:

Expr Simplify(Expr e)
{
    switch (e) {
        case Mult(Const(0), _): return Const(0);
        // …
        case Add(Const(0), var x): return Simplify(x);
    }
}

作为gafter writes here,这个想法也是有属性模式匹配,允许以下:

if (o is Point {X is 3, Y is var y})
{ … }

【讨论】:

  • 那些是 C# 8 记录和表达式,还不能使用。这很烦人
  • 是的,不过我们会到达那里的 :)
【解决方案2】:

如果不检查 Github 上的设计说明,我猜这是为了与 switch 保持一致而添加的更多内容,并作为更高级模式匹配案例的垫脚石,

来自原始What’s New in C# 7.0 帖子:

var x 形式的 Var 模式(其中 x 是一个标识符),它始终匹配,并且只需将输入的值放入与输入具有相同类型的新变量 x 中。

还有 Sergey Teplyakov 最近的 dissection post

如果您知道到底发生了什么,您可能会发现这种模式很有用。它可用于在表达式中引入临时变量: 这种模式本质上是使用对象的实际类型创建一个临时变量。

public void VarPattern(IEnumerable<string> s)
{
    if (s.FirstOrDefault(o => o != null) is var v
        && int.TryParse(v, out var n))
    {
        Console.WriteLine(n);
    }
}

sn-p 之前的警告也很重要:

尚不清楚为什么仅在发布模式下行为不同。但我认为所有问题都属于同一个问题:该功能的初始实现并不理想。但根据 Neal Gafter 的评论,这将发生变化:“模式匹配降低代码正在从头开始重写(也支持递归模式)。我希望您在这里寻求的大部分改进都是“免费的” “在新代码中。但是要等到重写准备好迎接黄金时间。”。

根据Christian Nagel

优点是var关键字声明的变量是对象的真实类型,

【讨论】:

  • 谢谢。但我不明白最后一句话(Christian Nagel)。如果我使用itemObject,那么item.GetType().Name 将返回相同的结果(Book 而不是Object)。所以我还是不明白它的优点。如果不将其转换为该类型,我仍然无法使用 Book 成员。
  • x is var v 中,v 的类型始终是静态类型,如x。所以当它是object x = new Book() 时,那么v 也将是object,而不是Book。该链接帖子中的示例很糟糕,因为x.GetType() 将始终返回实际的对象类型,并且与变量的静态类型无关。
  • 这个例子好像坏了,s.FirstOrDefault(o =&gt; o != null) is var v 永远为真,v 最终可能为空。 s.FirstOrDefault(o =&gt; o != null) is var vs.FirstOrDefault(o =&gt; o != null) is string v相同,这是非常不幸的:/
【解决方案3】:

我只能临时想到的是,如果您发现自己编写了两个相同的代码块(比如说一个 switch),一个用于expr is object a,另一个用于expr is null

您可以通过切换到expr is var a来组合块。

它也可能在代码生成场景中很有用,无论出于何种原因,您已经将自己写到一个角落并且总是希望生成一个模式匹配但现在想要发出一个“匹配所有”模式。

【讨论】:

    【解决方案4】:

    在大多数情况下确实如此,var 模式的好处并不清楚,甚至可能是个坏主意。然而,作为在临时变量中捕获匿名类型的一种方式,它效果很好。 希望这个例子可以说明这一点: 注意下面,添加一个空大小写可以避免 var 永远为空,并且不需要进行空检查。

            var sample = new(int id, string name, int age)[] { 
                                                              (1, "jonas", 50),                                                                                                                            
                                                              (2, "frank", 48) };
    
            var f48 = from s in sample 
                      where s.age == 48 
                      select new { Name = s.name, Age = s.age };
    
            switch(f48.FirstOrDefault())
            {
                case var choosen when choosen.Name == "frank":
                    WriteLine(choosen.Age);
                    break;
                case null:
                    WriteLine("not found");
                    break;
            }
    

    【讨论】:

      猜你喜欢
      • 2018-01-11
      • 2018-02-01
      • 2014-02-25
      • 1970-01-01
      • 2019-08-25
      • 2023-04-05
      • 2012-03-15
      • 1970-01-01
      • 2013-12-31
      相关资源
      最近更新 更多