C#基础总结

CLR

CLR公共语言运行时,核心功能:内存管理,程序集加载,安全性,异常处理,线程同步等。

using

  • using 命名空间名称
  • using 别名 using aclasss=Namespace1.myClass;
  • using 语句,用于定义一个范围,在此范围末尾释放对象。using语句对象必须实现IDisposable接口。此接口实现了Dispose方法,用于释放资源。

循环删除list单个元素

不能使用foreach,因为每次删除会导致集合大小和索引值发生变化,提示集合已被修改,无法执行枚举操作
使用for 倒排删除,或者每次删除之后,做i–处理

foreach/for

优势:

  1. foreach 语句简洁
  2. 比for效率高(c#是强类型检查,for循环访问数组时,要对索引的有效值进行检查)
  3. 类型转换方面,foreach不用显示进行类型转换
  4. foreach每循环完一个元素,就会释放对应的资源

劣势:

  1. foreach使用完的时候,会释放使用完的资源,所以会造成额外的gc开销
  2. foreach是只读循环,所以在循环时,无法对数组集合进行修改
  3. 数组中的每一项类型必须与其他项类型相同

值类型与引用类型以及堆栈等

引用类型和值类型都继承自System.Object类。不同的是,几乎所有的引用类型都直接从System.Object继承,而值类型则继承其子类,即直接继承System.ValueType。
值类型和引用类型都可以继承接口
interface Itest
{
void test();
}
struct TestStruct : Itest
{
public void test()
{
throw new NotImplementedException();
}
}

C#类型
不同点

  1. 值类型分配在堆栈上,引用类型是在托管堆上分配的。这里需要指出一点:如果一个引用类型中的某个属性是值类型,这个值类型的属性是分配在托管堆上的
  2. 所有的值类型都是隐式密封的(sealed),例如 :你不可能继承int 来构造自己的类型。
  3. 值类型的每一次赋值都会执行一次逐字段的复制,所以如果是频繁赋值也会造成性能上的压力,引用类型的赋值只是指针的传递,其实也是生成新的指针实例。
  4. 引用类型额外有类型对象指针和同步块索引,值类型是没有的。所以我们平时使用lock 锁的对象不可能是值类型,因为值类型没有同步块索引
    在这里插入图片描述
    string 继承System.Object,所以是引用类型
    堆(heap):
    堆是从下往上分配,所以已用的空间在自由空间下面,C#中所有引用类型的对象分配在托管堆上,托管堆在内存上是连续分配的,并且内存对象的释放受垃圾收集机制的管理,效率相对于栈来说要低的多。
    栈(stack):
    栈是自上向下进行填充,即由高内存地址指向低内存地址,并且内存分配是连续的,C#中所有的值类型和引用类型的引用都分配在栈上,栈根据后进先出的原则,依次对分配和释放内存对象。
    对象内存的分配与销毁
    当一个类的实例对象创建的时候,这个对象的不同成员按类别被分配到了不同的内存区域,值类型和引用类型的指针被分配到了栈上,引用类型的实例对象被分配到了托管堆上,静态成员被分配到了全局数据区。此时栈上的指针会指向堆上的对象。当对象使用完以后,引用和实际对象的联系就会断开,从而从而使对象冬眠。因为栈具有自我维护性,它的内存管理可以通过操作系统来完成,而此时堆上的冬眠对象就需要通过垃圾回收器(GC)使用一定的算法来进行回收,释放对象所占据的内存。
    C#中的深拷贝与浅拷贝
    深拷贝:指的是拷贝对象的时候,不仅把对象的引用进行复制,还把该对象引用的值一块拷贝。拷贝对象和源对象互相独立,任何一个对象改动不会对另一个对象产生影响。又称深度克隆,它完全是新对象的产生,不仅复制所有的非静态值类型成员,而且复制所有引用类型成员的实际对象。(即栈上和堆上的成员均进行复制)
    浅拷贝:又称影子克隆,只复制原始对象中的所有的非静态的值类型成员和所有引用类型成员的引用,就是说,原始对象和新对象共享所有引用类型成员的对象实例。(即只复制栈上的成员)
    浅拷贝:指的是拷贝一个对象时,只拷贝了对象的引用,但是拷贝对象和源对象还是引用的同一份实体。此时,一个对象的改变会影响另一个对象。 只是复制了对像的引用地址,两个对象指向同一个地址,其中一个改变,另一个也跟着改变。
    深拷贝:指改变一个对象的属性,另一个对象不会改变
    所有对象的父对象都是System.Object对象,这个父对象中有一个MemberwiseClone方法(ICloneable接口)来实现浅拷贝。
    深拷贝。一般是通过反序列化或者反射。或者直接new一个新的对象,new一个新的对象

装箱与拆箱

把值类型转换为引用类型叫装箱,拆箱是引用类型转换为值类型

  1. 装箱发生了什么过程呢:
    1. 在托管堆中分配好内存,分配的内存量是值类型的各个字段需要的内存量加上托管堆 上所以对象的两个额外成员(类型对象指针,同步块索引)需要的内存量
    2. 值类型的字段复制到新分配的堆内存中
    3. 返回对象的地址,这个地址就是这个对象的引用
  2. 拆箱发生了什么过程呢:
    1. 获取已经装箱的值类型实例的指针
    2. 把获取到的值复制到栈
      在这里插入图片描述

参数传递

按值传递
对于值类型的按值传递,传递的是该值类型实例的一个拷贝
引用类型的按值传递,传递和操作的是指向对象的引用,此时方法的操作就会改变原来的对象。但是String类型的按值传递,此时引用类型的按值传递却不会修改实参的值
按引用传递(ref/out)
ref和out都是按地址传递,使用后都将改变原来参数的数值。
ref方法外必须初始化,out方法内必须初始化
使用ref关键字的注意点:
i、 方法定义和调用方法都必须显式使用 ref 关键字
ii、传递到 ref 参数的参数必须初始化,否则程序会报错
iii、通过ref的这个特性,一定程度上解决了C#中的函数只能有一个返回值的问题
使用out关键字的注意点:
i、方法定义和调用方法都必须显式使用 out关键字
ii、out关键字无法将参数值传递到out参数所在的方法中,只能传递参数的引用(个人理解), 所以out参数的参数值初始化必须在其方法内进行,否则程序会报错
iii、通过out的这个特性,一定程度上解决了C#中的函数只能有一个返回值的问题
params可变参数传递

泛型

泛型主要的好处类型安全,不存在装箱拆箱的问题
泛型是把类或方法的类型的确定推迟到实例化该类或方法的时候,也就是说刚开始声明的时候不指定类型,等到要使用(实例化)时再指定类型。
泛型可用于类、方法、委托、事件等

泛型方法可以出现在泛型或非泛型类型上。需要注意的是,并不是只要方法属于泛型类型,或者甚至是方法的形参的类型是封闭类型的泛型参数,就可以说方法是泛型方法。只有当方法具有它自己的类型参数列表时,才能称其为泛型方法。

泛型方法是通过类型参数声明的方法

Array,arraylist,list,dictionary,hashtable

声明数组的时候,必须同时指明数组的长度, 它在内存中是连续的存储的.
Arraylist是动态数组,不仅可以插入字符串"abc",而且又可以插入数字123,存在装箱拆箱的问题,不安全类型
泛型list,在声明List集合时,我们同时需要为其声明List集合内数据的对象类型。不存在装箱拆箱的问题。
哈希表(HashTable)表示键/值对的集合,Hashtable 的元素属于 Object 类型,所以在存储或检索值类型时通常发生装箱和拆箱的操作,所以你可能需要进行一些类型转换的操作,而且对于int,float这些值类型还需要进行装箱等操作,非常耗时。
.单线程程序中推荐使用 Dictionary, 有泛型优势, 且读取速度较快, 容量利用更充分。多线程程序中推荐使用 Hashtable, 默认的 Hashtable 允许单线程写入, 多线程读取, 对 Hashtable 进一步调用 Synchronized() 方法可以获得完全线程安全的类型. 而 Dictionary 非线程安全, 必须人为使用 lock 语句进行保护, 效率大减
Hashtable 是线程安全的,而 Dictionary 明显不具备如此特性
Dictionary<T1,T2> 是根据插入的顺序来遍历,但是 Hashtable 在插入时会打乱其位置

迭代器

迭代器块中有两个特殊的语句:
yield return 指定了序列中返回的下一项
yield break 指定了序列中再没有其他项
迭代器本质就是一个状态机~

为什么要使用迭代器:

  1. 提供了一种更抽象的集合访问方式。不必知道集合是数组、列表还是其他什么,只要使用提供的迭代器就可以了。
  2. 迭代器是惰性计算,需要的时候才会返回。迭代器不会一次返回所有数据,而是在调用的时候才返回。
    举个例子,比方说你需要1W个数据,不使用迭代器的话,你需要预先生成1W数据放在那里等待使用,这会占用很大一部分空间;用迭代器的话不需要预先生成,只有当需要的时候再计算返回,整个生命周期一直只占用一个数据的内存。

Equals与==的区别

对于值类型,== 和 equals 等价,都是比较的变量的值
对于 引用类型,== 比较的是两个变量的”引用”是否一样,既是引用的”地址”是否相同(引用类型在栈中的地址),equals方法则比较的是变量的”内容”是否一样(引用类型在托管堆中的存储信息的内容)。
由于string是微软封装的一个字符串类,在内部他已经对 = = 操作符进行了重写。重写后他比较的则是两个变量的内容是否相同。
string是一个特殊的引用类型,C#重载了string对象的很多方法
string s1 = “abc”;
string s2 = “abc”;
s1 == s2为 true
s1.Equals(s2) 为 true

Person p1 = new Person(“aa”, 12);
Person p2 = new Person(“aa”, 12);
p1==p2 false
p1.Equals(p2) false, 原因就在于在Equals是Object中的一个虚方法,而person类中没有对她进行重写,因此此时调用的仍是父类中的Equals方法。但是父类是无法知道你都有哪些成员字段的,因此返回的是false。要想让他能够比较两个变量的内容是否相同,那就应该重写Equals方法

try/catch/finally

catch可以有多个,也可以没有,每个catch可以处理一个特定的异常。.net按照你catch的顺序查找异常处理块,如果找到,则进行处理,如果找不到,则向上一层次抛出。如果没有上一层次,则向用户抛出,此时,如果你在调试,程序将中断运行,如果是部署的程序,将会中止。 如果没有catch块,异常总是向上层(如果有)抛出,或者中断程序运行。

finally可以没有,也可以只有一个。无论有没有发生异常,它总会在这个异常处理结构的最后运行。即使你在try块内用return返回了,在返回前,finally总是要执行,这以便让你有机会能够在异常处理最后做一些清理工作。如关闭数据库连接等等。
throw 与throw ex的区别
在C#中推荐使用throw;来抛出异常;throw ex;会将到现在为止的所有信息清空,认为你catch到的异常已经被处理了,只不过处理过程中又抛出新的异常,从而找不到真正的错误源。

C#是否可以对内存直接进行操作?

C#是可以对内存进行直接操作的,虽然很少用到指针,但是C#是可以使用指针的,在用的时候需要在前边加unsafe,,在.net中使用了垃圾回收机制(GC)功能,它替代了程序员,不过在C#中不可以直接使用finalize方法,是在析构函数中调用基类的finalize()方法。

final/finally/finallize的区别

final 用于申明属性,方法和类,表示属性不可变,方法不可以被覆盖,类不可以被继承。
Finally 是异常处理语句结构中,表示总是执行的部分。
Finallize 表示是object类一个方法,在垃圾回收机制中执行的时候会被调用被回收对象的方法。

委托

委托把方法当作另一个方法的参数来进行传递。
委托的声明
delegate
Action是无返回值的泛型委托
Func是有返回值的泛型委托
Predicate是返回bool型的泛型委托

委托是一种方法容器,里面可以装载若干个具有相同签名的方法引用地址,那么调用委托,就相当于同时调用了该容器内的所有方法;
委托可以看做一种新的对象类型,具有面向对象的特点,定义时可签名接收参数,委托实例化时,可以把方法名作为一个参数传递给委托对象,委托可以理解为指向函数的引用。生成的委托对象可以代理所传递的方法,可以接收方法的参数。也就是定义了委托,可以在不用调用原方法的情况下,调用那个方法;
委托类似于C或 C++中的函数指针,但不同的是委托是面向对象、类型安全的;
委托允许将方法作为参数进行传递;
委托可用于定义回调方法;
委托可以链接在一起,创建多个对象,使用“+=”累加到同一个委托对象上的引用上;
方法不需要与委托签名精确匹配;

事件是不是一种委托?
委托是一种安全的函数指针,事件是一种消息机制;

委托与事件是什么关系?为什么要使用委托?
委托提供了封装方法的方式,事件是某动作已发生的说明,事件是建立于委托之上的;
程序运行时同一个委托能够用来调用不同的方法,只要改变它的引用方法即可,因此委托调节器用的方法不是在编译时决定的,而是在运行时确定的;

链表,队列,堆栈的区别

1、栈是个有底的口袋,像袜子。
队列是没底的口袋,像通心粉。
所以:栈的特点是先进后出,队列的特点是先进先出。
2、主要区别是适用的地方不一样,
链表实际上可以认为是一种数据的物理组织形式,是用指针或对象的引用组织起的一种数据的存储方式.
队列和堆栈是一个更高层次的概念,其底层可以是用链表也可以是用数组来实现.
队列和堆栈的主要区别是进出的顺序不一样,
队列是先进先出,堆栈是后进先出.
3、cooled(经典中–经过非典中) 说的很详细了,我补充一下
队列和堆栈是一种特殊的数据组织形式。
可以把他们看成是一系列的集合。
队列可以看成是有2个口的集合一个口叫队头一个叫队尾,只能在对头进行删除操作,在队尾做插入。根据这样的操作。队列特点是先进先出
堆栈可以看成是有1个口的集合,这个口叫栈顶。插入和删除操作只能在栈顶操作。根据这样的操作。堆栈的特点是是后进先出.
链表是一种存储方式,它可以在非连续的内存空间里面存储一个集合的元素。和它对应的是数组,数组要在连续的空间里存储集合的元素

简述Func与Action的区别

Func是有返回值的委托,Action是没有返回值的委托。

Viewstate

Viewstate:将数据存入页面隐藏控件里,不再占用服务器资源,因此我们可以把一些需要服务器记住的变量和对象保存到viewstate里面。
Viewstate不能跨页面
Viewstate的类型是System.Web.UI.StateBag,它是存储 键/值 对的字典

application、session、cookie

Application 用于保存所有用户的公共的数据信息,保存在服务器端
Session用于保存每个用户的专用信息,保存在服务器端
Cookie 用于保存客户浏览器请求服务器页面的请求信息,保存在客户端
ViewState 常用于保存单个用户的状态信息,有效期等于页面的生存期。跟隐藏控件相似,保存在客户端

什么是反射

动态获取程序集信息
获取类型的信息
获取类型成员的信息
动态创建对象
动态调用对象的成员
程序集包含模块,而模块又包括类型,类型下有成员,反射就是管理程序集,模块,类型的对象,它能够动态的创建类型的实例,设置现有对象的类型或者获取现有对象的类型,能调用类型的方法和访问类型的字段属性。它是在运行时创建和使用类型实例

什么是序列化

序列化是将对象转换为容易传输的格式的过程。例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象。在另一端,反序列化将从该流重新构造对象。持久化保存对象的手段
二进制序列化、Xml序列化、Json序列化

Const与readonly

  1. const修饰的局部变量或字段属于静态常量,静态常量是在程序编译时就确定其值;readonly通常用来修饰字段,属于动态常量,动态常量是在运行时确定其值的。

  2. 由于const是编译时常量,所以声明时必须初始化,而且const修饰的局部变量或字段不能添加static修饰符;readonly是运行时常量,其修饰的字段可以在声明或构造函数中进行初始化,readonly修饰的字段可以添加static修饰符,在被赋值后,其值也是不可以修改的。

  3. const修饰的常量注重效率,没有内存消耗,可以修饰的类型包括基元类型、字符串类型、枚举类型;readonly修饰的常量注重灵活性,需要内存消耗,可以修饰任何类型。

is与as

is判断一个对象是否兼容于指定的类型,考虑里氏代换。Dog是Animal,而Animal不是Dog。

as 与强制类型转换一样,区别是使用as是安全的。使用as如果转换失败,返回Null,不会抛出异常。
as比is性能好,使用is会检查两次对象的类型,一次是核实,一次是强制转换。使用as只进行了一次对象类型的检查。

Http与Https

  1. https协议需要到CA申请证书,一般免费证书较小,因而需要一定费用
  2. http是超文本传输协议,信息是明文传递,https则是具有安全性的ssl加密传输协议
  3. http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443
  4. http的连接很简单,是无状态的;https协议是有SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值