1.值类型与引用类型
值类型:struct、enum、int、float、char、bool、decimal
引用类型:class delegate interface arrray object string
1)值类型与引用类型的区别
答:值类型在声明后,无论是否赋值,编译器都会为其分配内存,且其值存放在栈上;
引用类型在声明时只会在栈中分配内存,用于存在在堆上的地址,并未为其在堆上分配内存空间,其值存放在堆中,地址存放在栈上。
辅助理解代码:
namespace forTest
{
class Program
{
static void Main(string[] args)
{
//值类型
int a = 0;
int b = 0;
Console.WriteLine("第一次赋值:" + "a = {0}, b = {1}", a, b);
a = 1;
Console.WriteLine("a更改后赋值:" + "a = {0}, b = {1}", a, b);
b = 2;
Console.WriteLine("b更改后赋值:" + "a = {0}, b = {1}", a, b);
Console.ReadKey();
//引用类型
MyClass myClass1 = new MyClass();
myClass1.num = 1;
MyClass myClass2 = myClass1;
Console.WriteLine("第一次赋值:" + "myClass1.num = {0}, myClass2.num = {1}", myClass1.num, myClass2.num);
myClass1.num = 2;
Console.WriteLine("myClass1.num更改赋值后:" + "myClass1.num = {0}, myClass2.num = {1}", myClass1.num, myClass2.num);
myClass2.num = 3;
Console.WriteLine("myClass2.num更改赋值后:" + "myClass1.num = {0}, myClass2.num = {1}", myClass1.num, myClass2.num);
Console.ReadKey();
}
}
class MyClass
{
public int num;
}
}
输出结果:
解释:将值类型的变量赋值给另一个变量,会执行一次赋值,赋值变量包含的值;将引用类型的变量赋值给另一个引用类型变量,它复制的是引用对象的内存地址,在赋值后就会多个变量指向同一个引用对象实例。
2.装箱和拆箱
装箱:将值类型转换为引用类型(object)则为装箱
拆箱:将引用类型转换为值类型则为拆箱
在执行装箱或拆箱操作会损失性能
int x = 100;//值类型--存储在栈上
object obj ;//在栈上分配一个引用类型内存四个字节
obj = x; //先将栈上的X的值复制到堆内存上某一块内存并形成一个对象,然后在栈上寻找一个引用类型的存储位置,将栈内存区的值刷上上述对象所存储在堆内存的地址,实现装箱操作
int y = (int)obj;//声明值类型变量y时,系统会在栈内存上开辟一个四个字节的内存空间(int占用4个字节),然后找到引用类型obj在指在堆内存的内容,并将其强制转换为int类型,并将复制到栈内存所分配给y的地址上,即实现拆箱操作。
3.堆和栈(Heap&Stack)
存放在栈中时需要管理存储顺序,保持着先进后出的原则,它是一片连续的内存区域,由系统自动分配和维护;
堆是无序的,它是一片不连续的内存区域,由用户自己来控制和释放,如果用户自己不释放的且内存达到一定的值后,系统会自动通过垃圾回收器(GC)来回收;
栈内存无需用户管理,也不受GC管理,当栈顶元素使用完毕,立马释放。但是堆需要GC清理
4.GC(Garbage Collection)
当程序需要更多的堆空间时,GC需要进行垃圾清理工作,暂停所有线程,找出所有没有被引用的对象,进行清理,并通知栈中的指针重新指向地址排序后的对象。
GC只能处理托管内存资源的释放,对于非托管资源则不能使用GC进行回收,必须由程序员手动回收,例如FileStream或SqlConnection需要调用Dispose进行资源的回收。
注:在使用filestream进行文本读写时注意释放,否则会导致文本被占用进而以下API执行失败
[DllImport("kernel32")]
private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
5.CLR(Common Language Runtime)
公共语言运行库,负责资源管理(包括内存分配、程序及加载、异常处理、线程同步、垃圾回收等),并保证应用和底层操作系统的分离。
6.静态构造函数
最先被执行的构造函数,且在类中只允许有一个无参的静态构造函数
执行顺序:静态变量>静态构造函数>实例变量>实例构造函数
7.文本读写操作(流)
1)FileStream
2)Stream Reader/Writer
8.序列化与反序列化
序列化:讲对象状态转换为可保持或传输的格式的郭晨。将抽象实例的字段及类的名称转化为字节流,然后将字节流写入数据流
反序列化:将流转换为对象
二者结合可实现存储和传输数据
9.抽象类abstract class和接口interface的异同
相同:
1)都可以被继承
2)都不能被实例化
3)都可以包含方法的声明
不同点:
1)抽象类被子类继承;接口被类实现
2)抽象类只能被单个类继承;接口可继承接口,并可多继承接口
3)抽象基类可以定义字段、属性、方法实现;接口只能定义属性、索引器、时间和方法声明,不能包含字段
4)抽象类可以做方法声明,也可做方法实现;接口只能做方法声明
5)具体派生类必须覆盖(override)抽象基类的抽象方法;派生类必须实现接口的所有方法
6)抽象类是一个不完整的类,需要进一步细化;接口是一个行为规范
7)抽象类中的虚方法或抽象方法必须用public修饰;接口中的所有成员默认为public,不能有private修饰符(也不能用public进行显示修饰)
10.类class和结构体struct的异同
class属于引用类型,是分配在内存堆上的
Struct属于值类型,是分配在内存的栈上的;不能从另一个结构和类继承,本身也不能被继承;没有默认的构造函数,可以添加构造函数,可以不使用new初始化
11.using关键字的使用场景
1)作为指令:用于导入其他命名空间中定义的类型或为命名空间创建别名
2)作为语句:用于定义一个范围,在此范围的末尾将释放对象
解释:当在某个代码段中使用了类的实例,而希望无论因为什么原因,只要离开了这个代码段就自动调用这个类实例的Dispose。 要达到这样的目的,用try…catch来捕捉异常也是可以的,但用using也很方便。
using (Class1
cls1 = new Class1(),
cls2 = new Class1())
{
//the code using cls1, cls2
}
//call the Dispose on cls1 and cls2
12.new关键字的使用场景
1)实例化对象
2)隐藏父类方法
注:当从基类继承了一个(非抽象成员时),也就继承了父类的实现代码。如果是virtual成员,可以override;另外一种方法也能隐藏父类的实现代码(虚成员和非虚成员都可使用):定义与父类相同的方法名,加上关键词new
public class Parent
{
public void Fun()
{
Debug.log("我是爸爸");
}
public virtual void virtualFun()
{
Debug.log("我是妈妈");
}
}
class Son:Parent
{
public new void Fun()
{
Debug.log("我是爸爸的儿子");
}
public new void virtualFun()
{
Debug.log("我是妈妈的儿子");
}
}
13.委托与事件
委托可以把方法作为一个参数传入到另一个方法中,可以理解为指向一个函数的引用
事件是一种特殊的委托;
14.重载(overload)与重写(override)的区别
重载:是方法的名称相同,参数或者参数类型不同;重载是面向过程的概念
重写:是对基类的虚方法进行重写。重写是面向对象的概念
15.return执行顺序
try{}里面有一个return语句,那么finally{}里面的代码在return前执行
16.switch(expression)
其中expression之处任何数据类型,包括null
18.属性和字段的区别
在C#中我们可以非常自由的、毫无访问限制的访问共有字段,但是在某些情况下,我们希望对某些字段的赋值作出一定的限制或是对字段的访问只能是只读或者只写,此时仅仅通过字段是无法做到,此时就用到了属性
19.访问修饰符
1)private:只能被当前类成员访问
2)public :公有访问,不受任何限制
3)protected:保护访问,只能被当前类或者是子类访问
4)internal:内部访问,只能被当前程序集内访问
20.面向对象的主要思想是什么?
封装、继承、多态
21.sealed修饰符有什么特点?
sealed修饰符可以用在类、实例方法、属性中
密封类不能被继承
密封方法会重写基类的方法,但其本身不能在任何派生中进一步重写,当应用于方法或者属性时,sealed修饰符必须始终与override一起使用
22.c#中 string s = null和string s = “”的区别是什么?
string s = null知识初始化了对象,string s = “”初始化对象并且为其分配了空间;
23.强类型语言
C#是强类型语言,因此每个 变量或者对象都必须具有声明类型
24.接口和类有什么异同
不同点:
1)接口不能够实例化
2)接口不包含方法的实现
3)接口、类和结构可从多个接口继承。但是C#类只支持单继承,类只能从一个基类继承实现,类定义可在不同源文件之间进行拆分
相同点:
1)接口、类和结构可从多个接口继承
2)接口类似于抽象基类;继承接口的任何非抽象类型都必须实现接口的所有成员
3)接口可以包含事件、索引器、方法和属性
25.接口是否可以继承接口?抽象类是否可以实现接口?抽象类是否可以继承实体类?
接口是可以继承接口的
抽象类是可以实现接口的
抽象类也可以继承实体类,但是有一个条件,实体类必须要有明确的构造函数
26.构造器是否可以被继承?是否可以被重写?
构造器是不可以被继承的,因此不能够被重写,但是可以实现重载
27.string类是否可以被继承?
不可以,string类是final类不可以被继承
28.C#是否可以直接操作内存
C#是可以对内存进行直接操作的,可以通过指针的方式实现,但是需要在前面加上unsafe。
29.进程和线程应该怎么理解
进程是比线程大的程序执行单元,都是由操作系统所体会的系统运行单元
一个程序至少需要一个进程,一个进程至少需要一个线程,线程的划分尺度要比进行要小,进行拥有独立的内存单元,线程是共享内存,从而极大地提高了程序的运行效率,同一个进行中的多个线程可以并发执行
30.&和&&的区别
&是位运算符,表示按位与运算,&&是逻辑运算符,表示逻辑与(and).
31.a=10,b=15,在不用第三方变量的前提下,把a,b的值互换
a= a+b,b = a- b;a = a-b;
32.lambda表达式
Lambda运算符“=>”的左边列出了需要的参数,如果是一个参数可以直接写 a=>(参数名自己定义),如果多个参数就使用括号括起来,参数之间以,间隔
//lambda表达式
Func<int,int,int> func = new Func<int,int,int >((a,b)=>{return a+b;});
int res = func(134, 123);
Console.WriteLine(res);
Console.ReadKey();
1、数据库操作的相关类
特定类:Connection,Command,CommandBuilder,DataAdapter,DataReader,Parameter,Transaction
共享类:DataSet,DataTable,DataRow,DataColumn,DataRealtion,Constraint,DataColumnMapping,DataTableMapping
1)Connection:开启程序与数据库之间的连接。
2)Command:对数据库发送一些指令。例如增删改查等指令,以及调用存在数据库中的存储过程等。
3)DataAdapter:主要在数据源及DataSet 之间执行传输工作,通过Command 下达命令后,将取得的数据放进DataSet对象中。
4)DataSet:这个对象可视为一个暂存区(Cache),可以把数据库中所查询到的数据保存起来,甚至可以将整个数据库显示出来,DataSet是放在内存中的。
总结:http://ADO.NET 使用Connection对象来连接数据库,使用Command或DataAdapter对象来执行SQL语句,并将执行的结果返回给DataReader或DataAdapter,然后再使用取得的DataReader或DataAdapter对象操作数据结果。
2、事务
3、索引
4、视图
5、存储过程
三、数据结构
1.冒泡排序
1)定义
就是将一个数组中的元素按照从大到小或者从小到大的排序进行排列。
2)实现方法
对数组元素进行多次相邻元素比较和换位判断的方法得到一个按照数组元素大小排序的新数组
3)代码实现
int[] myString = new int[] { 4, 1, 34, 33, 45, 13, 17 };
for (int i = 0; i < myString.Length - i; i++)
{
for (int j = 0; j < myString.Length - i - 1; j++)
{
int s = 0;
if (myString[j]> myString[j+1] )
{
s = myString[j];
myString[j] = myString[j + 1];
myString[j + 1] = s;
}
}
}
foreach (int s in myString)
{
Console.WriteLine(s);
}
Console.ReadKey();