C# Types 类别 描述 值类型 value type 简单类型 int, byte, char, float, decimal, bool 枚举类型 enum 结构类型 struct 引用类型 reference type 类类型 object, string, 自定义 class 接口类型 interface 数组类型 int[] 委托类型 delegate
Enum简单的 type - Days of the Week, Payment Status Struct可以用于 Enum, can have constructors, properties, and methods, but cannot have an explicit parameterless constructor in C# Delegate type-safe function pointer 通过使用 +=
, -=
, invoke 会调用所有的 delegate functions 堆和栈 值类型的数据被保存在栈(stack)上,引用类型的数据被保存在堆(heap)上,当值类型作为参数传递给函数时,会将其复制到新的内存空间中,因此在函数中对该值类型的修改不会影响原始值类型
拆箱和装箱 装箱将栈 (value type) 中的内容迁移到堆上 (reference type)int double 转换为对应的引用(object)类型 拆箱 1 2 3 int i=3 ;objcet o=i; i=(int )o
== 和 Equals 的区别 Equals 继承自 Object,一般用于比较对象内容是否相等 == 比较 type value 值是否相等,reference value 比较地址是否相等 内存泄漏 静态引用:如果一个静态对象长时间存活且占用大量内存,并且该对象不会被释放或重置,可能导致内存泄漏 不使用的引用对象没有置 null 委托或事件没有解除注册 Virtual & Abstract 相同点抽象方法和虚方法都可以供派生类重写, 派生类重写父类的方法都要使用关键字 override 来声明。 不同点虚方法必须有方法名称和方法实现;抽象方法是只有方法名称,没有方法实现; 虚方法在派生类中可以实现,也可以不实现;抽象方法是一种强制派生类覆盖的方法,子类必须实现,否则派生类将不能被实例化; 抽象方法只能在抽象类中声明,虚方法不是,如果类包含抽象方法,那么该类也必须声明为抽象类; 抽象方法使用修饰符 abstract 声明,虚方法使用方法修饰符 virtual 声明; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public abstract class AbstProgram { public abstract string abstMethod () ; public virtual void virMethod () { Console.WriteLine("hello, virmethod" ); } } public class Subclass : AbstProgram { public override string abstMethod () { return "hello,abstMethod" ; } }
Interface & abstract 相同点
都可以被继承 都不能直接被实例化 都可以包含方法声明且都没有实现 派生类必须实现未实现的成员 不同点
接口支持多实现,抽象类只能被单一继承实现; 接口中的成员访问类型默认为公共的,不能有其他的访问修饰符修饰; 定义的关键字不一样, 抽象类需要 abstract,而接口则使用 interface; 接口可以作用于值类型(枚举可以实现接口)和引用类型,抽象类只能作用于引用类型; 接口不能包含字段和已实现的方法, 接口只包含方法、属性、索引器、事件的签名;抽象类可以定义字段、属性、包含有实现的方法; 在抽象类中, 新增一个方法的话, 继承类中可以不用作任何处理;而对于接口来说, 则需要修改继承类, 提供新定义的方法; 接口用于规范, 更强调契约, 抽象类用于共性, 强调父子。抽象类是一类事物的高度聚合, 那么对于继承抽象类的子类来说, 对于抽象类来说, 属于”Is A”的关系; 而接口是定义行为规范, 强调”Can Do”的关系, 因此对于实现接口的子类来说, 相对于接口来说, 是”行为需要按照接口来完成”; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public interface IMyInterface { public void intMethod () ; } public abstract class AbstProgram { public abstract void abstMethod () ; } public class SubClass : AbstProgram , IMyInterface { public override void abstMethod () { Console.WriteLine("hello,abstMethod" ); } public void intMethod () { Console.WriteLine("hello,intMethod" ); } }
Interface & Class 相同点
接口、类和结构体都可以从多个接口继承; 接口似于抽象基类, 继承接口的任何非抽象类型都必须实现接口的所有成员; 接口和类都可以包含事件、索引器、方法和属性; 不同点
不能直接实例化接口; 接口可以实现多继承,类只能被派生类单独继承; 接口只包含方法或属性的声明,不包含方法的实现, 而类定义的方法必须实现; 表达的含义不同,接口定义了所有类继承接口时应遵循的”语法合同”;接口定义了语法合同 “是什么” 部分,派生类定义了语法合同 “怎么做” 部分; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 interface IMyInterface { void MethodToImplement () ; } class InterfaceImplementer : IMyInterface { static void Main () { InterfaceImplementer iImp = new InterfaceImplementer(); iImp.MethodToImplement(); } public void MethodToImplement () { Console.WriteLine("MethodToImplement() called." ); } }
virtual、sealed、override 和 abstract 的区别 virtual 声明虚方法的关键字,说明该方法可以被重写。 sealed 说明该类不可被继承,也就是声明该类为密封类。 override 重写基类/父类的方法 。 abstract 声明抽象类和抽象方法的关键字,抽象方法不提供实现,由子类实现,抽象类不可实例化。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public abstract class Program { public abstract string abstmethod () ; public virtual void virmethod () { Console.WriteLine("hello,virmethod" ); } } public sealed class SubClass : Program { public override string abstmethod () { return "hello,abstmethod" ; } }
const 与 readonly 的区别 const 字段只能在该字段的声明中初始化;而 readonly 字段可以在声明或构造函数中初始化。因此,根据所使用的构造函数,readonly 字段可能具有不同的值。 const 字段是编译时常数,而 readonly 字段是运行时常数,是运行时确认的(一个在编译时就确定了,一个在运行时才确定)。 const 默认就是静态的,而 readonly 如果设置成静态的就必须显示声明。 const 对于引用类型的常数,可能的值只能是 string 和 null;而 readonly 可以是任何类型。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Program { public static readonly int NumberA = NumberB \* 10 ;public static readonly int NumberB = 10 ; public const int NumberC = NumberD * 10 ; public const int NumberD = 10 ; static void Main (string [] args ) { Console.WriteLine("NumberA is {0}, NumberB is {1}." , NumberA, NumberB); Console.WriteLine("NumberC is {0}, NumberD is {1}." , NumberC, NumberD); Console.ReadLine(); } }
重载 (override) 和重写 (overload) 有什么区别 重载: 当类包含两个名称相同但签名不同(方法名相同,参数列表不相同)的方法时发生方法重载。用方法重载来提供在语义上完成相同而功能不同的方法。(一个类中、多个方法) 重写: 在类的继承中使用, 通过覆写子类方法可以改变父类虚方法的实现。(二个类以上) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Program { public void method (int num1 ) { } public void method (string str1 ) { } public virtual void virtmethod () { Console.WriteLine("hello,virtmethod" ); } } public class SubClass : Program { public override void virtmethod () { Console.WriteLine("hello,overload" ); } }
结构体和类的区别 结构体是值类型,类是引用类型。 结构体常用于数据存储,类多用于行为。 类支持继承, 而结构体不支持继承。 类可以为抽象类,结构体不支持抽象模式。 结构体不支持无参构造函数,也不支持析构函数,并且不能有 Protected 修饰符。 1 2 3 4 5 6 7 8 struct Books{ public string title; public string author; public string subject; public int book_id; };
ref 与 out 的区别 使用 ref 型参数时,传入的参数必须先被初始化;对 out 而言,必须在方法中对其完成初始化。 使用 ref 和 out 时,在方法的参数和执行方法时,都要加 ref 或 out 关键字,以满足匹配。 out 适合用在需要 retrun 多个返回值的地方,而 ref 则用在需要被调用的方法修改调用者的引用的时候。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Program { public void test1 (ref int i ) { i = 99 ; } public void test2 (out int i ) { i = 11 ; } static void Main (string [] args ) { Program p = new Program(); int i = 1 ; p.test1(ref i); int j; p.test2(out j); } }
值类型和引用类型的区别 值类型: 就是一个包含实际数据的对象。即当定义一个值类型的变量时,C#会根据它所声明的类型,以栈方式分配一块大小相适应的存储区域给这个变量,随后对这个变量的读或写操作就直接在这块内存区域进行; 引用类型: 一个引用类型的变量不存储它们所代表的实际数据,而是存储实际数据的引用。引用类型分两步创建:首先在栈上创建一个引用变量,然后在堆上创建对象本身,再把这个内存的句柄(也是内存的首地址)赋给引用变量; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class SomeRef { public Int32 x; }struct SomeVal { public Int32 x; }class Program { static void ValueTypeDemo () { SomeRef r1 = new SomeRef(); SomeVal v1 = new SomeVal(); r1.x = 5 ; v1.x = 5 ; Console.WriteLine(r1.x); Console.WriteLine(v1.x); SomeRef r2 = r1; SomeVal v2 = v1; r1.x = 8 ; v1.x = 9 ; Console.WriteLine(r1.x); Console.WriteLine(r2.x); Console.WriteLine(v1.x); Console.WriteLine(v2.x); } }
拆箱和装箱的定义及拆箱和装箱的性能影响?怎么解决? 装箱: 值类型转换为引用类型; 拆箱:引用类型转换为值类型; 影响: 都涉及到内存的分配和对象的创建,有较大的性能影响; 解决:使用泛型; 1 2 3 4 5 6 7 8 9 10 11 public class Solution { static void Main (string [] args ) { int i = 123 ; object obj = i; Console.WriteLine("装箱操作:值为{0},装箱之后对象为{1}" , i, obj); int j = (int )obj; Console.WriteLine("拆箱操作:装箱对象为{0},值为{1}" , obj, j); } }
委托是什么?事件是不是委托? c# 中的委托(Delegate)类似于 c 或 c++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 delegate int NumberChanger (int n ) ;class TestDelegate { static int num = 10 ; public static int AddNum (int p ) { num += p; return num; } public static int MultNum (int q ) { num *= q; return num; } public static int GetNum () { return num; } static void Main (string [] args ) { NumberChanger nc1 = new NumberChanger(AddNum); NumberChanger nc2 = new NumberChanger(MultNum); nc1(25 ); Console.WriteLine("Value of Num: {0}" , GetNum()); nc2(5 ); Console.WriteLine("Value of Num: {0}" , GetNum()); Console.ReadKey(); } }
构造函数 Constructor 是否可以被继承?是否可以被 Override 重载? Constructor 不可以被继承,因此不能被重写(Overriding),但可以被重载(Overloading)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class Student { public int Age { get ; } public string Name { get ; } public Student () { Console.WriteLine("对象被创建。" ); } public Student (string name ) : this () { this .Name = name; } public Student (string name, int age ) : this (name ) { this .Age = age; } } public class Program { static void Main (string [] args ) { Student student_1 = new Student(); Student student_2 = new Student("姓名" , 14 ); Console.WriteLine(student_2.Name + ' ' + student_2.Age); } }
String 类是否可以被继承? String 类是 sealed 类故不可以继承。 当对一个类应用 sealed 修饰符时,此修饰符会阻止其他类从该类继承。 在下面的示例中,类 SealedProgram 从类 DemoProgram 继承,但是任何类都不能从类 SealedProgram 继承。 1 2 class DemoProgram { };sealed class SealedProgram : DemoProgram { };
Task 和 Thread 的区别 Task 比较新,发布于.NET 4.5 版本,而 Thread 在.NET 1.1 版本。 Task 能结合新的 async/await 代码模型写代码, 而 Thread 则不支持。 Task 可以创建线程池,返回主线程,管理 api, 而 Thread 则不能。 Task 是多核多线程,Thread 是单核多线程。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class Solution { static void Main () { var testTask = new Task(() => { Console.WriteLine("hello, Task" ); }); Console.WriteLine(testTask.Status); testTask.Start(); } } public class Solution { static void Main (string [] args ) { ThreadTest test = new ThreadTest(); Thread thread = new Thread(new ThreadStart(test.MyThread)); thread.Start(); Console.ReadKey(); } } public class ThreadTest { public void MyThread () { Console.WriteLine("这是一个实例方法" ); } }
其他 死锁的必要条件?怎么克服? 死锁的原因主要是:
因为系统资源不足。 进程运行推进的顺序不合适。 资源分配不当等。如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。 必要条件
互斥条件:一个资源每次只能被一个进程使用。 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 处理死锁的措施
预防死锁:通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或几个条件,来防止死锁的发生。 避免死锁:在资源的动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免死锁的发生。 检测死锁:允许系统在运行过程中发生死锁,但可设置检测机构及时检测死锁的发生,并采取适当措施加以清除。 解除死锁:当检测出死锁后,便采取适当措施将进程从死锁状态中解脱出来。 Error 和 Exception 有什么区别? Error 是不可捕捉到的,无法采取任何恢复的操作。Except 表示可恢复的例外,这是可捕捉到的。 举个例子: 跟儿子逛街,儿子看中一个奥特曼是 Exception,没带钱是 Error。 Error 更多的表示系统方面的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出,内存溢出等;而 Exception 类表示程序可以捕获到的异常(比方说 c#的 try-catch 语句),可以捕获且可能恢复,出现这类异常,应该及时处理,使得程序正常运行,从而避免影响整个项目的运行。 UDP 和 TCP 连接有什么区别? TCP 是传输控制协议,提供的是面向连接的,是可靠的,字节流服务,TCP 提供超时重拨,检验数据功能。 UDP 是用户数据报协议,是一个简单的面向数据报的传输协议,是不可靠的连接。 TCP 保证数据正确性,UDP 可能丢包,TCP 保证数据顺序,UDP 不保证。 TCP 对系统资源的要求较多, UDP 较少。