.NET中的静态与非静态
静态类 vs 普通类
静态类与普通类的区别有以下几点:
1)静态类无法实例化而普通类可以;
2)静态类只能从System.Object基类继承;普通可以继承其它任何非static和非sealed类,但也只能继承一个类;
3)静态类不能继承接口;普通类可以继承多个接口;
4)静态类只能包含静态成员;普通类可以包含静态和非静态成员;
5)静态类不能作为字段,方法参数和局部变量使用;普通类可以;
静态类的意义:
可以用静态类封装一组不与任何对象相关联的方法,如Math类,Console类。
静态构造函数 vs 实例构造函数
静态构造函数与实例构造函数的区别有以下几点:
1)静态构造函数属于类,在第一次用到该类时执行且只执行一次;实例构造函数属于对象,在每次初始化一个新对象的时候都会执行;
2)静态构造函数只能定义一次,且不能包含参数;实例构造函数可以定义重载,且可以包含参数;
3)静态构造函数只能访问类型的静态字段;实例构造函数可以访问类型的静态和非静态字段;
4)静态类不能包含访问修饰符,默认为private.
静态构造函数的意义:
设置类型的初始化,例如初始化类型需要的实例对象,为类型的静态字段赋值等。
静态方法 vs 实例方法
静态方法与实例方法的区别有以下几点:
1)静态方法属于类,通过类来调用;实例方法属于对象,通过对象来调用;
2)静态方法不能访问类的非静态成员;
静态方法的意义:
完成一个与特定对象无关的功能。
静态字段 vs 非静态字段
静态字段与非静态字段的区别:
静态字段属于类,通过类来调用;非静态字段属于对象,通过对象来调用。
静态字段的意义:
可以用静态字段来记录一些属于类本身的信息。
代码演示
1 public class Test 2 { 3 public int i = 10; 4 public static int j = 20; 5 public int k; 6 7 public Test() 8 { 9 Console.WriteLine("i is a non-static field, its value is {0}", i); 10 Console.WriteLine("j is a static field, its value is {0}", j); 11 } 12 13 public Test(int k) 14 { 15 this.k = k; 16 Console.WriteLine("i is a non-static field, its value is {0}", i); 17 Console.WriteLine("j is a static field, its value is {0}", j); 18 Console.WriteLine("k is a non-static field, its value is {0}", k); 19 } 20 21 static Test() 22 { 23 Console.WriteLine("I am a static constructor, I couldn't contain any parameters!"); 24 Console.WriteLine("I couldn't access to the non-static field i, I can only access to the static field j, the value of j is {0}", j); 25 } 26 27 public void Print() 28 { 29 Console.WriteLine("I am a instance method, I can access both the non-static field and the static field!"); 30 Console.WriteLine("The value of i is {0} and the value of j is {1}", i, j); 31 } 32 33 public static void StaticPrint() 34 { 35 Console.WriteLine("I am a static method, I couldnt access to the non-static field i, I can only access to the static field j, the value of j is {0}", j); 36 } 37 }
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Test t1 = new Test(); 6 Test t2 = new Test(30); 7 8 Console.WriteLine(Test.j); 9 Console.WriteLine(t1.i); 10 Console.WriteLine(t2.k); 11 12 Test.StaticPrint(); 13 t1.Print(); 14 Console.Read(); 15 } 16 }
运行结果
从运行结果可以看出,在Main方法中创建了两个Test对象,静态构造函数只执行了一次,且先于实例构造函数执行,实例构造函数在两次实例化过程中各执行了一次。同时可以看到我们是通过Test.j来调用的j字段,而i字段和k字段的调用则通过Test的两个对象t1和t2调用。同样的,静态方法StaticPrint也是用Test类来调用,而实例方法Print通过Test类的对象来调用。
忽略基础知识点梳理
今天打开一本《C#高级编程》值类型与引用类型翻看,发现有些熟悉的知识点跟以往认识很不一样,今天对某些忽略点的在认识做一些总结。以便深刻理解,倘若你也遇到过,以此共享!
预定义数据类型:
这个里面有个重心点也是很多面试点就是值类型与引用类型。本人已多次总结,这里不再过多解释。那么我想关心什么是预定义类型?预定义类型多么?有多少呢?
预定义类型是具有CTS标准规范的类型,他们并不内置在C#中,而是内置的.NET Framework中。其他类型都来源于预定义类型的继承。C#预定义类型一共有15个,其中值类型13个(sbyte,short,int,long,byte,ushort,uint,ulong)2个浮点类型(float,dpuble)和1个decimal型,1个bool型,1个字符型(char)和2个预定义引用类型(object,string),object父类毋庸置疑,但是string的引用类型就显得比较特殊了。
String引用类型有何特殊?
static void Main(string[] args)
{
string s1 = "hello Tom!";
string s2 = s1;
Console.WriteLine(s1);
Console.WriteLine(s2);
Console.Read();
}
输出结果1:
修改s1数据:
static void Main(string[] args)
{
string s1 = "hello Tom!";
string s2 = s1;
s1 = "New hello Tom!";
Console.WriteLine(s1);
Console.WriteLine(s2);
Console.Read();
}
运行结果2:
很明显结果是不一致的。原因在于:
结果1:s1引用类型分配在堆中。s2对象指向s1地址索引这个没问题!相信大家都能理解,这个不在详细描述,随后咱们反汇编一下!
结果2:s1原先在堆分配内存中分配地址1(hello Tom!),现在对s1对象重新赋值。相当于new s1.那么堆自然会给s1分配新内存地址2(new hello Tom!).然而s2依旧指向地址1(hello Tom!)中.运行后自然结果不同。
为了详细剖析咱们想想。根据结果不同,肯定s2的内存分配大小更大,而且在调用时s1和s2在第一次调用同一个地址,第二次肯定不同。我们的猜想对于不对?需要实际验证,调用两者反汇编代码:
IL1:
IL2:结果2地内存大小偏大,我们猜想一正确。IL_0009是加载新的string,我们验证没错。
下面了解下有关编译C#文件更多内容
C#代码是托管代码我们所知道的。其原理就是c#代码经过相应的C#编译器(CSC.exe)编译的IL语言。这一点与C,C++,JAVA都是一样的,它们也同样对应相应编译器编译到IL。这里主要介绍C#编译器。其编译选项主要有哪些呢?
1./t:exe 输出控制台应用程序。
2,/t:library 输出带有清单(Il和元数据等)类库
3,/t:modult 输出没有清单的组件
4,/t:winexe 输出windows应用程序(么有控制台窗口)
具体使用:咱们以类库Librayry.cs文件为例。其经过生成后为Librayry.dll。命令编译为:csc /t:library Librayry.cs(同vs中生成一样效果)
控制台I/O问题,也许还有你所不知道的?
1,一个算法输出如下格式:1,空格会显示输出,{m,n}m是表示第m个索引一般0到m,n是宽度值
int i=330;
int j = 30;
Console.WriteLine(" {0,4}\n+{1,4}\n-----\n {2,4}", i, j, i + j);
Console.ReadKey();
2,带有美元符号控制格式输出
decimal i=345.34m;
decimal j = 76.7m;
Console.WriteLine(" {0,9:C2}\n+{1,9:C2}\n-----------\n {2,9:C2}", i, j, i + j);
Console.ReadKey();
3,也可以占位符表示:
double d = 0.544;
Console.WriteLine(" {0:#.00}", d);
Console.ReadKey();
下面就介绍下C#预处理器指令吧!
熟悉c与C++开发的预定义指令一定也不陌生,但是我开始学习C#时以为C#没有预定义概念,应该平常就不会用到它。其实这也正是其作为托管代码的原因所在,C#同样具有与预处理指令。就像内存分配一样,C#不需要手动分配,托管堆会自动分配(上面string分析中结果1的30个大小内存就是自动分配,记得吧?)预处理指令很好认,开头都是#
1,#define和#undef
这里的#define不是实际代码的一部分,只是告诉编译器它定义对象的存在。只在编译代码时存在
#undef,显而易见,这个就相当于删除定义啦
2,#if,#elif(else if),#else ,#endif
这里同样是成对存在的,使用方法与平常if语句一样,但是注意这里;代码演示吧!
这是粘贴出来的,原始定义debug,这里不存在,所有代码编译不会通过,代码部分为灰色。
我强制拿一句Console.ReadKey();运行结果是空白:
3,#warning和#error
这个大家肯定非常熟悉,我们编译出错警告都会遇到,究竟怎么出现呢?原理是什么?
当编译器遇到它们两个会产生警告和错误。如果编译器遇到#warning时候,会显示其指令后面的代码,这个过程也是C#对应编译器实现的。然后继续往下运行。但是如果遇到#error会显示错误信息,立即退出编译,并且不会产生IL代码。即便查看也是没有的
4#region和#endregion
#region 预编译#If应用
#if debug
double d = 0.544;
Console.WriteLine(" {0:#.00}", d);
Console.ReadKey();
#endif
Console.ReadKey();
#endregion
点击节点
这个过程中编译器识别这对指令,可以让代码块折叠,更好代码布局和显示。这个过程中它们好像没有什么用,不影响编译过程。
C#编程规则异常重要!
C#规范编程,这样对于自己也是好的风格,思路清晰,让别人看起来也清晰明了。特别团队合作和别人维护都很重要!(个人总结几点)
1,命名空间异常重要,有时候可能在你都计算机可以,移植到另一台就命名冲突。故采用公司名/个人名.项目名称:如zhangsan.demo
2,私有变量头字母小写:如myTd
3,公用的或者保护类型:如MyId
4,必须字母或者下划线开头,可以追加数字:_myId
5,不能包含关键字,但是加@可以:如@int
6属性和方法尽可能让人能够一看即懂:如:SetPassword()
到此,今天所看到的的细节问题基本总结完了。明天由于还要早起,暂时到这吧!