作为一个半路出家的人来说,对C#语言的学习对于每一个版本的新特性了解是非常必要的 关键字: c#, csharp [[toc]]
C#的设计目标
- 旨在是一种简单,现代,通用的面向对象编程语言。
- 语言及其实现应该为软件工程原理提供支持,例如强类型检查,数组维度检查,未初始化的变量引用检测以及自动垃圾收集。软件的鲁棒性,耐久性和程序员的生产力很重要。
- 旨在用于开发适用于分布式环境中部署的软件组件。
- 便携性对于源代码和程序员非常重要,特别是已经熟悉C和C ++的程序员。
- 支持国际化是非常重要的。
- C#适用于为托管和嵌入式系统编写应用程序,从使用复杂的操作系统到非常小的专用功能都非常适用。
- 虽然C#应用程序在内存和处理能力要求方面是经济的,但是该语言并不打算直接用C或汇编语言直接与性能和尺寸进行竞争。
C# 2.0 (记录重点)
泛型
匿名方法
labmba的前身
迭代器
可空类型 (nullable type)
Getter / setter单独可访问性
方法组转换(代表)
Co- and Contra-variance for delegates
静态类
Delegate inference
C# 3.0
LINQ查询表达式
令C#可以直接编写查询并以静态方法检查其正确性
隐式类型局部变量
var 关键字允许在声明中省略变量类型,然后由编译器推断其类型
对象和收集初始化器
在初始化器里直接设置对象的可访问字段或属性
自动实现的属性
匿名类型
扩展方法
Lambda表达式
表达树
一种特殊类型Expression
C# 4.0
动态绑定
dynamic
类型是一种静态类型,类型为 dynamic 的对象会跳过静态类型检查。 大多数情况下,该对象就像具有类型 object 一样。
dynamic dyn = 1;
object obj = 1;
// dyn = dyn + 3; 不会报错
// obj = obj + 3; 会报错
System.Console.WriteLine(dyn.GetType());
System.Console.WriteLine(obj.GetType());
//输出同时为System.Int32
可选参数
可选参数就是带有默认值的函数参数
void Foo(int x = 20) { ... }
命名参数
除了按照顺序位置来确定参数外,还可以用名称来确定参数
void Foo(int x = 0, int y) => x + y;
对于该函数的调用有如下方法
Foo(1, 2);
Foo(x: 1, y: 2);
Foo(1, y:2);
Foo(y: 2);
协变和逆变扩展
加入了对Generic Type的Co&Contravariance的支持。 具体可以参见文章 https://blog.csdn.net/weixin_30672019/article/details/96082697
嵌入式互操作类型(“NoPIA”)
暂不记录...
C# 5.0
异步方法 async 和 await
await 是为了让异步操作更像同步
function 摇色子(){
return new Promise((resolve, reject)=>{
let sino = parseInt(Math.random() * 6 +1)
setTimeout(()=>{
resolve(sino)
},3000)
})
}
async function test(){
let n = await 摇色子()
console.log(n)
}
test()
CallerInfoAttributes
using System;
using System.Runtime.CompilerServices;
namespace TestPro
{
class Program
{
public static void Main()
{
Log("Test.");
}
// 对日志消息进行记录,同时所有内容均有默认值,如果获取失败,则使用默认值。
public static void Log(string message,
[CallerMemberName] string callerName = "unknown",
[CallerFilePath] string callerFilePath = "unknown",
[CallerLineNumber] int callerLineNumber = -1)
{
Console.WriteLine("Message: {0}", message);
Console.WriteLine("Caller's Name: {0}", callerName);
Console.WriteLine("Caller's FilePath: {0}", callerFilePath);
Console.WriteLine("Caller's LineNumber: {0}", callerLineNumber);
}
}
}
输出一下内容
Message: Test.
Caller Name: Main
Caller FilePath: C:UsersAdministratorsource
eposTestProProgram.cs
Caller Line number: 10
C# 6.0
Roslyn编译器
新的编译器将一整条编译流水通过程序库进行开放,使得对各种源代码进行分析 可以参见 https://github.com/dotnet/roslyn
字符串插值
更加简单的方式代替 string.Format
string s = $"it is ${DateTime.Now.DayOfWeek} today";
将静态类型成员导入命名空间
using static
using static System.Console;
...
WriteLine("Hello world");
异常过滤器
可以在 catch
后追加条件
try {
...
} catch (Exception ex when (ex.Status == WebExceptionStatus.Timeout)) {
...
}
指的一提的是在Catch和Finally中使用Await
属性初始化器
初始化的时候自动设置默认值
public DateTime TimeCreated {get;set;} = DateTime.Now;
只读属性初始化器
这种初始化同样支持只读属性
public DateTime TimeCreated {get;} = DateTime.Now;
索引器初始化器
还可以对索引器进行初始化
var dict = new Dictionary<int, string>()
{
[3] = "three",
[10] = "ten"
};
null的简化判断
前该赋值操作会报错NullReferenceExc
System.Text.StringBuilder sb = null;
string result = sb?.ToString(); // result 为 null
nameof operator
nameof 可以返回 变量、类型、其他符号
的名称
int capacity = 123;
string x = nameof(capacity); // x is "capacity"
string y = nameof(Uri.Host); // y is "Host"
表达式函数体(胖箭头语法)
public int TimesTwo(int x) => x * 2;
public string SomeProperty => "Property value";
C# 7.0
可以参考连接 https://devblogs.microsoft.com/dotnet/whats-new-in-csharp-7-0/
out变量
out 使用更容易
bool successful = int.TryParse("123", out int result);
Console.WriteLine(result);
如果调用有多个out参数的方法时, 可以使用下划线来忽略参数
testfunc(out _, out _, out int result, out _);
Console.WriteLine(result);
模式
is 运算符 也成为模式变量
if(x is string) { ... }
简化了类型转换
object o = 1;
//老方法
int i = Convert.ToInt32(o);
Console.WriteLine(i + 1);
//运用 is 模式匹配
if (o is int i) {
Console.WriteLine(i + 1);
}
switch 同样支持模式, 配合 when 判断条件; 同时支持 null 条件
switch(x){
case int i:
break;
case string s:
break;
case bool b when b == true:
break;
case null:
break;
}
元组
元祖实际上是使用了System.ValueTuple<...>泛型结构的语法糖
var bob = ("Bob", 23)
Console.WriteLine(bob.Item1);
Console.WriteLine(bob.Item2);
我们还可以对元祖的元素进行命名
var tuple = (Name:"Bob", Age:23);
Console.WriteLine(tuple.Name);
Console.WriteLine(tuple.Age);
元祖也可以代替out的参数返回功能
static (int row, int column) GetFilePosition() => (3, 10);
static void Main() {
var pos = GetFilePosition();
Console.WriteLine(pos.row);
Console.WriteLine(pos.column);
}
元祖隐式地支持解构模式,因此很容易解构为若干个独立变量
static void Main() {
(int row, int column) = GetFilePosition();
Console.WriteLine(row);
Console.WriteLine(column);
}
解构器
解构器跟构造器的作用正好相反,它将字段反向赋值给变量
public class Person {
public void Deconstruct (out string firstName, out string lastName)
{
int spacePos = name.IndexOf(' ');
firstName = name.Substring(0, spacePos);
lastName = name.Substring(spacePos + 1);
}
}
以特定的语法进行调用
var joe = new Person("Joe Bloggs");
var (first, last) = joe;
Console.WriteLine(first);
Console.WriteLine(last);
局部函数
局部方法同样支持 lambda
void WriteCubes() {
Console.WriteLine(Cube(1));
Console.WriteLine(Cube(2));
Console.WriteLine(Cube(3));
int Cube(int value) => value * vaule * vaule;
}
数字分隔符
使用下划线来改善可读性,会被编译器忽略
int million = 1_000_000;
//二进制加0b前缀
var b = 0b1010_1010_1010_1100;
局部引用和引用返回
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");
}
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
WriteLine(array[4]); // prints 9
扩展异步返回类型
改进后不需要强制异步操作时必须返回 void, Task or Task
表达式的构造函数和finalizers
public class Person {
string name;
public Person (string name) => Name = name;
~Person () => Console.WriteLine("finalize");
}
throw表达式
throw 可以出现在函数体中
public string Foo() => throw new Notlmple-mentedException();
throw 可以出现在三元运算符中
string Capitalize(string value) => value == null ? throw new ArgumentException("value") : value == "?" : char.ToUpper(value[0]) + value.Substring(1);
C# 8.0 - C# 10.0
官方有非常详细的手册,推荐查阅 快速查阅