适用场景: 需要一些变量,它的值在运行时不应改变,但在运行之前是未知的。
readonly关键字比const灵活,允许把一个字段设置为常量,但是需要对其初始值进行确认。其规则是可以在构造函数中给可读字段赋值,但不能在其他地方赋值。只读字段还可以是一个实例字段,而不是静态字段,类的每个实例可以有不同的值。
举个例子:如一个程序的不同版本所能同时打开的文档数不同,在升级时在源代码中进行硬编码是不现实的,而是需要一个字段表示这个最大文档数。因此,这个字段必须是只读的,每次启动程序时,从注册表键或其他文件存储中读取。
public class DocumentEditor
{
public static readonly uint MaxDocuments;
static DocumentEditor()
{
MaxDocuments = DoSomeThingToFindOutMaxNumber();
}
private static uint DoSomeThingToFindOutMaxNumber()
{
return 3;
}
}
构建方法:var与new一起使用
var captain = new {FirstName = "James", MiddleName = "T", LastName = "Kirk"};
var person = new {captain.FirstName, person.MiddleName, person.LastName};
使用场景:当仅需要一个小的数据结构时,此时类提供的功能多余我们需要的功能,由于性能原因,最好使用结构。
下面的代码说明了结构的构造函数和属性:
struct Dimensions
{
public double Length;
public double Width;
public Dimensions(double length, double width)
{
Length = length;
Width = width;
}
public double Diagonal
{
get
{
return Math.Sqrt(Length*Length + Width*Width);
}
}
}
结构是值类型,非引用类型。其存储在栈中或存储为内联(inline)。
结构不支持继承。
对于结构,构造函数的工作方式有一些区别,尤其是编译器总是提供一个无参数的默认构造函数,它是不允许替换的。
使用结构,可以指定字段如何在内存中布局
严格来说,字段(除了const)应总是私有的,并由共有属性封装。但是,对于简单的结构,许多开发人员都认为共有字段是可接受的编程方式。
虽然结构是值类型,但在语法上常常可以把他们当作类来处理。因为结构是值类型,所以new运算符与类和其他引用类型的工作方式存在不同。new运算符并不分配堆中的内存,而是只调用相应的构造函数,根据传送给他的参数初始化所有的字段。但对于结构,变量声明实际上是为整个结构在栈中分配空间,所以就可以为它赋值了。
Dimensions point = new Dimensions();
//Dimensions point;
point.Length = 3;
point.Width = 4;
但是直接将未赋值的属性赋值到其他变量中是不合法的。结构遵循其他数据类型都遵循的规则:在使用前所有元素都必须进行初始化。
结构的正面影响是:为结构分配内存时,速度非常快,因为其将内联或者保存在栈中。在超出作用域时,其删除的速度也很快。负面影响时,当其作为参数传递或者赋值时,所有内容都会被复制,而对于类,则只复制引用。这样就会造成性能损失。因此,当作为参数传递时,应添加ref.
结构不能够继承。
结构派生于System.Object类,因此,结构也可以访问System.Object的方法,甚至可以重写。
不能为结构提供其他基类,每个结构都派生于ValueType, ValueType派生于System.Object类。
结构与类的构造方式相同,但是不允许定义无参数的构造函数,因为其已经隐藏在.net运行库中实现。并且,C#的结构中禁止使用无参数的构造函数。
默认构造函数会初始化数值与类型字段,即使是在提供了其他带参数的构造函数下。提供字段的初始值也不能绕过默认构造函数,在结构中直接对字段进行初始化,会造成编译错误,类没有这个问题。
在应用程序代码内实例化一个类或结构时,只要有代码引用它,就会形成强引用。意味着垃圾回收器不会清理引用对象使用的内存。
对于不经常访问的对象,可以创建对象的弱引用。弱引用允许创建和使用对象,但是垃圾回收器运行时,就回回收对象并释放内存。但一般不会这么做。
弱引用,使用WeakReference类创建,因为对象可能在任意时刻被回收,所以在引用该对象前必须确认它存在。
static void Main()
{
WeakReference mathReference = new WeakReference(new MathTest());
MathTest math;
if (mathReference.IsAlive)
{
math = mathReference.Target as MathTest;
math.Value = 30;
Console.WriteLine(math.Value);
Console.WriteLine(math.GetSquare());
}
else
{
Console.WriteLine("Reference is not available.");
}
GC.Collect();
if (mathReference.IsAlive)
{
math = mathReference.Target as MathTest;
}
else
{
Console.WriteLine("Reference is not available.");
}
}
可以给类增加一个析构函数看一下,再加了GC.Collect()后,对象确实是被释放掉了,而不加的话,则没有释放。还有一种用法可以参考下面的。还有一个是,上面的mathReference.IsAlive一直都是True,但是将debug改为release后就可以了。原因可能是当调试开启(Debug,或Debugger运行下)时,CLR可能为了调试方便(比如方便你检查数值),做了一些特殊处理。
https://blog.csdn.net/qq_31967569/article/details/82252142
https://bbs.csdn.net/topics/390913305 //解惑
partial关键字允许把类、结构、方法或接口放在多个文件中。
适用场景:多个开发人员需要访问同一个类,或者某种类型的代码生成器生成了一个类的某部分,所以把类放在多个文件夹下是有益的。
不同文件中的相同的部分类中不同的方法在编译后会同时处于一个新创建的类中,即可同时调用两种方法。
如果类只包含静态的方法和属性,该类就是静态的。
所有.Net类都派生自System.Object。实际上,如果在定义类时没有指定基类,编译器就会自动假定这个类派生自Object。
如果有类的源代码,集成就是给对象添加功能的好方法。但如果没有源代码,则可以使用扩展方法,它允许改变一个类,但不需要该类的源代码。
扩展方法是静态方法,他是类的一部分,但实际上没有放在类的源代码中。
示例:
namespace Wrox
{
public static class MoneyExtension
{
public static void AddToAmount(this Money money, decimal amountToAdd)
{
money.Amount += amountToAdd;
}
}
}
//调用
object.AddToAmount(10M);调用时不显示第一个参数