新词
对象初始值设和集合初始值设定项
对象初始值设定项的工作原理:
init访问器,本质上就是在对象初始化时候给内部字段(实例)赋值。
init就 modreq([System.Runtime]System.Runtime.CompilerServices.IsExternalInit) 类型的缩写
modreq= Modified Request:修改请求
IsExternalInit=is Externa Init(initialize):是外部初始化 。
所以init 完整的意思就是 修改请求(在外部初始化的时候)
1、如果给匿名类型属性赋值,调用(call)匿名类型的构造函数。
2、给类的属性或索引赋值,调用(calvirtl)set访问器给 属性或索引赋值,只读属性或索引(因为没set访问器)不能在 对象初始值设定项中赋值。
在get方法前面添加 带有修饰符的init 标志该set 方法是给内部自动设定初始值的。
3、给类的公开字段赋值,就是直接赋值了。
4、数组初始值设定项依赖的是Array的Add()的方法而不是对象Add()的方法,因此A[] a=new A{a,s,f}中的new A要省略掉 写成A[] a= {a,s,f}。集合对象初始值设定项 依赖的是集合对象 的Add方法例如:A a=new A{a,s,v},因此对象new A不能省略。
//对象的初始值设定项 Cat cat = new Cat { Age = 10, Name = "Fluffy" }; Cat sameCat = new Cat("Fluffy"){ Age = 10 }; //匿名类型 的对象初始值设定项 var student = new { Name = "Wyhon", Age = 18 }; //从 C# 6 开始,除了分配字段和属性外,对象初始值设定项还可以设置索引器。 请思考这个基本的 Matrix 类: public class Matrix { private double[,] storage = new double[3, 3]; public double this[int row, int column] { // The embedded array will throw out of range exceptions as appropriate. get { return storage[row, column]; } set { storage[row, column] = value; } } } //可以使用以下代码初始化标识矩阵: var identity = new Matrix { [0, 0] = 1.0, [0, 1] = 0.0, [0, 2] = 0.0, [1, 0] = 0.0, [1, 1] = 1.0, [1, 2] = 0.0, [2, 0] = 0.0, [2, 1] = 0.0, [2, 2] = 1.0, }; =====================================================================================================================================
实现IEnumerable接口和Add()、Add<T>(T? value)方法的对象都可以使用集合初始值设定项
//list 集合初始值设定项 List<string> positionAbbreviations = new List<string> { "P", "C", "1B", "2B", "3B", "SS", "LF", "CF", "RF" };
List<object> de= new List<object>() { new Object(),new Object()}; -------------------------------------------------------------------------------- //字典 集合初始值设定项 在 C# 6 之前 初始化字典和其他关联容器方式 var students = new Dictionary<int, string>() { { 111, "Sachin" } , { 112, "Sachin" }, { 113, "Sachin" } , }; //如果集合支持读取/写入索引,可以指定索引元素。 var students2 = new Dictionary<int, string>() { [111] = "Sachin",//键值对方式 [112] = "Sachin", [113] = "Sachin", }; var students2 = new Dictionary<int, StudentName> { [111] = new StudentName { FirstName = "Sachin", LastName = "Karnik", ID = 211 }, [112] = new StudentName { FirstName = "Dina", LastName = "Salimzianova", ID = 317 }, [113] = new StudentName { FirstName = "Andy", LastName = "Ruth", ID = 198 } }; //或者 Dictionary<int, StudentName> students2 = new () { [111] = new StudentName { FirstName = "Sachin", LastName = "Karnik", ID = 211 }, [112] = new StudentName { FirstName = "Dina", LastName = "Salimzianova", ID = 317 }, [113] = new StudentName { FirstName = "Andy", LastName = "Ruth", ID = 198 } };
具有集合只读属性初始化的对象初始值设定项, 查看官网
//list不是数组,怎么可以这样赋值 MySerializeable serializeable = new() {Fields ={ "ddfgdg","sdfsdf" } }; public class MySerializeable { public List<string> Fields { get; } = new List<string>(); public int Name4 ;//会被忽略因为是默认值 }
2、自动实现的属性
public string Name { get; set; }//自动化实现属性不用在二进制序列化 的类中,因为每次生成的字段都可能不一样,影响反序列化
3、这称为隐式类型化数组:
int[] array2 = { 1, 3, 5, 7, 9 };
var a = new[] { 1, 10, 100, 1000 }; //一维数组 int[]
var c = new[]{ new[]{1,2,3,4}, new[]{5,6,7,8} };二维数组也是用int[]
从 C# 9.0 开始,构造调用表达式由目标确定类型。 也就是说,如果已知表达式的目标类型,则可以省略类型名称,如下面的示例所示:
List<int> xs = new(); List<int> ys = new(capacity:10_000);////调用对构造函数 List<int> zs = new(){ Capacity = 20_000 };//调用对象集合初始值设定项
List<int> xs = new List<int> { Capacity = 20_000 };//调用对象集合初始值设定项
4、 表达式主体 expresion bodies(以表达式为主体的) =>
Expression-Bodied Properties:以表达式为主体的属性。
Expression-Bodied Methods:以表达式为主体的方法。
Expression-bodied 成员(类的成员)通过表达式主体定义,可采用非常简洁的可读形式提供成员的实现。 只要任何支持的成员(如方法或属性)的逻辑包含单个表达式,就可以使用表达式主体定义。 表达式主体定义具有下列常规语法:
使用构造函数的表达式主体
使用方法的表达式主体(Expression-bodied Methods )
使用运算符的表达式主体()
使用属性的表达式主体(Expression-bodied Properties)
使用索引器的表达式主体
使用访问器的表达式主体
使用 Lambda 表达式的表达式主体
使用本地函数的表达式主体
左边参数=>右边式表达式主体; 整个构成表达式主体定义
属性表达式主体:public int Age => _age; //只读Age 属性以表达式主体定义的形式实现 //d读写Name 属性以表达式主体定义的形式实现 public string Name { get => locationName; set => locationName = value; } //构造函数表达式主体: private string locationName; public Location(string name) => Name = name;
//索引 表达式主体:
public
string
this
[
int
idx] => languages[idx];
//方法表达式主体: public override string ToString() => $"{fname} {lname}".Trim(); //Lambda 表达式的表达式主体 Action line = () => Console.WriteLine();//将 lambda 表达式的输入参数括在括号中。 使用空括号指定零个输入参数: Func<double, double> cube = x => x * x * x;//如果 lambda 表达式只有一个输入参数,则括号是可选的: Func<int, int, bool> testForEquality = (x, y) => x == y;//两个或更多输入参数使用逗号加以分隔 Func<int, int, int> constant = (_, _) => 42;//从 C# 9.0 开始,可以使用弃元指定 lambda 表达式中不使用的两个或更多输入参数: //switch 表达式 string word = number switch { 1 => "one", 2 => "two", 3 => "three", _ => throw new ArgumentOutOfRangeException(nameof(number)) };
6、系统生成类
迭代器:<GetEnumerator>d__0
匿名类型:<>f__AnonymousType
匿名函数:'<>c'
闭包 :<> c__DisplayClassX
7、类型和命名空间别名
导入命名空间可能导致类型名称的冲突,因此可以只导入需要的特定类型而不是整个命名空间,并给它们创建别名。例如:
8、using static指令
从C#6开始,我们不仅可以导入命名空间还可以使用using static指令导入特定的类型。这样就可以直接使用类型静态成员而不需要指定类型的名称了。在接下来的例子中,我们这样调用Console类的静态方法WriteLine:
using static system.Console;class Test { static void Main (){ writeLine( "Hello");})
using static指令将类型的可访问的静态成员,包括字段、属性以及嵌套类型(后续章节会讲解),全部导入进来。同时,该指令也支持导入枚举类型的成员(后续章节会讲解)。因此如果导入了以下的枚举类型:
using static System.Windows.Visibility;
我们就可以直接使用Hidden而不是Visibility.Hidden了:
var textBox = new TextBox { Visibility = Hidden }; // XAML- style
C#编译器还没有聪明到可以基于上下文来推断出正确的类型,因此在导入多个静态类型导致二义性时会发生编译错误。
7、语法糖
迭代器、匿名函数、匿名类型
9、13种模式匹配
10、方法组转换
public delegate void Del<T>(T item); public static void Notify(int i) { } Del<int> m1 = new Del<int>(Notify); Del<int> m2 = Notify;//方法组转换简化语法编写上一行:
WebAssembly
12、RTTI(运行时类型信息)
在现代化的企业级的开发中,可维护性是大家首要考虑的,所以就有泛型这个概念
AppDomain:应用程序域由 AppDomain 对象表示,有助于提供隔离、卸载和安全边界来执行托管代码。
AssemblyLoadContext:主要用于提供程序集加载隔离, 它允许在单个进程中加载同一程序集的多个版本。 它将替换 .NET Framework 中的多个实例提供的隔离机制 AppDomain 。不提供任何安全功能。 所有代码都具有该进程的完全权限。在 .NET Core 上, AppDomain 实现受设计限制,不提供隔离、卸载或安全边界。 对于 .NET Core,只有一个 AppDomain 。 通过提供隔离和卸载 AssemblyLoadContext 。 安全边界应由进程边界和适当的远程处理技术提供。
13、 本地函数
本地函数是一种嵌套在另一类成员中的私有方法,不能有任何访问修饰符。但是,不能在 expression-bodied 成员中声明本地函数。本地函数定义不能包含成员访问修饰符,因为所有本地函数都是私有的。可以将以下修饰符用于本地函数:
async、unsafe、static (在 C# 8.0 和更高版本中)。 静态本地函数无法捕获局部变量或实例状态。extern (在 C# 9.0 和更高版本中)。 外部本地函数必须为 static 。
14、内联变量声明在函数内不调用方法时候,可以声明变量,这个变量在方法内部都有效
if (Int32.TryParse(numberAsString, out int number))
15、指针和句柄有啥区别?
1、句柄就是进程句柄表中的索引。
2、句柄是对进程范围内一个内核对象地址的引用,一个进程的句柄传给另一个进程是无效的。一个内核对象可用有多个句柄。
Windows之所以要设立句柄,根本上源于内存管理机制的问题,即虚拟地址。简而言之数据的地址需要变动,变动以后就需要有人来记录、管理变动,因此系统用句柄来记载数据地址的变更。
16、查询表达式
从应用程序的角度来看,原始源数据的特定类型和结构并不重要。 应用程序始终将源数据视为 IEnumerable<T> 或 IQueryable<T> 集合。数据库中一行就是一个可以枚举序列。
查询表达式使用类似于 SQL 或 XQuery 的声明性语法来查询 IEnumerable 集合。 查询表达式必须以 from 子句开头,且必须以 select 或 group 子句结尾。。 应用程序通过使用 using
指令指定适当的命名空间来控制范围内的标准查询运算符。 下面的查询表达式获取一个字符串数组,按字符串中的第一个字符对字符串进行分组,然后对各组进行排序。
var query = from str in stringArray group str by str[0] into stringGroup orderby stringGroup.Key select stringGroup;
17、LINQ
LINQ 最明显的“语言集成”部分就是查询表达式。 查询是一种从数据源检索数据的表达式。随着时间的推移,人们已经为各种数据源开发了不同的语言。例如,用于关系数据库的SQL和用于XML的XQuery。因此,开发人员不得不针对他们必须支持的每种数据源或数据格式而学习新的查询语言。LINQ通过提供一种跨数据源和数据格式使用数据的一致模型,简化了这一情况。在LINQ查询中,始终会用到对象。可以使用相同的编码模式来查询和转换XML文档、SQL数据库、ADO.NET数据集、.NET集合中的数据以及对其有LINQ提供程序可用的任何其他格式的数据。
var newLargeOrderCustomers = IncomingOrders.Where(x => x.OrderSize > 5).Select(y => new Customer { Name = y.Name, Phone = y.Phone });