Technology Csharp - chaolunner/CloudNotes GitHub Wiki
- 速度上的区别: 值类型存取速度快,引用类型存取速度慢。
- 用途上的区别: 值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针或引用。
- 来源上的区别: 值类型继承自System.ValueType,引用类型继承自System.Object。
- 类型上的区别: 值类型的变量直接存放实际的数据,而引用类型的变量存放的则是数据的地址,即对象的引用。
- 内存释放上的区别: 栈的内存分配是自动释放;而堆在.NET中会有GC来释放。
- 保存位置上的区别: 值类型变量直接把变量的值保存在堆栈中,引用类型的变量把实际数据的地址保存在堆栈中,而实际数据则保存在堆中。
注意,堆和堆栈是两个不同的概念,在内存中的存储位置也不相同,堆一般用于存储可变长度的数据,如字符串类型。而堆栈则用于存储固定长度的数据,如整型类型的数据int(每个int变量占用四个字节)。
-
new 运算符:用于创建对象和调用构造函数。
-
new 修饰符:在用作修饰符时,new 关键字可以显式隐藏从基类继承的成员。
new 修饰符会用同样的名称创建一个新成员并使原始成员变为隐藏的。 override 修饰符会扩展继承成员的实现。
-
new 约束:用于在泛型声明中约束可能用作类型参数的参数的类型。
void Main() { // 此处编译器会检查Employee是否具有公有的无参构造函数。 // 若没有则会有The Employee must have a public parameterless constructor 错误。 ItemFactory<Employee> EmployeeFactory = new ItemFactory<Employee>(); }
-
限定字符串
用 @ 符号加在字符串前面表示其中的转义字符“不”被处理。
如果我们写一个文件的路径,例如"D:/文本文件"路径下的text.txt文件,不加@符号的话写法如下:
string fileName = "D://文本文件//text.txt";
如果使用@符号就会比较简单:
string fileName = @"D:/文本文件/text.txt";
-
让字符串跨行
有时候一个字符串写在一行中会很长(比如SQL语句),不使用@符号,一种写法是这样的:
string strSQL = "SELECT * FROM HumanResources.Employee AS e" + " INNER JOIN Person.Contact AS c" + " ON e.ContactID = c.ContactID" + " ORDER BY c.LastName";
加上@符号后就可以直接换行了:
string strSQL = @"SELECT * FROM HumanResources.Employee AS e INNER JOIN Person.Contact AS c ON e.ContactID = c.ContactID ORDER BY c.LastName";
-
在标识符中的用法
C#是不允许关键字作为标识符(类名、变量名、方法名、表空间名等)使用的,但如果加上@之后就可以了,例如:
namespace @namespace { class @class { public static void @static(int @int) { if (@int > 0) { Debug.Log("Positive Integer"); } elseif (@int == 0) { Debug.Log("Zero"); } else { Debug.Log("Negative Integer"); } } } }
装箱(boxing)和拆箱(unboxing)是C#类型系统的核心概念
-
是不同于C与C++的新概念!通过装箱和拆箱操作,能够在值类型和引用类型中架起一做桥梁。
-
装箱和拆箱是值类型和引用类型之间相互转换所要执行的操作。
-
装箱是将值类型转换为引用类型,拆箱是将引用类型转换为值类型。
为何需要装箱?(为何要将值类型转为引用类型?)
-
一种最普通的场景是,调用一个含类型为Object的参数的方法,该Object可支持任意类型,以便通用。当你需要将一个值类型(如Int32)传入时,需要装箱。
-
另一种用法是,一个非泛型的容器,同样是为了保证通用,而将元素类型定义为Object。于是,要将值类型数据加入容器时,需要装箱。
装箱/拆箱的内部操作
-
装箱
- 新分配托管堆内存
- 将值类型的实例字段拷贝到新分配的内存中
- 返回托管堆中新分配对象的地址
-
拆箱
- 检查对象实例,确保它是给定值类型的一个装箱值
- 将该值从实例复制到值类型变量中
装箱操作和拆箱操作是要额外耗费cpu和内存资源的,所以在c# 2.0之后引入了泛型来减少装箱操作和拆箱操作的消耗。
-
RunSynchronously():在当前任务调度程序上同步运行任务。
-
Start():启动任务,将其调度到当前任务调度程序执行。
Start()尝试并将任务排队到调度程序,而RunSynchronously()将尝试并内联执行它,如果失败(返回false),它将只是排队。
在Unity的主线程上使用Start()会创建一个新的线程,而RunSynchronously()则会在主线程上执行。
默认情况下,当你使用async/await时,它将在开始请求的原始线程上继续运行(状态机)。但是,如果当前另一个长时间运行的进程已经接管了该线程,那么你就不得不等待它完成。要避免这个问题,可以使用ConfigureAwait的方法和false参数。当你用这个方法的时候,这将告诉Task它可以在任何可用的线程上恢复自己继续运行,而不是等待最初创建它的线程。这将加快响应速度并避免许多死锁。
但是,这里有一点点损失。当你在另一个线程上继续时,线程同步上下文将丢失,因为状态机改变。这里最大的损失是你会失去归属于线程的Culture和Language,其中包含了国家语言时区信息,以及来自原始线程的HttpContext.Current之类的信息。因此,如果你不需要以此来做多语系或操作任何HttpContext类型设置,则可以安全地进行此方法的调用。注意:如果需要Culture或Language,可以始终在await之前存储当前相关状态值,然后在await新线程之后重新应用它。