重载和重写的区别
- 封装、继承、多态所处位置不同,重载在同类中,重写在父子类中
- 定义方式不同,重载方法名相同参数列表不同,重写方法名和参数列表都相同
- 调用方式不同,重载使用相同对象以不同参数调用,重写用不同对象以相同参数调用
- 多态时机不同,重载时编译时多态,重写是运行时多态
面向对象的三大特点
封装
优点
- 将变化隔离
- 便于使用
- 提高复用性
- 提高安全性
封装原则
- 将不需要对外提供的内容都隐藏起来
- 把属性都隐藏,提供公共方法对其访问
继承
优点
- 提高代码重用度,增强软件可维护性的重要手段,符合开闭原则
- 继承最主要的作用就是把子类的公共属性集合起来,便与共同管理,使用起来也更加方便
特性
- 传递机制 a▶b; b▶c; c具有a的特性
- 在C#中一个类只能继承一个类,不能有多个父类
多态性
- 多态性是指同名的方法在不同环境下,自适应的反应出不同得表现,是方法动态展示的重要手段
- 多态就是一个对象多种状态,子类对象可以赋值给父类型的变量
值类型和引用类型有什么区别
- 值类型:包含了所有简单类型(整数、浮点、bool、char)、struct、enum。 继承自System.ValueType
- 引用类型包含了string,object,class,interface,delegate,array 继承自System.Object
- 值类型存储在内存栈中,引用类型数据存储在内存堆中,而内存单元中存放的是堆中存放的地址
- 值类型存取快,引用类型存取慢
- 值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针和引用
- 栈的内存是自动释放的,堆内存是.NET 中会由 GC 来自动释放
- 值类型的变量直接存放实际的数据,⽽引⽤类型的变量存放的则是数据的地址,即对象的引⽤
private,public,protected,internal的区别
- public:对任何类和成员都公开,无限制访问
- private:仅对该类公开
- protected:对该类和其派生类公开
- internal:只能在包含该类的程序集中访问该类
C#中所有引用类型的基类是什么
- 引用类型的基类是System.Object
- 值类型的基类是 System.ValueType
- 值类型也隐式继承自System.Object
ArrayList和 List的主要区别
- ArrayList 不带泛型,数据类型丢失
- List 带泛型,数据类型不丢失
- ArrayList 需要装箱拆箱 ,List不需要
- ArrayList存在不安全类型(ArrayList会把所有插⼊其中的数据都当做Object来处理)装箱拆箱的操作(费时
- IList是接⼝,ArrayList是⼀个实现了该接⼝的类,可以被实例化
- List类是ArrayList类的泛型等效类。它的大部分用法都与ArrayList相似,因为List类也继承了IList接口。最关键的区别在于,在声明List集合时,我们同时需要为其声明List集合内数据的对象类型
GC(垃圾回收)产生的原因,如何避免?
GC为了避免内存溢出而产生的回收机制
避免
:
- 减少new的次数
- 字符串拼接使用stringbuilder,字符串比较先定义一个变量存储,防止产生无效内存
- list,new时候,规定内存大小
- 如果要射线检测,应该使用避免GC的方法XXXXNoAlloc函数
- foreach迭代器容易导致GC(目前Unity5.5已修复),使用For循环
- 使用静态变量,GC不会回收存在的对象,但静态变量的引用对象可能被回收
- 使用枚举替代字符串变量
- 调用gameobject.tag==”XXX”就会产生内存垃圾;那么采用GameObject.CompareTag()可以避免内存垃圾的产生:
- 不要在频繁调用的函数中反复进行堆内存分配,比如OnTriggerXXX,Update等函数
- 在Update函数中,运行有规律的但不需要每一帧执行的代码,可以使用计时器,比如1秒执行一次某些代码!!!
Interface与抽象类之间的不同
语法不同处:
- 抽象类中可以有字段,接口没有,接口可以有属性
- 抽象类中可以有实现成员,接口只能包含抽象成员
- 抽象类中所有成员修饰符都可以使用,接口中所有的成员都是对外的,所以不需要修饰符修饰
用法不同处:
- 抽象类是概念的抽象,接口关注于行为
- 抽象类的子类与父类的关系是泛化关系,耦合度较高,而实现类和接口之间是实现的关系,耦合度比泛化低
- 一个类只能继承一个类,但是可以实现多个接口。
关键字Sealed用在类声明和函数声明时的作用
- 类声明可防止其他类继承此类
- 方法声明时可防止派生类重写此方法
反射的实现原理
可以在加载程序运行时,动态获取和加载程序集,并且可以获取到程序集的信息反射即在运行期动态获取类、对象、方法、对象数据等的一种重要手段。
原理:审查元数据并收集关于它的类型信息的能力。
实现步骤:
- 导入using System.Reflection;
- Assembly.Load(“程序集”);//加载程序集,返回类型是一个Assembly;
- 得到程序集中所有类的名称
foreach (Type type in assembly.GetTypes())
{
string t = type.Name;
}
Type type = assembly.GetType("程序集.类名");//获取当前类的类型
Activator.CreateInstance(type); //创建此类型实例
MethodInfo mInfo = type.GetMethod("方法名");//获取当前方法
mInfo.Invoke(null,方法参数);
主要使用的类库:System.Reflection
核心类:
- Assembly描述了程序集
- Type描述了类这种类型
- ConstructorInfo描述了构造函数
- MethodInfo描述了所有的方法
- FieldInfo描述了类的字段
- PropertyInfo描述类的属性
通过以上核心类可在运行时动态获取程序集中的类,并执行类构造产生类对象,动态获取对象的字段或属性值,更可以动态执行类方法和实例方法等。
Net与 Mono 的关系
Net是一个语言平台
Mono为.Net提供集成开发环境,集成并实现了.NET的编译器、CLR 和基础类库,使得.Net既可以运行在windows也可以运行于 linux,Unix,Mac OS 等
在类的构造函数前加上static会报什么错?为什么?
- 静态构造函数不能添加访问修饰符
- 静态构造器前面不能有修饰符,是因为不能让外部调用
String类型比 stringBuilder 类型的优势是什么?
- String主要用于公共 API,通用性好、用途广泛、读取性能高、占用内存小。
- StringBuilder主要用于拼接 String,修改性能好。
- 不过现在的编译器已经把String的 + 操作优化成 StringBuilder 了, 所以一般用String 就可以了
- String是不可变的,所以天然线程同步。
- StringBuilder可变,非线程同步
函数 Func(string a, string b)用 Lambda 表达式怎么写?
(a,b) => {};
数列1,1,2,3,5,8,13…第 n 位数是多少?用 C#递归算法实现
public int CountNumber(int num)
{
if (num == 1 || num == 2)
{
return 1;
}
else
{
return CountNumber(num -1) + CountNumber(num-2);
}
}
C#中有哪些常用的容器类,各有什么特点
- List:索引泛型容器 访问速度快 修改速度慢
- HashTable/Dictionary:散列表格式 查询效率高 空间占用较大
- Stack:后进先出
- Queue:先进先出
Stack栈
:先进后出,入栈和出栈,底层泛型数组实现,入栈动态扩容2倍
Queue队列
:先进先出,入队和出队,底层泛型数组实现,表头表尾指针,判空还是通过size比较。Queue和Stack主要是用来存储临时信息的
Array数组
:需要声明长度,不安全
ArrayList数组列表
:动态增加数组,不安全,实现了IList接口(表示可按照索引进行访问的非泛型集合对象),Object数组实现
List列表
:底层实现是泛型数组,动态扩容,泛型安全。将泛型数据(对值类型来说就是数据本身,对引用类型来说就是引用)存储在一个泛型数组中,添加元素时若超过当前泛型数组容量,则以2倍扩容,进而实现List大小动态可变。(注:大小指容量,不是Count)
LinkList链表
:
- 数组和List、ArrayList集合都有一个重大的缺陷,就是从数组的中间位置删除或插入一个元素需要付出很大的代价,其原因是数组中处于被删除元素之后的所有元素都要向数组的前端移动。
- LinkedList(底层是由链表实现的)基于链表的数据结构,很好的解决了数组删除插入效率低的问题,且不用动态的扩充数组的长度。
- 优点:插入、删除元素效率比较高
- 缺点:访问效率比较低。
HashTable哈希表(散列表)
:
概念:不定长的二进制数据通过哈希函数映射到一个较短的二进制数据集,即Key通过HashFunction函数获得HashCode 装填因子:α=n/m=0.72 ,存储的数据N和空间大小M 然后通过哈希桶算法,HashCode分段,每一段都是一个桶结构,一般是HashCode直接取余。 桶结构会加剧冲突,解决冲突使用拉链法,将产生冲突的元素建立一个单链表,并将头指针地址存储至Hash表对应桶的位置。这样定位到Hash表桶的位置后可通过遍历单链表的形式来查找元素。
1、Key—Value形式存取,无序,类型Object,需要类型转换。
2、Hashtable查询速度快,而添加速度相对慢
3、Hashtable中的数据实际存储在内部的一个数据桶里(bucket结构体数组),容量固定,根据数组索引获取值。
//哈希表结构体
private struct bucket {
public Object key;//键
public Object val;//值
public int hash_col;//哈希码
}
//字典结构体
private struct Entry {
public int hashCode; // 除符号位以外的31位hashCode值, 如果该Entry没有被使用,那么为-1
public int next; // 下一个元素的下标索引,如果没有下一个就为-1
public TKey key; // 存放元素的键
public TValue value; // 存放元素的值
}
private int[] buckets; // Hash桶
private Entry[] entries; // Entry数组,存放元素
private int count; // 当前entries的index位置
private int version; // 当前版本,防止迭代过程中集合被更改
private int freeList; // 被删除Entry在entries中的下标index,这个位置是空闲的private int freeCount; // 有多少个被删除的Entry,有多少个空闲的位置
private IEqualityComparer<TKey> comparer; // 比较器
private KeyCollection keys; // 存放Key的集合
private ValueCollection values; // 存放Value的集合
性能排序
- 插入性能: LinkedList > Dictionary > HashTable > List
- 遍历性能:List > LinkedList > Dictionary > HashTable
- 删除性能: Dictionary > LinkedList > HashTable > List
C#中常规容器和泛型容器有什么区别,哪种效率高?
不带泛型的容器需要装箱和拆箱操作速度慢,所以泛型容器效率更高数据类型更安全
有哪些常见的数值类?
- 简单值类型:包括 整数类型、实数类型、字符类型、布尔类型
- 复合值类型:包括 结构类型、枚举类型
C#中委托和接口有什么区别?各用在什么场合?
- 接口(interface)是约束类应该具备的
功能集合
,约束了类应该具备的功能,使类从千变万化的具体逻辑中解脱出来,便于类的管理和扩展,同时又合理解决了类的单继承问题。 - C#中的委托是
方法集合
,可以便捷的使用委托对这个方法集合进行操作。 - 在以下情况中使用接口: 1.无法使用继承的场合 2.完全抽象的场合 3.多人协作的场合
- 在以下情况中使用委托:多用于事件处理中和回调
C#中unsafe关键字是用来做什么的?什么场合下使用?
非托管代码才需要这个关键字一般用在带指针操作的场合。 项目背包系统的任务装备栏使用到
C#中ref和out关键字有什么区别?
- ref修饰引用参数。参数必须赋值,带回返回值,又进又出
- out修饰输出参数。参数可以不赋值,带回返回值之前必须明确赋值, 引用参数和输出参数不会创建新的存储位置
- 如果ref参数是值类型,原先的值类型数据,会随着方法里的数据改变而改变, 如果ref参数值引用类型,方法里重新赋值后,原对象堆中数据会改变,如果对引用类型再次创建新对象并赋值给ref参数,引用地址会重新指向新对象堆数据。方法结束后形参和新对象都会消失。实参还是指向原始对象,数据改变了
For,foreach,Enumerator.MoveNext的使用,与内存消耗情况
- for循环可以通过索引依次进行遍历
- foreach和Enumerator.MoveNext通过迭代的方式进行遍历
- 内存消耗上本质上并没有太大的区别。 但是在Unity中的Update中,一般不推荐使用foreach 因为会遗留内存垃圾。
函数中多次使用string的+=处理,会产生大量内存垃圾(垃圾碎片),有什么好的方法可以解决
通过StringBuilder进行append,这样可以减少内存垃圾
当需要频繁创建使用某个对象时,有什么好的程序设计方案来节省内存?
设计单例模式进行创建对象或者使用对象池
JIT和AOT区别
1、Just-In-Time -实时编译:执行慢,安装快,占空间小一点
2、Ahead-Of-Time -预先编译:执行快,安装慢,占内存大一点
给定一个存放参数的数组,重新排列数组
private void SortArray(Array arr)
{
Array.Sort(arr);
}
Foreach循环迭代时,若把其中的某个元素删除,程序报错,怎么找到那个元素?以及具体怎么处理这种情况?(注:Try…Catch捕捉异常,发送信息不可行)
- foreach不能进行元素的删除,因为迭代器会锁定迭代的集合,在迭代过程中会比较version
- 解决方法:记录找到索引或者key值,迭代结束后再进行删除
GameObject a=new GameObject();GameObject b=a 实例化出来了A,将A赋给B,现在将B删除,问A还存在吗?
存在,b删除只是将它在栈中的内存删除,而A对象本身是在堆中,所以A还存在
C#中 委托和事件的区别
- 委托是一个类,是存有对某个方法的引用的一种引用类型变量,类似于 C 或 C++ 中函数的指针
- 事件可以被看作一个委托类型的变量,通过事件注册、取消多个委托或方法。
- 通过委托的构造函数来把方法赋值给委托实例
- 触发委托有2种方式: 委托实例.Invoke(参数列表),委托实例(参数列表)
- EventHandler就是一个委托
结构体和类有何区别?
- 结构体是一种值类型,而类是引用类型。(值类型、引用类型是根据数据存储的⻆度来分的)就是值类型用于存储数据的值,引用类型用于存储对实际数据的引用。
- 那么结构体就是当成值来使用的,类则通过引用来对实际数据操作
概述序列化
序列化简单理解成把对象转换为容易传输的格式的过程。比如,可以序列化一个对象,然后使用HTTP通过Internet在客户端和服务器端之间传输该对象
C#的委托是什么?有何用处?
- 是存有对某个方法的引用的一种引用类型变量,类似于 C 或 C++ 中函数的指针
- 用处:使用委托使程序员可以将方法引用封装在委托对象内。然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道将调用哪个方法。与C或C++中的函数指针不同,委托是面向对象,而且是类型安全的。
foreach迭代器遍历和for循环遍历的区别
底层实现
:foreach是通过指针偏移实现的(最初在-1位置,每循环一次,指针就偏移一个单位),而for循环是通过当前索引相对零索引的偏移量(通过首地址和地址偏移量)计算实际访问地址实现的编码结构
:foreach语句省去了for语句中设置循环起点和循环条件的过程使用要求
:使用foreach语句遍历对象要求对象类型实现了枚举接口IEnumerable使用效率
:foreach循环访问时会将对象的值复制到栈上,效率比for循环要低
扩展1:foreach遍历的实现逻辑
在微软.NET推出了IEnumerable和IEnumerator两个接口之后才有了foreach的用法,foreach是建立在这两个接口之上的,使用foreach的前提是里边的容器实现了IEnumerable接口;
实现逻辑是:集合或数组实现了IEnumerable接口,并调用GetEnumerator抽象方法返回IEnumerator遍历器,通过使用IEnumerator这个工具来遍历这个类。
IEnumerator中定义了一组方法:
Current:返回遍历工具所指容器的当前元素;
MoveNext:指向下一个元素,当遍历到没有元素时返回一个false;
扩展2:为什么不能在foreach遍历中更改源集合?
在foreach循环访问时会将集合中的每个值复制到栈上(引用类型复制的是地址),实际遍历的对象其实是一个复制出来的中间变量;在这个机制的存在下,foreach循环在执行速度上要比for循环慢;
C#和C++的区别?
- C# 是一种完全面向对象的语言,而C++ 不是
- C# 是基于IL中间语言和.NET Framework CLR 的,在可移植性,可维护性和强壮性都比C++有很大的改进。
- C# 的设计目标是用来开发快速稳定可扩展的应用程序, 当然也可以通过Interop和Pinvoke完成一些底层操作
具体对比:
- 继承:C++支持多继承,C#类只能继承一个基类中的实现但可以实现多个接口
- 数组:声明 C# 数组和声明 C++ 数组的语法不同。在 C# 中,“[]”标记出现在数组类型的后面
- 数据类型:在C++中bool类可以与整型转换,但C#中bool 类型和其他类型(特别是 int)之间没有转换。long 类型:在 C# 中,long 数据类型为 64 位,而在 C++ 中为 32 位。
- struct 类型:在 C# 中,类和结构在语义上不同。struct 是值类型,而 class 是引用类型。
- switch 语句:与 C++ 中的 switch 语句不同,C# 不支持从一个 case 标签贯穿到另一个 case 标签。
- delegate 类型:委托与 C++ 中的函数指针基本相似,但前者具有类型安全,是安全的。从派生类调用重写基类成员。 base
- 使用 new 修饰符显式隐藏继承成员。
- 重写方法需要父类方法中用virtual声名,子类方法用override 关键字。
- 预处理器指令用于条件编译。C# 中不使用头文件
- 异常处理:C#中引入了 finally 语句,这是C++没有的。
- C# 运算符:C# 支持其他运算符,如 is 和 typeof。它还引入了某些逻辑运算符的不同功能。
- static 的使用,static方法只能由类名调用,改变static变量。
- 在构造基类上替代 C++ 初始化列表的方法。
- Main 方法和 C++ 及Java中的 main 函数的声明方式不同,Main而不能用main
- 方法参数:C# 支持 ref 和 out 参数,这两个参数取代指针通过引用传递参数。
- 在 C# 中只能在unsafe不安全模式下才使用指针。
- 在 C# 中以不同的方式执行重载运算符。
- 字符串:C# 字符串不同于 C++ 字符串。
- foreach:C#从VB中引入了foreach关键字使得以循环访问数组和集合。
- C# 中没有全局方法和全局变量:方法和变量必须包含在类型声明(如 class 或 struct)中。
- C# 中没有头文件和 #include 指令:using 指令用于引用其他未完全限定类型名的命名空间中的类型
- C# 中的局部变量在初始化前不能使用。
- 析构函数:在 C# 中,不能控制析构函数的调用时间,原因是析构函数由垃圾回收器自动调用
- 构造函数:与 C++ 类似,如果在 C# 中没有提供类构造函数,则为您自动生成默认构造函数。该默认构造函数将所有字段初始化为它们的默认值。
- 在 C# 中,方法参数不能有默认值。如果要获得同样的效果,需使用方法重载
C#引用和C++指针的区别
C#不支持指针,但可以使用Unsafe,不安全模式,CLR不检测 C#可以定义指针的类型、整数型、实数型、struct结构体 C#指针操作符、C#指针定义 使用fixed,可以操作类中的值类型
相同点
:都是地址 指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。
不同点
: 指针是个实体,引用是个别名。 sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小; 引用是类型安全的,而指针在不安全模式下
Heap与Stack有何区别?
- heap是堆,stack是栈。
- stack的空间由操作系统自动分配和释放,heap的空间是手动申请和释放的, heap常用new关键字来分配。
- stack空间有限,heap 的空间是很大的自由区。
Mock和Stub有何区别?
- Mock:关注行为验证。细粒度的测试,即代码的逻辑,多数情况下用于单元测试。
- Stub:关注状态验证。粗粒度的测试,在某个依赖系统不存在或者还没实现或者难以测试的情况下使用, 例如访问文件系统,数据库连接,远程协议等。
为什么dynamic font 在 unicode环境下优于 staticfont(字符串编码)
字符覆盖
: Unicode 包含大量字符,包括各种语言的字母、符号、表情等。动态字体能够根据实际需要加载所需字符,确保应用程序能够正确显示和处理各种语言的文本。国际化支持
: 支持多语言应用程序需要处理不同字符集,而动态字体可以根据需要加载不同语言所需的字符。节省资源
: 在静态字体中,如果想要支持多种语言,可能需要包含大量字符,导致资源浪费。而动态字体则允许在运行时选择性地加载字符,以节省内存。
string、stringBuilder、stringBuffer
- String不变性,字符序列不可变,对原管理中实例对象赋值,会重新开一个新的实例对象赋值,新开的实例对象会等待被GC。 string拼接要重新开辟空间,因为string原值不会改变,导致GC频繁,性能消耗大
- StringBuffer是字符串可变对象,可通过自带的StringBuffer.方法来改变并生成想要的字符串。对原实例对象做拼接的实例,不会生成新的实例对象。 拼接使用StringBuilder和StringBuffer,只开辟一个内存空间,这是性能优化的点。
- StringBuilder是字符串可变对象,基本和StringBuffer相同。唯一的区别是StringBuffer是线程安全,相关方法前带synchronized关键字,一般用于多线程 StringBuilder是非线程安全,所以性能略好,一般用于单线程
三者性能比较:StringBuilder>StringBuffer>String
- 如果要操作少量的数据 =string
- 单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
- 多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
字典Dictionary的内部实现原理
- 一个是Hash算法:
- 将不定长度的二进制数据集给映射到一个较短的二进制长度数据集
- 不同的数据进行Hash运算,其结果也可能会相同。所以冲突解决算法
- Hash桶算法: 一个Key通过Hash函数运算后可快速的得到hashCode,将生成的HashCode以分段的形式来映射,把每一段称之为一个Bucket(桶),常见的Hash桶就是直接对结果取余
- 一个是用于应对Hash碰撞冲突解决算法
- 拉链法(开散列):将产生冲突的元素建立一个单链表,并将头指针地址存储至Hash表对应桶的位置。这样定位到Hash表桶的位置后可通过遍历单链表的形式来查找元素。
- 开放定址法(闭散列):当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去。
- 再Hash法:顾名思义就是将key使用其它的Hash函数再次Hash,直到找到不冲突的位置为止。
泛型是什么
多个代码对 【不同数据类型】 执行 【相同指令】的情况。泛型:多个类型共享一组代码 泛型允许类型参数化,泛型类型是类型的模板。5种泛型:类、结构、接口、委托、方法 类型占位符 T 来表示泛型
泛型是模板
性能:泛型不会强行对值类型进行装箱和拆箱,或对引用类型进行向下强制类型转换,所以性能得到提高
安全:通过知道使用泛型定义的变量的类型限制,编译器可以在一定程度上验证类型假设,所以泛型提高了程序的类型安全。
Mathf.Round和Mathf.Clamp和Mathf.Lerp含义?
- Mathf.Round:四舍五入
- Mathf.Clamp:左右限值
- Mathf.Lerp:插值
能用foreach遍历访问的对象需要实现__接⼝或声明_____⽅法的类型(C#遍历)
IEnumerable;GetEnumerator
List和Dictionary类型可以用foreach遍历,他们都实现了IEnumerable接口,申明了GetEnumerator方法。
什么是里氏替换原则?(C#多态)
里氏替换原则(Liskov Substitution Principle LSP)⾯向对象设计的基本原则之⼀。
- 里氏替换原则中说,任何基类可以出现的地⽅,⼦类⼀定可以出现,作⽤⽅便扩展功能能
- 子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。
- 子类中可以增加自己特有的方法。
- 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
- 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
public class Animal
{
public virtual void Eat(Food food)
{
// 父类方法的具体实现
}
}
public class Meat : Food { }
public class Carnivore : Animal
{
// 子类方法的前置条件更宽松,可以接受 Meat 或 Food
public override void Eat(Meat meat)
{
// 子类方法的具体实现
}
}
public abstract class Shape
{
public abstract double GetArea();
}
public class Circle : Shape
{
public override double GetArea()
{
// 子类方法返回比父类更具体的类型
return Math.PI * radius * radius;
}
}
概述c#中代理和事件?
- 代理就是⽤来定义指向⽅法的引⽤。
- C#事件本质就是对消息的封装,⽤作对象之间的通信;发送⽅叫事件发送器,接收⽅叫事件接收器;
哈希表与字典对比
字典
:
- 内部用了Hashtable作为存储结构
- 如果我们试图找到一个不存在的键,它将返回 / 抛出异常。
- 它比哈希表更快,因为没有装箱和拆箱,尤其是值类型。
- 仅公共静态成员是线程安全的。
- 字典是一种通用类型,这意味着我们可以将其与任何数据类型一起使用(创建时,必须同时指定键和值的数据类型)。
- Dictionay 是 Hashtable 的类型安全实现, Keys和Values是强类型的。
- Dictionary遍历输出的顺序,就是加入的顺序
哈希表
:
- 如果我们尝试查找不存在的键,则返回 null。
- 它比字典慢,因为它需要装箱和拆箱。
- 哈希表中的所有成员都是线程安全的,
- 哈希表不是通用类型,
- Hashtable 是松散类型的数据结构,我们可以添加任何类型的键和值。
- HashTable是经过优化的,访问下标的对象先散列过,所以内部是无序散列的
C#中四种访问修饰符是哪些?各有什么区别?
- 属性修饰符 :Serializable,
- 访问修饰符 :public 、private、internal、protected
- 类修饰符 : abstract、sealed,static
- 成员修饰符 : abstract、sealed、delegate、event、readonly、virtual、new、const、extern
属性修饰符:
- Serializable:按值将对象封送到远程服务器。
- STATread:是单线程套间的意思,是一种线程模型。
- MATAThread:是多线程套间的意思,也是一种线程模型。
存取修饰符:
- public:存取不受限制。
- private:只有包含该成员的类可以存取。
- internal:只有当前命名空间可以存取。只能在包含该类的程序集中访问该类。
- protected:只有包含该成员的类以及派生类可以存取。
- protected internal:protected + internal
类修饰符:
- abstract:抽象类。指示一个类只能作为其它类的基类。
- sealed:密封类。指示一个类不能被继承。理所当然,密封类不能同时又是抽象类,因为抽象总是希望被继承的。
成员修饰符:
- abstract:指示该方法或属性没有实现。
- sealed:密封方法。可以防止在派生类中对该方法的override(重载)。不是类的每个成员方法都可以作为密封方法密封方法,必须对基类的虚方法进行重载,提供具体的实现方法。所以,在方法的声明中,sealed修饰符总是和override修饰符同时使用。
- delegate:委托。用来定义一个函数指针。C#中的事件驱动是基于delegate + event的。
- const:指定该成员的值只读不允许修改。
- event:声明一个事件。
- extern:指示方法在外部实现。
- override:重写。对由基类继承成员的新实现。
- readonly:指示一个域只能在声明时以及相同类的内部被赋值。
- static:指示一个成员属于类型本身,而不是属于特定的对象。即在定义后可不经实例化,就可使用。
- virtual:指示一个方法或存取器的实现可以在继承类中被覆盖。
- new:在派生类中隐藏指定的基类成员,从而实现重写的功能。 若要隐藏继承类的成员,请使用相同名称在派生类中声明该成员,并用 new 修饰符修饰它。
下列代码在运行中会发生什么问题?如何避免?
List<int> ls = new List<int>(new int[]{ 1, 2, 3, 4, 5 });
foreach (int item in ls)
{
Console.WriteLine(item * item);
ls.Remove(item);
}
会产⽣运⾏时错误,因为foreach是只读的。不能⼀边遍历⼀边修改。
使用For循环遍历可以解决。
什么是装箱拆箱,怎样减少操作
- 装箱是将值类型转换为引用类型
- 拆箱是将引用类型转换为值类型
牵扯到装箱和拆箱操作比较多的就是在集合中,例如:ArrayList或者HashTable之类。
MVC
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范。
用一种业务逻辑、数据、界面显示分离的方法,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
1、Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。 通常模型对象负责在数据库中存取数据。
2、View(视图)是应用程序中处理数据显示的部分。 通常视图是依据模型数据创建的。
3、Controller(控制器)是应用程序中处理用户交互的部分。 通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据
控制反转和依赖注入
把A类对B类的控制权抽离出来,交给一个第三方去做,把控制权反转给第三方,就称作控制反转(IOC)。控制反转是一种思想,是能够解决问题的一种可能的结果,而依赖注入就是其最典型的实现方法。由第三方(IOC容器)来控制依赖,把他通过构造函数、属性或者工厂模式等方法,注入到类A内,这样就极大程度的对类A和类B进行了解耦
下列代码在运行中会产生几个临时对象
string a = new string("abc");
a = (a.ToUpper() +"123").Substring(0,2);
三个临时对象:abc、ABC、ABC123