祝兴科 發表於 2020-9-30 11:26:00

C# 中的 is 真的是越来越强大,越来越语义化

<h2 id="一背景">一:背景</h2>
<h3 id="1-讲故事">1. 讲故事</h3>
<p>最近发现 C#7 之后的 is 是越来越看不懂了,乍一看花里胡哨的,不过当我静下心来仔细研读,发现这 is 是越来越短小精悍,而且还特别语义化,那怎是一个爽字了得😄,这一篇就和大家简单聊一聊。</p>
<h2 id="二c7-之前的-is-如何使用">二:C#7 之前的 is 如何使用</h2>
<h3 id="1-类型兼容性检测">1. 类型兼容性检测</h3>
<p>相信学过 C# 的朋友都会知道 is 是干嘛的,而且还经常和 as 一起比较,前者一般做兼容性检测,后者一般做兼容性转换,这里我就举个例子吧:</p>
<pre><code class="language-C#">
      static void Main(string[] args)
      {
            object slot = new Slot() { ClothesName = "上衣" };

            if (slot is Slot)
            {
                Console.WriteLine($"slot is {nameof(Slot)}");
            }

            if (slot is IComparable)
            {
                Console.WriteLine($"slot is {nameof(IComparable)}");
            }
      }

      public class Slot : IComparable
      {
            public string ClothesName { get; set; }

            public int CompareTo(object obj) {return 0;}
      }

</code></pre>
<p><img src="https://img2020.cnblogs.com/other/214741/202009/214741-20200930112548817-629317768.png" alt="" loading="lazy"></p>
<p>从这个例子可以看到, object 类型的 slot 和 Slot, IComparable 都是类型兼容的,非常简单。</p>
<h3 id="2-遗憾的地方">2. 遗憾的地方</h3>
<p>然而在实际编码中,我相信有很多朋友都会在后续的过程中用到 slot 实例,而上面的这个例子,即使我用 is 检测到了是 Slot 类型,最后我还是要 将 object slot 强转成 Slot类型,做了一次检测,又做了一个强转,这就很奇葩了,如下代码:</p>
<pre><code class="language-C#">
            if (slot is Slot)
            {
                var query = (Slot)slot;
                Console.WriteLine($"slot is {nameof(Slot)}, ClothesName={query.ClothesName}");
            }

</code></pre>
<p>除非有毛病才写这样的代码,干嘛不直接用 as 尝试性转换将两步合为一步走呢? 修改代码如下:</p>
<pre><code class="language-C#">
            var query = slot as Slot;

            if (query != null)
            {
                Console.WriteLine($"slot is {nameof(Slot)}, ClothesName={query.ClothesName}");
            }

</code></pre>
<p><img src="https://img2020.cnblogs.com/other/214741/202009/214741-20200930112548960-1687426280.png" alt="" loading="lazy"></p>
<p>这就导致很多场景下,is 都被 as 替代了,搞的 is 成了一个空架子,如果 is 能合并 as 的功能,那就🐂👃了,我觉得这个急需增强。</p>
<h2 id="三c7-之后的-is-如何使用">三:C#7 之后的 is 如何使用</h2>
<p>也终于在 C#7 之后对 is 进行了翻天覆地的语法糖改造,导致你初看已经不明白啦😄😄😄,下面我就一一举例来说明吧。</p>
<h3 id="1-is-和-复杂类型简单类型-的结合">1. is 和 复杂类型/简单类型 的结合</h3>
<p>现在就来看一下怎么用新is 解决刚才两次转换的问题,如下代码:</p>
<pre><code class="language-C#">
            object slot = new Slot() { ClothesName = "上衣" };

            if(slot is Slot query)
            {
                Console.WriteLine($"slot is {nameof(Slot)}, ClothesName={query.ClothesName}");
            }

</code></pre>
<p><img src="https://img2020.cnblogs.com/other/214741/202009/214741-20200930112549129-1561458773.png" alt="" loading="lazy"></p>
<p>这段代码表面意思是:先用 is 检测 slot 是否为 Slot 类型,如果是就赋值给 Slot 类型的 query 变量,哈哈,有点意思吧,为了验证是否如我所说,用反编译工具看看。</p>
<ul>
<li>ILSpy 反编译</li>
</ul>
<p><img src="https://img2020.cnblogs.com/other/214741/202009/214741-20200930112549298-1092955777.png" alt="" loading="lazy"></p>
<ul>
<li>DnSpy 反编译</li>
</ul>
<p><img src="https://img2020.cnblogs.com/other/214741/202009/214741-20200930112549536-1241601852.png" alt="" loading="lazy"></p>
<p>可以看到,在实操中,编译器都用 as 进行了还原,不过从代码流畅性来看,ILSpy更🐂👃一点。</p>
<p>除了和类实例比较之外,还可以和 int,string,tuple ...进行比较, 代码如下:</p>
<pre><code class="language-C#">
            object e = 150;

            //字符串比较
            if (e is "hello") { }

            //整形比较
            if (e is 10) { }

            //tuple 比较
            if (e is (10, 20)) { }

</code></pre>
<h3 id="2-is-和-null-的结合">2. is 和 null 的结合</h3>
<p>大家在写 sql 的时候判断某一个字段是否为 null,通常都会这样写: <code>username is null </code> 或者 <code>username is not null</code> ,哈哈,这种写法也被引入到 C# 中了,有意思吧,上代码:</p>
<pre><code class="language-C#">
            object e = 150;

            if (e is null)
            {
                Console.WriteLine("e is null");
            }

            if (e is not null)
            {
                Console.WriteLine("e is not null");
            }

</code></pre>
<p>这么语义化的写法在C#中看到是不是有点不习惯,那为啥在 sql 中就习以为常呢? 其实反编译过来也没啥,就是一个 == 判断,如下代码:</p>
<p><img src="https://img2020.cnblogs.com/other/214741/202009/214741-20200930112549727-792691761.png" alt="" loading="lazy"></p>
<h3 id="3-is-和-and-or-的结合">3. is 和 and ,or 的结合</h3>
<p>现在大家都看到了 is 通常是放在 if 语句中,既然在 if 语句中,那肯定有很多的逻辑判断,这就需要结合 and,or 构建非常复杂的逻辑关系,不要眼花哦。</p>
<pre><code class="language-C#">
            object e = 150;

            if (e is &gt;= 100 and &lt;= 200)
            {
                Console.WriteLine($"e={e} 果然 大于 100 并且 小于 200");
            }

            if (e is 100 or 150 or 200)
            {
                Console.WriteLine($"e={e} 是在 100,150,200 三个数字中");
            }

            if (e is not null and not "")
            {
                Console.WriteLine($"e={e},模拟 !string.IsNullOrEmpty 功能");
            }

</code></pre>
<p><img src="https://img2020.cnblogs.com/other/214741/202009/214741-20200930112549895-74537746.png" alt="" loading="lazy"></p>
<p>可以看到最后的: <code>e is not null and not ""</code> 其实等价于 <code>!string.IsNullOrEmpty</code>, 是不是有点意思哈。</p>
<p>这里有一点要提醒的是,上面的 e 在编译器层面都是 object 类型,如果你想在 编译器层面使用 int 运作,还是用 例子1 的方式转换一下哈,如下图所示:</p>
<p><img src="https://img2020.cnblogs.com/other/214741/202009/214741-20200930112550160-1650494373.png" alt="" loading="lazy"></p>
<h3 id="4-is-和-var-的结合">4. is 和 var 的结合</h3>
<p>当 is 和 var 结合起来就更🐂👃了,可以实现在 if 判断的过程中生成临时变量,如下代码:</p>
<pre><code class="language-C#">
            int e = 150;

            if (e is var i &amp;&amp; i &gt;= 100 &amp;&amp; i &lt;= 200)
            {
                Console.WriteLine($"e={i} 果然 大于 100 并且 小于 200");
            }

</code></pre>
<p>上面代码中的 i 就是一个临时变量,后面做的一切业务逻辑都是基于 i 这个临时变量的,如果还没有领会到精粹,没关系,我举一个项目中的例子吧。。。</p>
<p>我们公司是搞衣物洗涤自动化,也需要对线下 传输线上的衣服进行自动化上挂,取走和衣物组合搭配,举个例子:<code>找到 刚好挂了一件裤子L &amp;&amp; 一件上衣L&amp;&amp; 总衣服个数=2的 挂孔号</code>,要是还没听懂就算了,直接上代码说话。</p>
<pre><code class="language-C#">
class Program
    {
      static void Main(string[] args)
      {
            var slotList = new List&lt;Slot&gt;()
            {
                new Slot(){SlotID=1, ClothesID=10,ClothesName="上衣", SizeName= "L" },
                new Slot(){SlotID=1, ClothesID=20,ClothesName="裤子", SizeName= "M" },
                new Slot(){SlotID=1, ClothesID=11,ClothesName="皮带", SizeName= "X" },
                new Slot(){SlotID=2, ClothesID=30,ClothesName="上衣", SizeName= "L" },
                new Slot(){SlotID=2, ClothesID=40,ClothesName="裤子", SizeName= "L" }
            };

            //找到 刚好挂了一件裤子L &amp; 一件上衣L&amp; 总衣服个数=2的 挂孔号
            var query = slotList.GroupBy(m =&gt; m.SlotID).Where(m =&gt;
                                                                      m.Where(n =&gt; n.SizeName == "L").ToList() is var clothesList &amp;&amp;
                                                                      clothesList.Count(k =&gt; k.ClothesName == "裤子") is 1 &amp;&amp;
                                                                      clothesList.Count(k =&gt; k.ClothesName == "上衣") is 1 &amp;&amp;
                                                                      m.Key == 2
                                                            )
                                                    .ToDictionary(k =&gt; k.Key, v =&gt; v.ToList());
      }

      public class Slot
      {
            public int SlotID { get; set; }

            public int ClothesID { get; set; }

            public string ClothesName { get; set; }

            public string SizeName { get; set; }
      }
    }

</code></pre>
<p><img src="https://img2020.cnblogs.com/other/214741/202009/214741-20200930112550375-480213663.png" alt="" loading="lazy"></p>
<p>重点在于上面代码的<code>m.Where(n =&gt; n.SizeName == "L").ToList() is var clothesList </code>,这里的 clothesList 就是临时变量,它存放了所有 <code>尺寸L</code> 的衣物,后续的检索都是基于这个 clothesList,是不是大大提高了检索速度~~~</p>
<h2 id="四总结">四:总结</h2>
<p>我觉得 is 的功能增强早就该出现了,现在终于搞定了,越来越人性化,键盘敲击次数越来越少,头发也不落了,甚至又开始第二春了,总的来说还是那句话,C# 大法🐂👃。</p>
<p><strong>更多高质量干货:参见我的 GitHub: dotnetfly</strong></p>
<img src="https://img2020.cnblogs.com/blog/214741/202005/214741-20200522143723695-575216767.png" width="600" height="200" alt="图片名称" align="center"><br><br>
来源:https://www.cnblogs.com/huangxincheng/p/13753658.html
頁: [1]
查看完整版本: C# 中的 is 真的是越来越强大,越来越语义化