写在前面
本文内容主要来自 O'Reilly 的《C# 7.0 in a Nutshell》。
最近在看微软 MVP solenovex(https://www.cnblogs.com/cgzl/)的视频教程,在《真会C#吗?-- C#全面教程》里推荐了这本书(还有另一本是《C# in Depth》)。早年看过这本书的中文版《果壳中的C#:C#5.0权威指南》,确实很值得一读。
C# 7.0 in a Nutshell下面是从书中摘录出来的C# 7 中的新增功能。
out 变量
-
无需编写单独的声明语句;变量范围和单独声明一样;支持使用隐式类型的局部变量;
// When calling methods with out parameters, you can declare variables inline: if (int.TryParse("123", out int result)) // result has been declared inline { Console.WriteLine(result); } // Note that the newly introduced variable is still in scope: Console.WriteLine(result); // Out parameters can be implicitly typed (var): int.TryParse("234", out var result2); { Console.WriteLine(result2); }
-
可丢弃参数
通过将下划线 (
_
) 赋给一个变量作为其变量名,指示该变量为一个占位符变量。void Main() { // If you don't need the value of an out parameter, you can discard it with an underscore: string numberString = Console.ReadLine(); if (int.TryParse(numberString, out _)) // Discard out argument { Console.WriteLine("Valid number"); } else { Console.WriteLine("Invalid number"); } // You can use the underscore multiple times in a single method call: Foo(out int interesting, out _, out _, out _); { Console.WriteLine($"The answer to life is {interesting}"); } } void Foo(out int p1, out string p2, out bool p3, out char p4) { p1 = 42; p2 = "fourty two"; p3 = true; p4 = 'x'; }
模式匹配
可以基于任意类型和这些类型的成员的值创建分支逻辑。
-
is
表达式// We can now test for type and introduce a variable at the same time: object foo = "Hello, world"; if (foo is string s) { Console.WriteLine(s.Length); }
-
switch
表达式void Main() { Demo(new TextBox()); Demo(new ComboBox()); Demo(null); Demo(new MonthCalendar()); } void Demo(Control control) { switch (control) { case TextBox t: // We can switch on *type* Console.WriteLine(t.Multiline); break; case ComboBox c when c.DropDownStyle == ComboBoxStyle.DropDown: Console.WriteLine(c.Items); break; case ComboBox c: // The ordering of case clauses now matters! Console.WriteLine(c.SelectedItem); break; case null: Console.WriteLine("Null"); break; default: Console.WriteLine("Unknown"); break; } }
元组
// We can create tuples easily in C# 7:
var tuple = ("three", 3);
Console.WriteLine($"Item1:{tuple.Item1},Item2:{tuple.Item2}");
// With explicit typing:
(string, int) tuple2 = tuple;
// We can even name the fields!
var namedTuple = (word:"three", number:3);
// Named tuples are compiled the same underneath (Item1, Item2):
Console.WriteLine($"namedTuple,Item1:{namedTuple.Item1},Item2:{namedTuple.Item2}");
// But with compiler trickery, we can refer to the names in source code:
Console.WriteLine(namedTuple.number);
Console.WriteLine(namedTuple.word);
方法中的元组
void Main()
{
Console.WriteLine(Foo().Item1);
Console.WriteLine(Foo().Item2);
// With named tuples:
Console.WriteLine(NamedFoo().name);
Console.WriteLine(NamedFoo().time);
}
(string, DateTime) Foo() => ("Now", DateTime.Now);
(string name, DateTime time) NamedFoo() => ("Now", DateTime.Now);
元组解构
void Main()
{
// We can "deconstruct" a tuple into individual variables as follows:
(string s, DateTime dt) = Foo();
Console.WriteLine(s);
Console.WriteLine(dt);
// If the variables already exist, just omit the type names:
(s, dt) = Foo();
// You can implicitly type the variables if desired:
var (s2, dt2) = Foo();
Console.WriteLine(s2);
Console.WriteLine(dt2);
// You can discard a tuple member with an underscore:
var (s3, _) = Foo();
Console.WriteLine(s3);
}
(string, DateTime) Foo() => ("Now", DateTime.Now);
自定义解构
// Deconstructors are not just for tuples.
// You can create your own by writing a method called "Deconstruct".
void Main()
{
var point = new Point(3, 4);
var (x, y) = point;
Console.WriteLine(x);
Console.WriteLine(y);
}
class Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y) { X = x; Y = y; }
public void Deconstruct(out int x, out int y) { x = X; y = Y; }
}
本地函数
许多类的设计都包括仅从一个位置调用的方法。 这些额外的私有方法使每个方法保持小且集中。 本地函数使你能够在另一个方法的上下文内声明方法。 本地函数使得类的阅读者更容易看到本地方法是仅从声明它的上下文中调用的。
void Main()
{
Console.WriteLine(Fibonacci(10));
}
public int Fibonacci(int x)
{
return Fib(x).current;
// You can now declare a function within a function:
(int current, int previous) Fib(int i)
{
if (i == 0) return (1, 0);
var (p, pp) = Fib(i - 1);
return (p + pp, p);
}
}
// (This also works with iterators)
数字文本语法改进
可使用下划线_
作数字分隔符,使用0b
表示二进制。
// You can use underscores within numeric literals to make them more readable:
int x = 12_000;
Console.WriteLine(x);
// The '0b' prefix now denotes a binary literal:
var b = 0b1010_1011_1100_1101_1110_1111;
Console.WriteLine(b);
ref
局部变量和返回结果
此功能允许使用并返回对变量的引用的算法,这些变量在其他位置定义。
// Ref returns/locals are now possible for advanced scenarios.
// See https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/
void Main()
{
int[] array = { 1, 15, -39, 0, 7, 14, -12 };
ref int place = ref Find(7, array); // aliases 7's place in the array
place = 9; // replaces 7 with 9 in the array
Console.WriteLine(array[4]); // prints 9
}
public ref int Find(int number, int[] numbers)
{
for (int i = 0; i < numbers.Length; i++)
{
if (numbers[i] == number)
{
return ref numbers[i]; // return the storage location, not the value
}
}
throw new IndexOutOfRangeException($"{nameof(number)} not found");
}
更多的 expression-bodied 成员
// Expression-bodied members are now allowed in constructors, finalizers and property accessors:
void Main()
{
new Foo().Name = "test";
GC.Collect();
GC.WaitForPendingFinalizers();
}
class Foo
{
public Foo() => Console.WriteLine("Constructor");
~Foo() => Console.WriteLine("Finalizer");
public string Name
{
get => "get accessor";
set => Console.WriteLine("set accessor");
}
}
throw
表达式
throw
表达式使得基于表达式的代码变得更容易。 不需要其他语句来进行错误检查。
void Main()
{
// You can now throw expressions in expressions clauses.
// This is useful in conditional expressions:
string result = new Random().Next(2) == 0 ? "Good" : throw new Exception ("Bad");
}
public string Foo() => throw new NotImplementedException();
网友评论