C# 9.0 新特性预览 - 类型推导的 new
<h1 id="c-90-新特性预览---类型推导的-new">C# 9.0 新特性预览 - 类型推导的 new</h1><p></p>
<h3 id="前言">前言</h3>
<p>随着 .NET 5 发布日期的日益临近,其对应的 C# 新版本已确定为 C# 9.0,其中新增加的特性(或语法糖)也已基本锁定,本系列文章将向大家展示它们。<br>
</p>
<h3 id="目录">目录</h3>
<p><br><br><br><br><br><br></p>
<p></p>
<h3 id="具有类型推导的-new-表达式-target-typed-new-expressions">具有类型推导的 new 表达式 (Target-typed new expressions)</h3>
<p>这是一个本应随着 C# 8.0 发布的语言特性,但因种种原因在发布 C# 8.0 的最后关头,它被移出了最终的发布版本,下面我们来认识认识它。<br>大家都知道,C# 在3.0中新增 var 关键字来做隐式类型声明,把繁重的声明语法简化了。</p>
<pre><code class="language-csharp">Dictionary<string, List<int>> field = new Dictionary<string, List<int>>()
// 可以简化为
var field = new Dictionary<string, List<int>>()
</code></pre>
<p>var 关键字的基本原理不再复述,简单说就是编译器可以根据等号后面的类型推导出 var 的类型,那么是不是也可以反过来,我们先声明类型,接下来的 new 关键字后面就不用写类型了呢?于是本文介绍的特性来了:</p>
<pre><code class="language-csharp">Dictionary<string, List<int>> field = new Dictionary<string, List<int>>()
// C# 9.0 中可以写成
Dictionary<string, List<int>> field = new()
</code></pre>
<p>从以上代码可以看出语法很简单,即省略了繁琐的可以推导出的类型。<br>其语法 Spec 如下:</p>
<pre><code>'new' '(' argument_list? ')' object_or_collection_initializer?
</code></pre>
<p>搭配初始化器,我们可以进一步简化带有初始值的初始化。</p>
<pre><code class="language-csharp">Dictionary<string, List<int>> field = new() {
{ "item1", new() { 1, 2, 3 } }
};
</code></pre>
<p></p>
<h4 id="进一步展示该语法在各种情况下的使用">进一步展示该语法在各种情况下的使用</h4>
<p>在所有可以推导出类型的上下文中,都可以使用,例如:<br></p>
<pre><code class="language-csharp">XmlReader.Create(reader, new() { IgnoreWhitespace = true });
</code></pre>
<p>带有参数的构造方法:</p>
<pre><code class="language-csharp">class C {
C(params int[] p) {}
}
C c = new(1, 2, 3);
</code></pre>
<p>调用方法时:</p>
<pre><code class="language-csharp">class A {}
static void M(A a) {};
M(a: new());
</code></pre>
<p>配合对象初始化器:</p>
<pre><code class="language-csharp">X x = new() { field = 42 };
</code></pre>
<p>泛型的类型推导,需要注意,要有一个显示类型声明才能正确推导:</p>
<pre><code class="language-csharp">void M<T>(T t1, T t2) {}
M(new X(), new());
</code></pre>
<p>类似的,数组的声明</p>
<pre><code class="language-csharp">var arr = new[] {new X(), new()};
</code></pre>
<p></p>
<h4 id="不适用此特性的场景">不适用此特性的场景</h4>
<p>值类型的初始化,可以使用 default 替代</p>
<pre><code class="language-csharp">int x = new(); // ERROR
Struct y = new(); // ERROR
</code></pre>
<p>使用 as 操作时无法正确推导</p>
<pre><code class="language-csharp">Console.Write(new() as X); // ERROR
</code></pre>
<p>自然,使用 var 时也无法推导</p>
<pre><code class="language-csharp">var x = new(); // ERROR
</code></pre>
<p>有歧义的重载</p>
<pre><code class="language-csharp">void M(object a, X b) => Console.Write($"{a} {b}");
void M(X a, object b) => Console.Write($"{a} {b}");
M(new(), new()); // ERROR
</code></pre>
<p></p>
<h4 id="_"></h4>
<p></p>
<h4 id="需要注意的地方">需要注意的地方</h4>
<p>这个新语法 <em>new()</em>,比较容易与匿名类型语法混淆 <em>new{}</em>,它们两个是完全不同的东西,需要注意一下。<br></p>
<p></p>
<h3 id="参考">参考</h3>
<p><br></p><br><br>
来源:https://www.cnblogs.com/Rwing/p/csharp-9-0-preview-target-typed-new.html 顶一个!终于把这个特性等来了,之前看C# 8.0的时候还遗憾了一把,没想到在9.0里终于加上了。
这个语法确实很实用,特别是写那些泛型集合的时候,比如Dictionary<string, List<int>>这种长类型,写一遍就够了,代码简洁很多。
原作者提到这个特性容易被匿名类型混淆,确实要注意区分。new()是目标类型推导,而new {}是匿名对象初始化器,完全两码事。
不过有个小疑问想请教一下:如果我同时引用了两个命名空间,每个命名空间里都有同名的类,这种情况下使用new()会不会有歧义?还是说编译器会自动报错提示需要指定完整类型名?
另外期待一下这个系列的其他文章,特别是Record类型,之前看提案就觉得很强大,应该能省掉不少Equals、GetHashCode之类的模板代码。
感谢楼主的分享,期待更新!
頁:
[1]