【C#小知识】C#中一些易混淆概念总结(三)---------结构,GC,静态成员,静态类
目录:
【C#小知识】C#中一些易混淆概念总结(二)
---------------------------------------分割线----------------------------------------------
一,C#中结构
在C#中可以使用struct关键字来定义一个结构,级别与类是一致的,写在命名空间下面。
1)结构中可以定义属性,字段,方法和构造函数。示例代码如下:
//定义结构
struct Point
{ //定义字段
private int x; //封装字段
public int X
{
get { return x; }
set { x = value; }
} //定义方法
public void Result()
{ } //定义构造函数
public Point(int n) {
this.x = n;
//Console.WriteLine(n);
} }
那么,声明类与结构的区别有哪些呢?
①无论如何,C#编译器都会为结构生成无参数的构造函数;
当我们显式的定义无参数的构造函数,编译时会报错,结果如下:
编译器告诉我们,结构不能包含显式的无参数的构造函数
但是这样编写代码时,编译器却不报错,代码如下:
//这里可以调用无参数的构造函数
Point p = new Point();
Console.WriteLine(p.GetType());
运行结果如下:
虽然结构不能显式的声明无参数的构造函数,但是程序员却可以显式的调用结构的无参数的构造函数,说明C#编译器无论如何都会为结构生成无参数的构造函数。
②结构中的字段不能赋初始值;
③在结构的构造函数中必须要对结构体的每一个字段赋值;
当我们不声明显式的构造函数时,可以不对成员字段赋值,但是一旦声明了构造函数,就要对所有的成员字段赋值
对所有的成员字段赋值,代码如下:
//定义构造函数
public Point(int n)
{
this.x = n;
//Console.WriteLine(n);
}
④在构造函数中对属性赋值不认为对字段赋值,属性不一定去操作字段;
所以在构造函数中我们对字段赋初始值的时候,正确的代码应该是
//定义构造函数
public Point(int n)
{
//正确的可以对字段赋初始值
this.x = n; //在构造函数中对属性赋值,但是不一定操作字段
this.X = n;
//Console.WriteLine(n);
}
2)结构体的数值类型问题
C#中的结构是值类型,它的对象和成员字段是分配在栈中的,如下图:
那么当我们写了如下的代码,内存中发生了什么呢?
//这里可以调用无参数的构造函数
Point p = new Point();
//为p的属性赋值
p.X = ;
//将p赋值给Point新的对象p1
Point p1 = p;
Point p1=p发生了什么呢?情况如下:
声明结构体对象可以不使用“new”关键字如果不使用“new”关键字声明结构体对象,因为没有调用构造函数,这个时候结构体对象是没有值的。而结构的构造函数必须为结构的所有字段赋值,所以通过"new"关键字创建结构体对象的时候,这个对象被构造函数初始化就有默认的初始值了。实例代码如下:
class Program
{
static void Main(string[] args)
{
//没有办法调用默认的构造函初始化
Point p;
Console.WriteLine(p); //会调用默认的构造函数对的Point对象初始化
Point p1 = new Point();
Console.WriteLine(p1);
Console.ReadKey(); }
}
//定义结构
struct Point
{
//定义时赋初始值,编译器会报错
private int x;
}
编译的时候会报错:
3)结构体不能使用自动属性
在第一篇文章我写自动属性的时候,反编译源代码,知道自动属性,会生成一个默认字段。而在结构的构造函数中需要对每一个字段赋值,但是编译器不知道这个字段的名字。所以,没有办法使用自动属性。
那么什么时候定义类,什么时候定义结构体呢?
首先我们都知道的是,栈的访问速度相对于堆是比较快的。但是栈的空间相对于堆来说是比较小的。
①当我们要表示一个轻量级的对象,就可以定义结构体,提高访问速度。
②根据传值的影响来选择,当要传递的引用就定义类,当要传递的是“拷贝”就定义结构体。
二,关于GC(.NET的垃圾回收)
1)分配在栈中的空间变量,一旦出了该变量的作用域就会被CLR立即回收;如下代码:
//定义值类型的n当,程序出了main函数后n在栈中占用的空间就会被CLR立即回收
static void Main(string[] args)
{
int n = ;
Console.WriteLine(n);
}
2)分配在堆里面的对象,当没有任何变量的引用时,这个对象就会被标记为垃圾对象,等待垃圾回收器的回收;
GC会定时清理堆空间中的垃圾对象,这个时间频率是程序员无法控制的,是由CLR决定的。所以,当一个对象被标记为垃圾对象的时候,不一定会被立即回收。
3)析构函数
在回收垃圾对象的时候,析构函数被GC自动调用。主要是执行一些清理善后工作。
析构函数没有访问修饰符,不能有你参数,使用“~”来修饰。 如下面的代码示例:
class Program
{
//定义值类型的n当,程序出了main函数后n在栈中占用的空间就会被CLR立即回收
static void Main(string[] args)
{
int n = ; OperateFile operate = new OperateFile(); operate.FileWrite();
//执行完写操作后,会调用该类的析构函数,释放对文件对象的控制
//Console.WriteLine(n);
}
} //定义操作硬盘上文件上的类
class OperateFile
{
//定义写文件的方法
public void FileWrite()
{ } //定义调用该类结束后,所要执行的动作
~OperateFile()
{
//释放对操作文件对象的控制
}
}
三,静态成员和实例成员的区别:
静态成员是需要通过static关键字来修饰的,而实例成员不用static关键字修饰。他们区别如下代码:
class Program
{
static void Main(string[] args)
{
//静态成员属于类,可以直接通过“类名.静态成员”的方式访问
Person.Run(); //实例成员属于对象,需要通过“对象名.实例成员”来访问
Person p = new Person();
p.Sing();
}
} class Person
{
//静态成员变量
private static int nAge;
//实例成员变量
private string strName; public static void Run()
{
Console.WriteLine("我会奔跑!");
} public void Sing()
{
Console.WriteLine("我会唱歌");
}
}
当类第一次被加载的时候(就是该类第一次被加载到内存当中),该类下面的所有静态的成员都会被加载。实例成员有多少对象,就会创建多少对象。
而静态成员只被加载到静态存储区,只被创建一次,且直到程序退出时才会被释放。
看下面的代码:
class Program
{
static void Main(string[] args)
{ Person p = new Person();
Person p1 = new Person();
Person p2 = new Person(); }
} class Person
{
//静态成员变量
private static int nAge;
//实例成员变量
private string strName; public static void Run()
{
Console.WriteLine("我会奔跑!");
} public void Sing()
{
Console.WriteLine("我会唱歌");
}
}
那么在内存中发生了什么呢?如下图:
由上面显然可知,定义静态的成员是可以影响程序的执行效率的。那么什么时候定义静态的成员变量呢?
①变量需要被共享的时候②方法需要被反复的调用的时候
2)在静态方法中不能直接调用实例成员。
当类第一次被加载的时候,静态成员已经被加载到静态存储区,此时类的对象还有可能能没有创建,所以静态方法中不能调用类成员字段。实例代码如下:
this和base关键字都不能在静态方法中使用。
②可以创建类的对象指明对象的成员在静态方法中操作,代码如下:
public static void Run()
{
Person p = new Person();
p.strName = "强子";
Console.WriteLine("我会奔跑!");
}
③在实例成员中肯定可以调用静态方法,因为这个时候静态成员肯定存在,代码如下:
public static void Run()
{
Person p = new Person();
p.strName = "强子";
Console.WriteLine("我会奔跑!");
} public void Sing()
{
//实例方法被调用的时候,对象实例一定会被创建,所以可以在实例方法中访问实例的字段
this.strName = "子强";
strName = "子强"; //调用静态成员
Run();
Console.WriteLine("我会唱歌");
}
静态成员和实例成员的对比:
①生命周期不一样
静态成员只有在程序结束时才会释放,而实例成员没有对象引用时就会释放
②内存中存储的位置不一样
静态成员存放在静态存储区,实例成员在托管堆中。
四,静态类
①静态类被static关键字修饰
//定义两个静态类
static class Person
{ } internal static class Cat
{ }
②静态类中只能生命静态的成员变量,否则会报错(因为访问该实例成员的时候,类的对象可能还没有被创建)
③静态类中不能有实例的构造函数(如果有实例的构造函数,则该静态类能被实例化,都是静态成员,没有实例成员被调用)
正确的声明方法:
static class Person
{
//private int nAge;
private static string strName; static Person()
{
}
}
④静态类不能被继承,反编译刚才的两个类,结果如下:
会发现静态类的本质是一个抽象密封类,所以不能被继承和实例化。所以,静态类的构造函数,不能有访问修饰符
2)那么什么时候声明静态类呢?
如果这个类下面的所有成员的都需要被共享,可以把这个类声明为静态类。
且在一般对象中不能声明静态类型的变量(访问该静态变量时,可能该对象还没有被创建)。
3)静态类的构造函数
静态类可以有静态的构造函数(且所有类都可以有静态的构造函数),如下代码:
class Program
{
static void Main(string[] args)
{
Cat c;
Cat c1 = new Cat(); Console.ReadKey();
}
} class Cat
{
private int n;
public string strName; //实例构造函数
public Cat()
{
Console.WriteLine("看谁先执行2");
} //静态构造函数
static Cat()
{
Console.WriteLine("看谁先执行1");
} }
执行结果如下:
由此我们可以知道,静态的构造函数会先于实例构造函数执行
且
//不执行静态构造函数
Cat c;
当我们在Main()函数中添加如下的代码是:
static void Main(string[] args)
{
//不执行静态构造函数
Cat c;
Cat c1 = new Cat();
Cat c2 = new Cat(); Console.ReadKey();
}
运行结果如下:
说明静态的构造函数只执行了一次。
---------------------------------------------分割线-----------------------------------------------
好吧这次的分享风到此结束。希望对大家对理解C#基础概念知识能有所帮助。
【C#小知识】C#中一些易混淆概念总结(三)---------结构,GC,静态成员,静态类的更多相关文章
- 【C#小知识】C#中一些易混淆概念总结(七)---------解析抽象类,抽象方法
目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...
- 【C#小知识】C#中一些易混淆概念总结(六)---------解析里氏替换原则,虚方法 分类: C# 2014-02-08 01:53 1826人阅读 评论(0) 收藏
目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...
- 【C#小知识】C#中一些易混淆概念总结(五)---------继承 分类: C# 2014-02-06 22:05 1106人阅读 评论(0) 收藏
目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...
- 【C#小知识】C#中一些易混淆概念总结(五)---------深入解析C#继承
目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 [C#小知识]C#中一些易混淆概念总结(二)--------构造函数,this关键字 ...
- 【C#小知识】C#中一些易混淆概念总结(四)---------解析Console.WriteLine() 分类: C# 2014-02-05 17:18 1060人阅读 评论(0) 收藏
目录: [C#小知识]C#中一些易混淆概念总结 [C#小知识]C#中一些易混淆概念总结(二) [C#小知识]C#中一些易混淆概念总结(三) ------------------------------ ...
- 【C#小知识】C#中一些易混淆概念总结(二)--------构造函数,this关键字,部分类,枚举 分类: C# 2014-02-03 01:24 1576人阅读 评论(0) 收藏
目录: [C#小知识]C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用 继上篇对一些C#概念问题进行细节的剖析以后,收获颇多.以前,读书的时候,一句话一掠而 ...
- C#中一些易混淆概念总结
C#中一些易混淆概念 这几天一直在复习C#基础知识,过程中也发现了自己以前理解不清楚和混淆的概念.现在给大家分享出来我的笔记: 一,.NET平台的重要组成部分都是有哪些 1)FCL (所谓的.NET框 ...
- 【C#小知识】C#中一些易混淆概念总结---------数据类型存储,方法调用,out和ref参数的使用
这几天一直在复习C#基础知识,过程中也发现了自己以前理解不清楚和混淆的概念.现在给大家分享出来我的笔记: 一,.NET平台的重要组成部分都是有哪些 1)FCL (所谓的.NET框架类库) 这些类是微软 ...
- 【C#小知识】C#中一些易混淆概念总结(八)---------解析接口 分类: C# 2014-02-18 00:09 2336人阅读 评论(4) 收藏
这一篇主要来解析关于面向对象中最总要的一个概念--接口. 对于接口来说,C#是有规定使用Interface关键字来声明接口.它的声明是和类一致的.可以说接口就是一个特殊的抽象类.如下代码: cl ...
随机推荐
- UVaLive 2531 The K-League (网络流)
题意:有 n 个队伍进行比赛,每个队伍比赛数目是一样的,每场恰好一个胜一个负,给定每个队伍当前胜的场数败的数目,以及两个队伍剩下的比赛场数,问你冠军队伍可能是哪些队. 析:对每个队伍 i 进行判断是不 ...
- Codeforces807 C. Success Rate 2017-05-08 23:27 91人阅读 评论(0) 收藏
C. Success Rate time limit per test 2 seconds memory limit per test 256 megabytes input standard inp ...
- sublime text 插件 -- 获取文件名到剪贴板
日常开发使用 sublime text 有好长一段时间了,有时候想拷贝当前正在编辑的文件名时发现没有很快捷的方法,一般都是先点击右键菜单栏中的 Reveal in Side Bar 对文件进行定位(在 ...
- bolg迁移
博客已迁移至:http://www.s0nnet.com 欢迎大家继续关注!!! 2015-7-4
- java web 中的WEB-INF文件夹
WEB-INF下的东西是禁止直接访问的.如果这个页面是你的,要想让人访问最好不要放在这个目录下.如果一定放在那里.你可以使用:request.getRequestDispatcher("/W ...
- 在AbpZero中hangfire后台作业的使用——开启hangfire
AbpZero框架已经集成了hangfire,但它默认是关闭的,我们可以在运行站点下的Startup.cs文件中把这行代码注释取消就行了,代码如下: //Hangfire (Enable to ...
- telerik:RadGrid 表格中删除数据
<telerik:RadGrid OnItemCommand=" Height="490px" Culture="zh-CN" CssClass ...
- Win(Phone)10开发第(2)弹,导出APPX包并签名部署
当我们新建一个win10 uap项目,如果想导出测试包,需要点击项目名称,选择商店-导出应用包,这个时候会生成一个文件夹,包含appx和ps1等文件. powershell运行Add-AppDevPa ...
- CentOS7 - 给VMwear Workstation 15安装VMwear tools
操作系统:CentOS 7 VMwear Workstation :15 Pro 最简单方法,打开shell,输入下面命令: yum install open-vm-tools -y 参考: http ...
- Python3.5 学习九
进程与线程 线程(Thread)是计算机运算调度的最小单位,它存在于进程中,是实际运作单位.每个进程都可能并发多线程. 每一个程序的内存是独立的. 线程:是操作系统最小的运算调度单位,是一串指令的集合 ...