码迷,mamicode.com
首页 > Windows程序 > 详细

C#基础小记(一)

时间:2018-01-17 20:06:03      阅读:239      评论:0      收藏:0      [点我收藏+]

标签:区分   响应   compiler   method   col   不能被继承   通过   抽象   处理   

1.值类型和引用类型 

参考(http://blog.csdn.net/qiaoquan3/article/details/51202926)

 

值类型和引用类型的使用范围:

C#的值类型包括:结构体(数值类型、bool型、用户定义的结构体),枚举,可空类型。
C#的引用类型包括:数组,用户定义的类、接口、委托,object,字符串。

 

值类型和引用类型储存的方式:

C#中值类型的变量直接存储数据,而引用类型的变量持有的是数据的引用,数据存储在数据堆中。

 

//值类型直接向栈中储存具体数据,引用类型储存的是数据的引用,也就是数据存储在数据堆上的地址,也可以理解为标识。

 

值类型和引用类型的继承类:

引用类型和值类型都继承自System.Object类。不同的是,几乎所有的引用类型都直接从System.Object继承,

而值类型则继承其子类,即 直接继承System.ValueTypeSystem.ValueType直接派生于System.Object

C#的所有值类型均隐式派生自System.ValueType

 

值类型和引用类型的区别:

引用类型与值类型相同的是,结构体也可以实现接口;

引用类型可以派生出新的类型,而值类型不能;

引用类型可以包含null值,值类型不能(可空类型功能允许将 null 赋给值类型);

引用类型变量的赋值只复制对对象的引用,而不复制对象本身。而将一个值类型变量赋给另一个值类型变量时,将复制包含的值

 

 

 

 

2.堆和栈  

参考(http://blog.csdn.net/wolenski/article/details/7951961

 

stackheap是两种内存分配的两个统称。

stackheap的第一个区别就是申请方式不同:stack是系统自动分配空间的,而heap则是程序员根据需要自己申请的空间。

每一个线程都有一个stack,但是每一个应用程序通常都只有一个heap

 

stackheap回收方式的区别:

由于stack上的空间是自动分配自动回收的,所以stack上的数据的生存周期只是在函数的运行过程中,运行后就释放掉,不可以再访问。而heap上的数据只要程序员不释放空间,就一直可以访问到,不过缺点是一旦忘记释放会造成内存泄露。

 

stackheap申请后系统的响应:

stack:只要stack的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示stack溢出。

heap:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的heap

 

stackheap申请效率的比较:

stack:由系统自动分配,速度较快。但程序员是无法控制的。

heap:是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。

 

 

总结:1.在创建stack的时候,stack的大小就固定了,因为stack需要连续占用一段时间。

        heap的大小是动态的,其分配和释放也是动态的。

      2.如果stack的数据过多,就会爆栈,而heap如果也爆了,就说明内存也一起爆了。

      3.每个函数的stack都是相对独立的,但是一个应用程序的heap是被所有stack所共享的。

 

特殊案例:

考虑数组:

int[] reference = new int[100];

 

根据定义,数组都是引用类型,所以int数组当然是引用类型(即reference.GetType().IsValueType为false)。

int数组的元素都是int,根据定义,int是值类型(即reference[i].GetType().IsValueType为true)。那么引用类型数组中的值类型元素究竟位于栈还是堆?

如果用WinDbg去看reference[i]在内存中的具体位置,就会发现它们并不在栈上,而是在托管堆上。

实际上,对于数组:

TestType[] testTypes = new TestType[100];

 

如果TestType是值类型,则会一次在托管堆上为100个值类型的元素分配存储空间,并自动初始化这100个元素,将这100个元素存储到这块内存里。

如果TestType是引用类型,则会先在托管堆为testTypes分配一次空间,并且这时不会自动初始化任何元素(即testTypes[i]均为null)。等到以后有代码初始化某个元素的时候,这个引用类型元素的存储空间才会被分配在托管堆上。

 

 

 

3.结构体

参考(https://baike.baidu.com/item/%E7%BB%93%E6%9E%84%E4%BD%93?fr=aladdin

 

结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,叫做结构。

 

结构体的定义如下所示,

struct为结构体关键字,

tag为结构体的标志,

member-list为结构体成员列表,其必须列出其所有成员;

variable-list为此结构体声明的变量。

(一般情况下,tagmember-listvariable-list3部分至少要出现2个)

 

规范:

 struct tag {

 member-list

 } variable-list ;

 

例子:

struct simple { //struct为结构体关键字,simple为结构体标志

    int a;

    char b;

    double c; //int a ,char b ,double c 为结构体成员

} s1;//s1为结构体声明的变量

 

结构体作用:

 

结构体和其他类型基础数据类型一样,例如int类型,char类型 只不过结构体可以做成你想要的数据类型。以方便日后的使用。

结构体在函数中的作用不是简便,其最主要的作用就是封装。封装的好处就是可以再次利用。

让使用者不必关心这个是什么,只要根据定义使用就可以了

 

 

 

4.构造函数

 

构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载

 

构造函数是在创建给定类型的对象时执行的类方法。构造函数具有与类相同的名称,它通常初始化新对象的数据成员

 

构造函数的特点:

 

1.它的函数名与类名相同;

2.它可以重载;

3.不能指定返回类型,即使是void也不行;

4.虽然在一般情况下,构造函数不被显式调用,而是在创建对象时自动被调用。但是并不是不能被显式调用。有些时候是一定要显式调用的,只要是父类有带参的构造函数,在子类中就必须显式的调用父类的构造函数,因为子类的构造器在实例化时无法找到父类的构造函数(当父类有自己写的无参构造函数时,子类也不用显式调用)。

 

一般情况下,如果没有提供任何构造函数,编译器会在后台创建一个默认的构造函数。这是一个非常基本的构造函数,它只能把所有的成员字段初始化为标准的默认值(例如,引用类型为空引用,数字数据类型为0boolfalse)。

注意:如果提供了带参数的构造函数,编译器就不会自动提供默认的构造函数,只有在没有定义任何构造函数时,编译器才会自动提供默认的构造函数。

 

案例:

在下面的示例中,使用一个简单的构造函数定义了名为 Taxi 的类。然后使用 new运算符来实例化该类。在为新对象分配内存之后,new运算符立即调用 Taxi 构造函数。

 

publicclass Taxi

{

public bool isInitialized;

public Taxi()

{

isInitialized = true;

}

}

class TestTaxi

{

static void Main()

{

Taxi t = new Taxi();

System.Console.WriteLine(t.isInitialized);

}

}

 

5.静态

一、静态成员

1、通过static关键字修饰,是属于类,实例成员属于对象,在这个类第一次加载的时候,这个类下面的所有静态成员会被加载。

2、静态成员只被创建一次,所以静态成员只有一份,实例成员有多少个对象,就有多少份。

3、类加载的时候,所有的静态成员就会被创建在静态存储区里面,一旦创建直到程序退出,才会被回收。

4、变量需要被共享的时候,方法需要被反复调用的时候,就可以把这些成员定义为静态成员。

5、在静态方法中,不能直接调用实例成员,因为静态方法被调用的时候,对象还有可能不存在。

6、在实例方法中,可以调用静态成员,因为这个时候静态成员肯定存在。

 

 

二、静态成员和实例成员的区别

 

1、生命周期不一样。

2、在内存中存储的位置不一样。

 

三、静态类

 

1、被static关键字修饰的类。

2、静态类里面只能声明静态成员。

3、静态类的本质,是一个抽象的密封类,所以不能被继承,也不能被实例化。

4、如果一个类下面的所有成员,都需要被共享,那么可以把这个类定义为静态类。

 

四、静态构造函数

 

1、这个类的成员,第一次被访问之前,就会执行静态构造函数。

2、静态构造函数只被执行一次。

 

6.字段和属性

字段和属性是C#面向对象模式中的两个概念。

字段(field) 用来存储数值或对象的真正实体,属性(property) 对字段的封装(也不一定- -) get段落和set段落 ,字段和属性都属于类的成员。

属性自带读写器,也就是get(只读)和set(只写)。

案例:

Class test{

int val;

Public int value{

get{return this.val;}

set{this.val=value;}

}

}//它包含一个字段和一个有公开读写访问权的属性

 

 

7.Refout参数

 

1.ref参数

一般通过值传送变量是默认的,也可以迫使值参数通过引用传送给方法。为此,要是用ref关键字。如果把一个参数传递给方法,且这个方法的输出参数前带有ref关键字,则该方法对变量所做的任何改变都会影响原来对象的值。

 

案例:

Static void SomeFunction(int[] ints,ref int i)

{

Ints[0]=100;

I=100;

}

 

在调用该方法时,还需要添加ref关键字:

SomeFunction(ints,ref i);

 

最后,C#仍要求对传递给方法的参数进行初始化。在传递给方法之前,无论是按值传递,还是按引用传递,变量都必须初始化;

 

2.out参数

编译器使用Out关键字来初始化。在方法的输入参数前面加上out关键字时,传递给该方法的变量可以不初始化。该变量通过引用传送,所以在从被调用的方法中返回时,方法对该变量的任何改变都会保留下来。在调用该方法时,还需要使用out关键字,与在定义该方式一样:

Static void SomeFunction(out int i)

{

i=100;

}

Public static int Main()

{

Int i;

SomeFunction(out i);

Console.WriteLine(i);

Return 0;

}

 

 

 

3.ref out的区别:

(1)ref传进去的参数必须在调用前初始化,out不必,即:
int i;
SomeMethod( ref i );//语法错误
SomeMethod( out i );//通过

(2)ref传进去的参数在函数内部可以直接使用,而out不可:
public void SomeMethod(ref int i)
{
   int j=i;//通过
   //...
}
public void SomeMethod(out int i)
{
   int j=i;//语法错误

}

(3)ref传进去的参数在函数内部可以不被修改,但out必须在离开函数体前进行赋值。ref在参数传递之前必须初始化;而out则在传递前不必初始化,且在 ... 值类型与引用类型之间的转换过程称为装箱与拆箱。

4.refout的重载

ref 和 out 关键字在运行时的处理方式不同,但在编译时的处理方式相同。因此,如果一个方法采用 ref 参数,而另一个方法采用 out 参数,则无法重载这两个方法。例如,从编译的角度来看,以下代码中的两个方法是完全相同的,因此将不会编译以下代码:

class CS0663_Example

{

    // Compiler error CS0663: "Cannot define overloaded

   // methods that differ only on ref and out".

   public void SampleMethod(out int i) { }

   public void SampleMethod(ref int i) { }

}

 

但是,如果一个方法采用 ref 或 out 参数,而另一个方法不采用这两类参数,则可以进行重载,如下所示:

class RefOutOverloadExample

{

    public void SampleMethod(int i) { }

    public void SampleMethod(out int i) { }

}

总结:

1.  ref的使用:使用ref进行参数的传递时,该参数在创建时,必须设置其初始值,且ref侧重于修改;

  2. out的使用: 采用out参数传递时,该参数在创建时,可以不设置初始值,但是在方法中必须初始化,out侧重于输出;

注释:当希望方法返回多个值时,可以用out,并且一个方法中的参数可以有一个或多个out参数;使用 out 参数,必须将参数作为 out 参数显式传递到方法中,但是out 参数的值不会被传递到 方法中,且属性不是变量,不能作为 out 参数传递。

    ref是有进有出,而out是只出不进。

 

 

 

 

C#基础小记(一)

标签:区分   响应   compiler   method   col   不能被继承   通过   抽象   处理   

原文地址:https://www.cnblogs.com/wyfloveyanqi/p/8304111.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!