C#面试题基础
1.什么是GAC,他的作用是什么?
我的这篇文章有详细的介绍
https://www.cnblogs.com/zxtang/p/14313273.html
2.描述一个被protected interal 修饰的类成员的访问权限?
public 关键字是方法和成员的访问修饰符。公共访问public是允许的最高访问级别,对访问公共成员没有限制。protected 关键字是一个成员访问修饰符。受保护成员在它的类中可访问并且可由派生类访问。private 关键字是一个成员访问修饰符。私有访问是允许的最低访问级别。私有成员只有在声明它们的类和结构体中才是可访问的。internal 关键字是类型和类型成员的访问修饰符。只有在同一程序集的文件中,内部类型或成员才是可访问的。
3.const和readOnly的区别?
const表示不变常量,也就是不能被修改,readonly表示只读的,也就是不能进行写的操作。readOnly和const都用来标识常量,常量成员是在编译时定义的,不能在运行时更改。两者的区别在于:
(1).使用const
关键字将const
声明为字段,并且必须在声明const
对其进行初始化(也就是必须声明的同时必须赋值),而readonly可以声明为只读字段时进行初始化,也可以只声明而在构造函数中进行初始化,且readonly只能初始化一次,构造后,只读字段无法更改。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 静态变量和动态变量的区别 { public class Teacher { public const int A=100;//声明正确 public const int a;//声明错误,没有赋值 public readonly int B; //Readonly可以在构造函数中初始化 public readonly int C = 300;//Readonly也可以声明同时进行初始化 public Teacher() { B = 200; } } }
(2).const不能声明为static的,因为const是隐式静态的,而readonly可以是静态的也可以是实例的,r实例成员,所以不同的实例有不同的常量值,readonly更灵活。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 静态变量和动态变量的区别 { public class Student { private static const int A = 100;//const不能用static修饰 private readonly int B = 100;//readonly可以用static修饰 } }
(3).const字段是编译时常量,像3.14和0这样的值是编译时常量,所以声明的时候就需要赋值,readonly是运行时常量。在程序运行的时候赋值,因此可以在构造函数中赋值或者在声明的时候赋值。
(4)const既可以被声明为字段也可以在任何函数内部声明,而readonly只能被声明为字段。
4.解释System.String和System.Text.StringBuilder的区别?
String 对象是不可改变的。每次使用 System.String 类中的方法之一时,都要在内存中创建一个新的字符串对象,也就是说对String对象进行修改需要为该新对象分配新的空间。在需要对字符串执行重复修改的情况下,与创建新的 String 对象相关的系统开销可能会非常昂贵。如果要修改字符串而不创建新的对象,则可以使用 System.Text.StringBuilder 类。例如,当在一个循环中将许多字符串连接在一起时,使用 StringBuilder 类可以提升性能。
StringBuilder类并没有String类的功能强大,只提供基本的替换和添加和删除字符串中的文本,但它的工作效率非常高,当定义StringBuilder对象时可以指定内存的内存容量,如果不指定系统就会根据对象初始化时的字符串长度来确定。它有两个主要参数Length和Capacity分别表示字符串的实际长度和字符串占据的内存空间长度。对字符串的修改就是在这个内存中进行的,大大提高了添加和替换的的效率。
StringBuilder sb=new StringBuilder("Hello,Welcome",100);//初始化对象并设置初始容量为100
sb.Append(" to www.csdn.net");
sb.Replace(old,new);//将old替换为new,作用与String.Replace()一样只是不需要在过程中复制字符。
StringBuilder的成员:
StringBuilder sb=new StringBuilder("Hello World") 定义初值为Hello World的值
StringBuilder
sb=new
StringBuilder(20); 初始化容量为20的空对象。另外StringBuilder还有MaxCapacity属性用来限定对象可以使用的最大容量。默认大约是int.MaxValue(20亿)可以在使用过程中定义sb.MaxCapacity=value;sb.Append(),给当前字符串追加字符串。
sb.AppendFormat()——添加特定格式的字符串
sb.Insert()——插入一个子字符串
sb.Remove()——从当前字符串删除字符
sb.Replace()——替换字符串中指定的字符
sb.ToString()——将sb转化为String 对象
5.什么是弱类型编程语言?var修饰的数据变量是强类型还是弱类型?
强类型语言也称为强类型定义语言。是一种总是强制类型定义的语言,要求变量的使用要严格符合定义,所有变量都必须先定义后使用。
ava、C#、C++等都是强制类型定义的。也就是说,一旦一个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型了。
例如你有一个整数,如果不显式地进行转换,你不能将其视为一个字符串。
与其相对应的是弱类型语言:数据类型可以被忽略的语言。它与强类型定义语言相反, 一个变量可以赋不同数据类型的值。
弱类型语言也称为弱类型定义语言。与强类型定义相反。像js,php等就属于弱类型语言。
var修饰的数据变量是弱类型。
6.描述var,onject和dynamic的区别是什么?
首先三者都能够对声明的变量赋任何类型的值,var声明的变量在赋值的那一刻就决定了它是什么类型,所有的类型都派生自object. 所以它可以赋值为任何类型,dynamic不是在编译时候确定实际类型的, 而是在运行时。
var是C# 3中引入的,其实它仅仅只是一个语法. var本身并不是 一种类型, 其它两者object和dynamic是类型。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace VarObjectDynamic { class Program { static void Main(string[] args) { var a = 10;//var声明的变量在赋值的那一刻就决定了它是什么类型 object b = 20;//所有的类型都派生自object. 所以它可以赋值为任何类型 dynamic c = "hello";//dynamic不是在编译时候确定实际类型的, 而是在运行时。 //所以下面的代码是能够通过编译的,但是会在运行时报错 c++; Console.WriteLine(a); Console.WriteLine(b); Console.WriteLine(c); } } }
7.值类型和引用类型的区别是什么?
C#详解值类型和引用类型区别
在C#中值类型的变量直接存储数据,而引用类型的变量持有的是数据的引用,数据存储在数据堆中。
常见的值类型包括:byte,short,int,long,float,double,decimal,char,bool 和 struct ,值类型变量声明后,不管是否已经赋值,编译器为其分配内存。
引用类型:class(类),string,interface(接口),delegate(委托)。当声明一个类时,只在栈中分配一小片内存用于容纳一个地址(等new实例后堆上的地址将保存到该空间),而此时并没有为其分配堆上的内存空间。当
使用 new 创建一个类的实例时,分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中。值类型的实例通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中。引用类型的对象总是在进
程堆中分配(动态分配)。
盗用一下大佬的图
值类型:
引用类型:@为存放的堆空间的地址
范围方面
(1)C#的值类型包括:结构体(数值类型、bool型、用户定义的结构体),枚举,可空类型。
(2)C#的引用类型包括:数组,用户定义的类、接口、委托,object,字符串。
内存分配方面:
(1)数组的元素不管是引用类型还是值类型,都存储在托管堆上。
(2)引用类型在栈中存储一个引用,其实际的存储位置位于托管堆。简称引用类型部署在托管推上。而值类型总是分配在它声明的地方:作为字段时,跟随其所属的变量(实例)存储;作为局部变量时,存储在栈上。
(栈的内存是自动释放的,堆内存是在NET中会由GC来自动释放)
适用场合
(1)值类型在内存管理方面具有更好的效率,并且不支持多态,适合用做存储数据的载体;引用类型支持多态,适合用于定义应用程序的行为。
- 引用类型可以派生出新的类型,而值类型不能,因为所有的值类型都是密封(seal)的;
- 引用类型可以包含null值,值类型不能(可空类型功能允许将 null 赋给值类型,如 int? a = null; );
- 引用类型变量的赋值只复制对对象的引用,而不复制对象本身。而将一个值类型变量赋给另一个值类型变量时,将复制包含的值。
值得注意的是,引用类型和值类型都继承自System.Object类。不同的是,几乎所有的引用类型都直接从System.Object继承,而值类型则继承其子类,即直接继承System.ValueType。即System.ValueType本身是一个类类
型,而不是值类型。其关键在于ValueType重写了Equals()方法,从而对值类型按照实例的值来比较,而不是引用地址来比较。
8.Ref和out修饰的参数分别代表什么意思?
ref 关键字使参数按引用传递。其效果是,在方法中对参数的任何更改都将反映在该变量中。若要使用 ref 参数,则方法定义和调用方法都必须显式使用 ref 关键字。
out 关键字会导致参数通过引用来传递。这与 ref 关键字类似,不同之处在于 ref 要求变量必须在传递之前进行初始化,而out是要把参数清空,就是说你无法把一个数值从out传递进去,out进去后,参数的数值为空,所以离
开当前方法前必须对out修饰的变量赋一次值。若要使用 out 参数,方法定义和调用方法都必须显式使用out 关键字。
如上所示,使用ref传递参数,参数必须要初始化。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ref和out { class Program { static void Main(string[] args) { int b=1; Method1(ref b); Console.WriteLine("b="+b);//输出100 int c; Method2(out c); Console.WriteLine("c="+c);//输出200 Console.ReadLine(); } static void Method1(ref int a) { a = 100; } static void Method2(out int a) { a = 300;//必须在离开方法前对a赋值,否则回报错 } } }
9.什么是后期绑定或者晚期绑定,如何实现后期绑定?
后期绑定又可以称为动态绑定(dynamic binding),需要使用System.Reflection,动态地获取程序集里封装的方法、属性等。这种后期绑定的方法大量的被使用在IDE和UI设计中,后期绑定的优点是可
以保存对任何对象的引用。
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace Binding { class Program { static void Main(string[] args) { Type _type = Type.GetType("Binding.Employee"); Console.WriteLine(_type.FullName); Console.WriteLine(_type.Namespace); Console.WriteLine(_type.Name); Console.WriteLine("****************************程序集中的所有信息集合***************************"); Console.WriteLine("****************************程序集中的所有属性信息***************************"); PropertyInfo[] info = _type.GetProperties();//获取属性信息 foreach (PropertyInfo propinfo in info) { Console.WriteLine(propinfo.Name); } Console.WriteLine("****************************程序集中的所有方法信息***************************"); MethodInfo[] methods = _type.GetMethods();//获取方法 foreach (MethodInfo methodinfo in methods) { Console.WriteLine(methodinfo.Name+""+"\t"+methodinfo.ReturnType.Name);//获取此方法名和方法返回类型 } Console.Read(); } } public class Employee { public int ID { get; set; } public string Name { get; set; } public float Salary { get; set; } public Employee() { ID = -1; Name = string.Empty; Salary = 0; } public Employee(int id, string name, float salary) { ID = id; Name = name; Salary = salary; } } }
输出结果为:
Binding.Employee Binding Employee ****************************程序集中的所有信息集合*************************** ****************************程序集中的所有属性信息*************************** ID Name Salary ****************************程序集中的所有方法信息*************************** get_ID Int32 set_ID Void get_Name String set_Name Void get_Salary Single set_Salary Void Equals Boolean GetHashCode Int32 GetType Type ToString String
每个属性都有set和get方法
10.描述一下new关键字的三种用法?
在 C# 中,new 关键字可用作运算符、修饰符或约束。
1)new 运算符:用于创建对象和调用构造函数。
2)new 修饰符:在用作修饰符时,new 关键字可以显式隐藏从基类继承的成员。
3)new 约束:用于在泛型声明中约束可能用作类型参数的参数的类型。
第一种用法就不介绍了,下面介绍第二种和第三种用法
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace new关键字三种用法 {
//用法2 public class Father { public string Name { get; set; } public int Age { get; set; } public int Height { get; set; } public string Job { get; set; } public void Talk() { Console.WriteLine("Father is talking"); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace new关键字三种用法 { public class Son:Father { new public string Job { get; set; }//隐藏和基类中的同名属性 new public void Talk() //隐藏和基类中的同名方法 { Console.WriteLine("Son is talking"); } } }
用法3
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace new关键字三种用法 { public class Teacher { //如果有了有参构造函数,那么无参构造函数要显示的写出来,如果没有有参构造函数,系统会默认有无参构造函数,无参构造函数可写可不写 public Teacher() { } public Teacher(string name) { } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace new关键字三种用法 { class Program { static void Main(string[] args) { MyClass<Teacher> myClass = new MyClass<Teacher>(); myClass.test1(); Console.ReadLine(); } } //使用new关键字修饰的T必须要有无参构造函数 class MyClass<T> where T : new() { public void test1() { Console.WriteLine("哈哈"); } } }
11.描述一下接口和基类的区别,定义接口的目的是什么?
使用接口和基类都可以实现多态:
(1)通过继承实现多态,基类类型作为方法的返回值类型,实际上返回的是一个具体的子类对象;基类类型作为方法参数类型实际上传递的是具体的子类对象。
(2)接口实现多态,接口作为方法返回值类型,实际上返回的是一个接口实现类对象;接口类型作为方法参数类型实际上传递的是一个接口实现类对象。
当开发一个软件的时候,可能很多时候设计完一个对象之后,并不需要马上考虑或者不知道这个对象具体怎样编写,只知道他要干什么,但不知道怎么实现,还有一种情况就是团队开发,比如一个项目
设计分为10个模块,10个小组完成,怎么做到各自开发各的模块呢?这时候使用接口就可以解决。接口只有方法规定,但是没有方法的具体实现。
通常实现多态要么通过抽象类。要么通过接口,都遵循里氏替换原则,即父类出现的地方可以使用子类替换。抽象类和接口的区别:
(1)接口侧重的是功能的封装,抽象类侧重的是代码的复用和继承,通常把公共行为放到基类中,然后需要多态的行为放到接口中。
(2)接口更简洁,使用更方便,框架设计中,使用的大都是接口而不是抽象类。
下面见一个例子,具体说明了通过继承实现多态和接口实现多态:
例子1:通过继承实现多态
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InherianceAndPolymoriphism2 { abstract class Person { //公共属性 public string Name { get; set; } public string Id { get; set; } public DateTime DateofBirth { get; set; } //protected只能供子类使用,也就是只能在子类内部使用,不能通过子类对象去使用 protected string PhoenNumber { get; set; } //构造方法 public Person() { } public Person(string Name,string Id) { this.Name = Name; this.Id = Id; } //共同的行为:可以放在父类中 public void Dowork1() { Console.WriteLine($"【调用父类的公共方法】:{Name}正在工作"); } public void Dowork2() { Console.WriteLine($"【调用父类的公共方法】:{Name}正在写作"); } //父类私有的成员 private void PersonDoWork() { Console.WriteLine($"{Name}正在泡妞"); } //抽象方法:父类规范一个行为要求(没有方法体),具体实现由子类来实现,具有强制性,即子类必须要实现该行为 //抽象方法只能包含在抽象类中,但是抽象类可以不包含抽象方法,且抽象类不能通过new构造 public abstract void Have(); //虚方法:父类可以对该方法有实现,子类也可以对该方法进行重写,不具有强制性,即子类也可以对该方法不进行重写 public virtual void Sleep() { Console.WriteLine($"【父类实现的虚方法】:{Name}正在睡觉"); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 继承和多态 { public class Student:Person { //属性 public int StudentId { get; set; } public string ClassName { get; set; } //构造方法 public Student() { } public Student(string name, string id, string ClassName, int StudentId) : base(name, id) { //base.Name = name; //base.Id = id; //或者这样继承,不过上述继承参数的方式写比较简便 this.ClassName = ClassName; this.StudentId = StudentId; } //实现抽象类方法:关键字为override,必须实现 public override void Have() { Console.WriteLine($"【Student子类实现父类中的抽象方法】:{Name}正在吃饭"); } //重写虚方法,关键字为override //父类中的虚方法不调用,除非子类主动调用base.Sleep(),父类中的虚方法既可以在父类中实现,也可以在子类中进行重写,或者两者都不实现 public override void Sleep() { Console.WriteLine($"【Student子类重写父类中的虚方法】:{Name}正在睡觉"); base.Sleep();//子类调用父类的虚方法 } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 继承和多态 { class Program { static void Main(string[] args) { Student student = new Student(); Person p = new Student();//里氏替换原则 //父类作为方法返回值类型,实际返回的是一个具体的子类对象;/父类作为方法参数类型,实际传递的是一个具体的子类对象 p = TestStudent(student); Console.ReadKey(); } private static Person TestStudent(Person p) { p.Name = "小唐"; //调用父类的公共方法,无法调用私有方法 p.Dowork1(); p.Dowork2(); //调用抽象方法 p.Have(); p.Sleep(); return p; } } }
输出结果:
【调用父类的公共方法】:小唐正在工作
【调用父类的公共方法】:小唐正在写作
【Student子类实现父类中的抽象方法】:小唐正在吃饭
【Student子类重写父类中的虚方法】:小唐正在睡觉
【父类实现的虚方法】:小唐正在睡觉
例子2:通过接口实现多态
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 继承和多态 { abstract public class Person { //公共属性 public string Name { get; set; } public string Id { get; set; } //构造方法 public Person() { } public Person(string Name, string Id) { this.Name = Name; this.Id = Id; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 继承和多态 { //定义演讲接口 public interface IMeeting { //规定行为:行为不能有方法体,且不能加public /// <summary> /// 演讲 /// </summary> void Speech(); /// <summary> /// 谈话 /// </summary> void Talk(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 继承和多态 { //定义教学接口 public interface ITeach { /// <summary> /// 授课 /// </summary> void Lecture(); /// <summary> /// 教学研究 /// </summary> void StudyCourse(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 继承和多态 { public class Teacher : Person, ITeach { public override void Have() { Console.WriteLine($"{Name}正在吃饭"); } public void Lecture() { Console.WriteLine($"{Name}正在授课"); } public void StudyCourse() { Console.WriteLine($"{Name}正在教学研究"); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 继承和多态 { public class President : Person,IMeeting { //实现抽象类方法 public override void Have() { Console.WriteLine($"{Name}正在吃饭"); } //实现接口 public void Speech() { Console.WriteLine($"{Name}正在演讲"); } //实现接口 public void Talk() { Console.WriteLine($"{Name}正在谈话"); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 继承和多态 { class Program { static void Main(string[] args) { //里氏替换原则 IMeeting meeting1 = new President() { Name = "唐主任" }; ITeach teach1 = new Teacher() { Name = "唐老师" }; TestIMeeting(meeting1); TestITeach(teach1); meeting1.Speech(); teach1.Lecture(); Console.ReadKey(); } //接口实现多态方式一:接口作为方法参数类型,实际传递的是接口的实现类对象President private static IMeeting TestIMeeting(IMeeting meeting) { //接口实现多态方式二:接口作为方法返回值类型,实际返回的是接口的实现类对象President return new President(); } //接口实现多态方式一:接口作为方法参数类型,实际传递的是接口的实现类对象Teacher private static ITeach TestITeach(ITeach teach) { //接口实现多态方式二:接口作为方法返回值类型,实际返回的是接口的实现类对象Teacher return new Teacher(); } } }
输出结果:
唐主任正在演讲
唐老师正在授课
12.什么是委托,委托和事件的关系是什么?
委托是一种程序特性,委托可代表一个或者多个同类型的方法,简单的说委托就是方法的变量,能够代表方法。委托的使用步骤如下:
【1】声明委托,定义一个方法的“样子”,也就是我们说的原型,就是指方法的返回值类型、方法的参数类型和个数。
【2】根据委托编写具体的方法
【3】创建委托变量
【4】委托变量关联具体方法(多路委托,使用+=关联多个方法,调用委托的时候将会按顺序执行各个方法)
【5】调用委托
委托的应用:当你在对象之间传递数据的时候,普通方法根本搞不定,这时候使用委托一定能够搞定,主要用在跨线程访问中,例如:
正常情况下:在A对象中创建了B对象,这时候如果B对象中有一个公共行为C,那么在A中是可以调用的
class A
{
B b=new B();
void MyMethod()
{
b.C();
}
void NewMethod()
{
}
}
问题是如果想在B中调用A中的行为NewMethod是否可以?当然不行!但是通过委托是可以的。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 委托 { public class A { B b = new B(); public void MyMethod() { b.C(); //【4】将委托变量关联具体方法 b.newMethodDelegate += NewMethod; //通过委托实现b调用A中的方法NewMethod b.B_NewMethod(); } void NewMethod() { Console.WriteLine("执行A中的NewMethod方法"); } } //【1】声明委托 public delegate void NewMethodDelegate(); }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 委托 { public class B { //【3】创建委托变量 public NewMethodDelegate newMethodDelegate ; public void C() { Console.WriteLine("执行B中的C方法"); } //【5】调用委托 public void B_NewMethod() { newMethodDelegate(); } } }
输出结果:
执行B中的C方法
执行A中的NewMethod方法
事件概念:事件其实是对象对外界信息的刺激,产生的一种消息响应机制。本质上事件其实是委托的进一步包装。
随便选取一个按钮事件做例子:
【1】声明委托
public delegate void EventHandler(object sender, EventArgs e);
【2】定义一个委托事件
public event EventHandler Click;
【3】关联委托
this.btn_Login.Click += new System.EventHandler(this.btn_Login_Click);//将事件和方法关联
事件和委托对比不同点:
第一、事件无法直接赋值,比如事件=null;会出现编译错误,而委托可以。
好处:避免用户对事件直接操作,比如Click事件,如果允许Click=null,会把底层代码清除!可以起到保护。
委托相对太“开放”。
第二、event对象没有invoke()方法,只能通过使用括号的方式来运行。
委托和事件的选择:
第一、正常解决问题,你使用委托和事件没有什么本质区别。所以,我们建议是使用委托。
第二、如果我们做控件二次开发,扩展控件的事件的时候,那必须用事件。
13.描述一下创建并启动一个线程的基本过程?
14什么是序列化,序列化常用的三种格式是什么?
如果我们给自己写的类标识[Serializable]特性,我们就能将这些类序列化。除非类的成员标记了[NonSerializable],序列化会将类中的所有成员都序列化。
(1)BinaryFormatter序列化:将对象序列化二进制流格式。
(2)SoapFormatter序列化:SOAP是一种轻量的、简单的、基于XML的协议,它被设计成在WEB上交换结构化的和固化的信息。 SOAP 可以和现存的许多因特网协议和格式结合使用,包括超文本传 输协议(HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。SOAP序列化的主要优势在于可移植性。SoapFormatter把对象序列化成SOAP消息或解析SOAP消息并重构被序 列化的对象。
(3)XML序列化方式:将对象序列化为XML文件。
15.Trace和Debug类的作用是什么,二者有什么区别?
在 C# 语言中允许使用Trace和Debug类输出程序运行时的调试信息,类似于使用 Console.WriteLine 的方式向控制台输出信息。
所谓调试信息是程序员在程序运行时需要获取的程序运行的过程,以便程序员更好地解决程序中出现的问题,这种调试也被称为是非中断调试。
需要注意的是当程序在 Debug 状态下执行时使用 Debug 类和Trace类均可以在输出窗口中显示输出信息,而在 Release 状态下执行时只有 Trace 类输出的内容才会显示在输出窗口中。
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Trace和Debug { class Program { static void Main(string[] args) { Trace.WriteLine("Trace"); Debug.WriteLine("Debug"); Console.ReadKey(); } } }
Debug调试状态下的输出:
Release状态下的调试输出:
16WCF框架解决的是什么问题?
17.WCF中的ABC分别指的是什么?
18.描述WCF中三种服务实例上下文模式?
19.WPF绑定数据的4种模式?
20.什么是依赖属性,其作用是什么?
21.什么是逻辑树?可视树?他们和默认模板的关系是什么?