该系列文章大量参考了 C# 7.0 in a nutshell 一书:
C# 语言对面向对象范式进行了丰富的实现, 当然包括封装, 继承和多态.
C#里面所有的类型都有一个共享的基类, 这也意味之C#里面所有的类型都具备一些相同的基本功能, 例如任何类型都可以通过调用ToString()方法来转化成字符串.
传统的面向对象范式里面, 只有一种类型就是class. 而在C#里面还有几种其他的类型, 这里就包含接口(interface).
在C#里面, 在需要多继承的情况下就应该使用接口, 因为C#不支持多继承.
在纯面向对象范式里面, 所有的函数(function)都是方法(method)(例如Smalltalk语言). 在C#里, 方法只是函数成员(function members)的一种, 其他的成员还包括属性(property)以及事件(event), 此外还有其它的类型.
属性(Property)是一种可以封装对象一部分的状态的函数成员.
事件(Event)这种函数成员会在对象状态变化的时候进行响应.
可以说C#主要是一种面向对象的语言, 但是它也借鉴了函数式编程范式的特点, 尤其是:
通过委托(delegate), C#允许函数可以像值一样在函数间进行传递.
函数式编程的核心思想就是尽量不要使用值会变化的变量, 并拥护陈述模式. C#通过它额特性对该模式进行支持. 例如: lambda表达式(它是一种可即时编写并能”俘虏”变量的匿名函数). C#还有能力通过查询表达式来执行列举动作或响应式编程. 同时C#可以很简单地通过定义只读字段(field)和属性(property)来实现不可变(immutable, 或叫只读)类型.
C#主要还是一种类型安全的语言, 也就是说类型的实例之可以通过预定的协议进行交互, 这样就可以保证类型内部的一致性.
C#也支持静态类型, 这意味着C#在编译时就保证了类型安全.
C#虽然可以通过dynamic关键字来实现动态类型, 但它主要还是个静态类型语言.
C#同时也被称为强类型语言, 因为它的类型规则十分的严格.
C#依赖于运行时来实现自动化的内存管理工作. CLR(Common Language Runtime)通用语言运行时, 它有个垃圾收集器(GC), GC会作为你程序的一部分来执行, 它会处理掉不再引用的对象并回收内存. 这也消除了像某些语言中使用指针来解除内存分配可能遇到的问题, 例如C++.
另外一点需要注意的就是C#没有消灭指针, 只不过大多数情况下根本不需要使用指针. 但是针对性能要求比较高或需要互操作的时候可以使用指针和明确的内存分配, 但这部分代码必须写在标明了unsafe的块里.
Windows, Linux, macOS, iOS, Android.
C# 依赖于运行时来提供很多功能, 例如自动内存管理和异常处理等. .NET Framework的核心就是一个运行时: CLR(通用语言运行时).
C#是一种托管语言, 它可以被编译成托管代码, 托管代码是使用的是IL语言(Intermediate Language, 中间语言). CLR会把IL转化成实际运行机器的本地代码, 例如X86或X64的机器, 通常就是在运行之前执行. 这可以叫做即时编译(JIT, Just-in-time). 也可以使用预编译(AOT, Ahead-of-time), 在组件(assembly)较多或资源紧缺的设备上使用AOT可以提升程序启动速度.
托管代码的容器叫做组件(assembly)或者便携式可执行文件. assembly可以是一个exe或者dll, 它里面不仅包含IL还包含类型信息(metadata, 元数据).
可以使用微软的ildasm工具来查看IL assembly.
.NET Framework由CLR和其它大量的库组成. 这些库由核心库(本系列文章主要讲的就是这个)和应用库, 而应用库也是依赖于核心库的. 请看下图:
下面是这几个主流框架对平台支持情况的列表:
遗留的框架主要有:
值得一提的小众框架有:
// 数值
int million = 1_000_000;
float millionWithDecimal = 1_000_000.000_002f;
// 使用二进制
var b = 0b1010_1011_1100_1101_1110_1111;
可以在方法参数的行内直接声明out变量:
bool successful = int.TryParse("123", out int result);
此外, 当你调用一个含有多个out参数的方法的时候, 你可以忽略那些你不感兴趣的out 参数, 使用下划线"_"即可:
SomeMethod(out _, out _, out _, out int x, out _, out _, out _);
你可以使用is操作符来随时引入变量. 这就叫做模式变量:
void Foo(object x)
{
if (x is string s)
{
Console.WriteLine(s.Length);
}
}
switch语句也支持模式变量:
switch(x) {
case int i:
Console.WriteLine("int!");
break;
case string s:
Console.WriteLine(s.Length);
break;
case bool b when b == true:
Console.WriteLine("True");
break;
case null:
Console.WriteLine("Nothing");
break;
}
本地方法就是在某个方法里面声明的方法:
void WriteCubes()
{
Console.WriteLine(Cube(3));
Console.WriteLine(Cube(4));
Console.WriteLine(Cube(5));
int Cube(int value) => value * value * value;
}
C# 6里面为方法, 只读属性(property), 操作符和索引器引进了expression-bodied "宽箭头"的语法. C# 7里面把该语法扩展到了构造函数, 读写属性, 和总结器(finalizer):
public class Person
{
string name;
public Person(string name) => Name = name;
public string Name
{
get => name;
set => name = value ?? "";
}
~Person() => Console.WriteLine("finalize");
}
C# 7 里面引入了析构函数模式. 构造函数的作用通常是把一组(参数)值赋給字段, 而析构函数则是做了相反的事, 把字段赋給一组变量:
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); // Joe
Console.WriteLine(last); // Bloggs
C# 7 里面支持显式元组:
var bob = ("Bob", 23);
System.Console.WriteLine(bob.Item1); // Bob
System.Console.WriteLine(bob.Item2); // 23
var tuple = (Name: "Bob", Age: 23);
System.Console.WriteLine(tuple.Name); // Bob
System.Console.WriteLine(tuple.Age); // 23
使用元组, 函数可以返回多个值, 也就无需使用out参数了:
var pos = GetFilePositon();
System.Console.WriteLine(pos.row); // 3
System.Console.WriteLine(pos.column); // 10
元组支持解析模式:
(int row, int column) = GetFilePositon();
System.Console.WriteLine(row); // 3
System.Console.WriteLine(column); // 10
在C# 7以前, throw 肯定是一个语句. 而现在它可以作为expression-bodied 函数的表达式了:
public string Foo() => throw new NotImplementedException();
throw表达式也可以出现在三元状态表达式里:
string Capitalize(string value) =>
value == null ? throw new ArgumentException("value") :
value == "" ? "" :
char.ToUpper(value[0]) + value.Substring(1);
C# 7 里面还有很多改进没有写, 这些会在以后的文章中提到.
它可以在访问类成员之前避免手动的去检查该类对象是否为null.
StringBuilder sb = null;
string result = sb?.ToString();
它允许使用一行表达式来完成方法, 属性, 操作符, 索引器:
public int TimesTwo(int x) => x * 2;
public string SomeProperty => "Property Value";
可以为自动属性赋初始值了:
public DateTime TimeCreated { get; set; } = DateTime.Now;
public DateTime TimeCreated { get; } = DateTime.Now; // 只读属性也可以
var dict = new Dictionary<int, string>()
{
[3] = "three",
[10] = "ten"
};
string s = $"It is {DateTime.Now.DayOfWeek} today";
string html;
try
{
html = new WebClient().DownloadString("http://test");
}
catch (WebException ex) when (ex.Status == WebExceptionStatus.Timeout)
{
...
}
使用 using static 指令可以直接引入一个类型的所有静态成员, 然后使用的时候就可以不写类名了:
using static System.Console;
...
WriteLine("Hello World");
nameof 操作符可以返回变量, 类型, 和其它符号的名字(字符串), 这样就可以避免在重命名符号的时候造成代码错误.
int capacity = 123;
string x = nameof(capacity); // "capacity"
string y = nameof(Uri.Host); // "Host"
现在可以在catch 和 finally块里使用await了.
主要是 async 和 await, 这个会在以后的文章具体介绍.
就不介绍了...
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。