C# 基础加强面试题汇总

1、C#中的委托是什么?事件是不是一种委托?事件和委托的关系。

委托可以把一个方法作为参数代入另一个方法。

委托可以理解为指向一个函数的指针。

委托和事件没有可比性,因为委托是类型,事件是对象,下面说的是委托的对象(用委托方式实现的事件)和(标准的event方式实现)事件的区别。事件的内部是用委托实现的。因为对于事件来讲,外部只能“注册自己+=、注销自己-=”,外界不可以注销其他的注册者,外界不可以主动触发事件,因此如果用Delegate就没法进行上面的控制,因此诞生了事件这种语法。事件是用来阉割委托实例的,类比用一个自定义类阉割List。事件只能add、remove自己,不能赋值。事件只能+=、-=,不能= 。加分的补充回答:事件内部就是一个private的委托和add、remove两个方法

 

 

面试聊:用Reflector查看.Net的类的内部实现,解决问题

 

2、C#中的接口和类有什么异同。
答:接口是负责功能的定义,项目中通过接口来规范类,操作类以及抽象类的概念!
而类是负责功能的具体实现!
在类中也有抽象类的定义,抽象类与接口的区别在于:
抽象类是一个不完全的类,类里面有抽象的方法,属性,也可以有具体的方法和属性,需要进一步的专业化。
但接口是一个行为的规范,里面的所有东西都是抽象的!
一个类只可以继承一个基类也就是父类,但可以实现多个接口

不同点:

不能直接实例化接口。

接口不包含方法的实现。

接口可以多继承,类只能单继承。

类定义可在不同的源文件之间进行拆分。

相同点:

接口、类和结构都可以从多个接口继承。

接口类似于抽象基类:继承接口的任何非抽象类型都必须实现接口的所有成员。

接口和类都可以包含事件、索引器、方法和属性。

 

基础知识:接口只能定义方法(只能定义行为,不能定义实现也就是字段),因为事件、索引器、属性本质上都是方法,所以接口中也可以定义事件、索引器、属性。

3、override与重载(overload)的区别

重载是方法的名称相同。参数或参数类型不同,进行多次重载以适应不同的需要。重载(overload)是面向过程的概念。

Override 是进行基类中函数的重写。Override是面向对象的概念

4、属性和public字段的区别是什么?调用set方法为一个属性设值,然后用get方法读取出来的值一定是set进去的值吗?

属性可以对设值、取值的过程进行非法值控制,比如年龄禁止设值负数,而字段则不能进行这样的设置。虽然一般情况下get读取的值就是set设置的值,但是可以让get读取的值不是set设置的值的,极端的例子。Public Age{get{return 100;}set{}}。加分的补充回答:用reflector反编译可以看出,属性内部本质上就是set_***、get_***方法,详细参考传智播客.net培训视频中串讲.net基础的部分。

    class Person

    {

        public int Age

        {

            get

            {

                return 3;

            }

            set

            {

 

            }

        }

    }

            Person p1 = new Person();

            p1.Age = 30;

            p1.Age++;

            Console.Write(p1.Age);//输出3

5.请叙述属性与索引器的区别。

 属性                          索引器    
  通过名称标识。                 通过签名标识。    
  通过简单名称或成员访问来访问。 通过元素访问来访问。    
  可以为静态成员或实例成员。     必须为实例成员。    
  属性的 get 访问器没有参数。    索引器的 get 访问器具有与索引器相同的形参表。    
  属性的set访问器包含隐式value参数。 除了value   参数外,索引器的   set   访问器还具有与索引器相同的形参表。

6、请叙述const与readonly的区别。

每一个class至多只可以定义一个static构造函数,并且不允许增加访问级别关键字,参数列必须为空。
        为了不违背编码规则,通常把static数据成员声明为private,然后通过statci property提供读写访问。
        const 关键字用于修改字段或局部变量的声明。它指定字段或局部变量的值不能被修改。常数声明引入给定类型的一个或多个常数。
        const数据成员的声明式必须包含初值,且初值必须是一个常量表达式。因为它是在编译时就需要完全评估。
        const成员可以使用另一个const成员来初始化,前提是两者之间没有循环依赖。
        readonly在运行期评估赋值,使我们得以在确保“只读访问”的前提下,把object的初始化动作推迟到运行期进行。
        readonly 关键字与 const 关键字不同: const 字段只能在该字段的声明中初始化。readonly 字段可以在声明或构造函数中初始化。因此,根据所使用的构造函数,readonly 字段可能具有不同的值。另外,const 字段是编译时常数,而 readonly 字段可用于运行时常数。
  readonly 只能在声明时或者构造函数里面初始化,并且不能在 static 修饰的构造函数里面。

7、在dotnet中类(class)与结构(struct)的异同?

Class可以被实例化,属于引用类型,是分配在内存的堆上的。类是引用传递的。

Struct属于值类型,是分配在内存的栈上的。结构体是复制传递的。加分的回答:Int32、Boolean等都属于结构体。

8、值类型和引用类型的区别?

1.将一个值类型变量赋给另一个值类型变量时,将复制包含的值。引用类型变量的赋值只复制对对象的引用,而不复制对象本身。

2.值类型不可能派生出新的类型:所有的值类型均隐式派生自 System.ValueType。但与引用类型相同的是,结构也可以实现接口。

3.值类型不可能包含 null 值:然而,可空类型功能允许将 null 赋给值类型。

4.每种值类型均有一个隐式的默认构造函数来初始化该类型的默认值。 

10、abstract class和interface有什么区别?

 

相同点:

都不能被直接实例化,都可以通过继承实现其抽象方法。

不同点:

接口支持多继承;抽象类不能实现多继承。

接口只能定义行为;抽象类既可以定义行为,还可能提供实现。

接口只包含方法(Method)、属性(Property)、索引器(Index)、事件(Event)的签名,但不能定义字段和包含实现的方法;

抽象类可以定义字段、属性、包含有实现的方法。 

接口可以作用于值类型(Struct)和引用类型(Class);抽象类只能作用于引用类型。例如,Struct就可以继承接口,而不能继承类。

加分的补充回答:讲设计模式的时候SettingsProvider的例子。

11、ref与out的区别?

Ref的参数,要求传递进来的变量必须声明,并且赋值

ref应用与内部对外部的值进行改变,out则是内部为外部变量赋值,out一般用在函数有多个返回值的场所。

Out参数,传递到out参数的变量,声明不需要赋值(赋值也没有意义),在out参数所在的方法中,out参数必须赋值,并且是在使用前赋值

 

12、堆和栈的区别?

栈是编译期间就分配好的内存空间,因此你的代码中必须就栈的大小有明确的定义;局部值类型变量、值类型参数等都在栈内存中。

堆是程序运行期间动态分配的内存空间,你可以根据程序的运行情况确定要分配的堆内存的大小。

13、XML 与 HTML 的主要区别

1. XML是区分大小写字母的,HTML不区分。

2. 在HTML中,如果上下文清楚地显示出段落或者列表键在何处结尾,那么你可以省略</p>或者</li>之类的结束 标记。在XML中,绝对不能省略掉结束标记。

HTML:<img src="1.jpg"><br><br>

XML:<img src="1.jpg"></img><br/><br/>

3. 在XML中,拥有单个标记而没有匹配的结束标记的元素必须用一个 / 字符作为结尾。这样分析器就知道不用 查找结束标记了。

4. 在XML中,属性值必须分装在引号中。在HTML中,引号是可用可不用的。

5. 在HTML中,可以拥有不带值的属性名。在XML中,所有的属性都必须带有相应的值。

 

XML是用来存储和传输数据的

HTML是用来显示数据的

如果使用了完全符合XML语法要求的HTML,那么就叫做符合XHTML标准。符合XHTML标准的页面有利于SEO。

 

14、string str = null 与 string str = “”说明其中的区别。

答:string str = null 是不给他分配内存空间,而string str = \"\" 给它分配长度为空字符串的内存空间。 string str = null没有string对象,string str = “”有一个字符串对象。

 

string s3 = string.Empty;//反编译发现,string.Empty就是在类构造函数中 Empty = "";

15、StringBuilder 和 String 的区别?

答:String 在进行运算时(如赋值、拼接等)会产生一个新的实例,而 StringBuilder 则不会。所以在大量字符串拼接或频繁对某一字符串进行操作时最好使用 StringBuilder,不要使用 String

 如果要操作一个不断增长的字符串,尽量不用String类,改用StringBuilder类。两个类的工作原理不同:String类是一种传统的修改字符串的方式,它确实可以完成把一个字符串添加到另一个字符串上的工作没错,但是在.NET框架下,这个操作实在是划不来。因为系统先是把两个字符串写入内存,接着删除原来的String对象,然后创建一个String对象,并读取内存中的数据赋给该对象。这一来二去的,耗了不少时间。而使用System.Text命名空间下面的StringBuilder类就不是这样了,它提供的Append方法,能够在已有对象的原地进行字符串的修改,简单而且直接。当然,一般情况下觉察不到这二者效率的差异,但如果你要对某个字符串进行大量的添加操作,那么StringBuilder类所耗费的时间和String类简直不是一个数量级的。 

 

16、您在什么情况下会用到虚方法?它与接口有什么不同?

答案:子类重新定义父类的某一个方法时,必须把父类的方法定义为virtual

在定义接口中不能有方法体,虚方法可以。

实现时,子类可以不重新定义虚方法,但如果一个类继承接口,那必须实现这个接口。

 

17、string、String;int、Int32;Boolean、bool的区别

String、Int32、Boolean等都属于.Net中定义的类,而string、int、bool相当于C#中对这些类定义的别名。CTS。

18、.Net、ASP.Net、C#、VisualStudio之间的关系是什么?

答:.Net一般指的是.Net Framework,提供了基础的.Net类,这些类可以被任何一种.Net编程语言调用,.Net Framework还提供了CLR、JIT、GC等基础功能。

ASP.Net是.Net中用来进行Web开发的一种技术,ASP.Net的页面部分写在aspx 文件中,逻辑代码通常通过Code-behind的方式用C#、VB.Net等支持.Net的语言编写。

C#是使用最广泛的支持.Net的编程语言。除了C#还有VB.Net、IronPython等。

VisualStudio是微软提供的用来进行.Net开发的集成开发环境(IDE),使用VisualStudio可以简化很多工作,不用程序员直接调用csc.exe等命令行进行程序的编译,而且VisualStudio提供了代码自动完成、代码高亮等功能方便开发。除了VisualStudio,还有SharpDevelop、MonoDevelop等免费、开源的IDE,VisualStudio Express版这个免费版本。

概念说明型

 

1、简述 private、 protected、 public、 internal 修饰符的访问权限。

private : 私有成员, 在类的内部才可以访问。

protected : 保护成员,该类内部和继承类中可以访问。

public : 公共成员,完全公开,没有访问限制。

internal: 当前程序集内可以访问。

2:new有几种用法

第一种:new Class();

第二种:覆盖方法

public new XXXX(){}

第三种:new 约束指定泛型类声明中的任何类型参数都必须有公共的无参数构造函数。

3、用sealed修饰的类有什么特点

答 sealed 修饰符用于防止从所修饰的类派生出其它类。如果一个密封类被指定为其他类的基类,则会发生编译时错误。密封类不能同时为抽象类。
sealed 修饰符主要用于防止非有意的派生,但是它还能促使某些运行时优化。具体说来,由于密封类永远不会有任何派生类,所以对密封类的实例的虚拟函数成员的调用可以转换为非虚拟调用来处理。String就是用sealed修饰的,所以不能被继承。

4、请编程遍历页面上所有TextBox控件并给它赋值为string.Empty?
答:
      foreach (System.Windows.Forms.Control control in this.Controls)
      {
              if (control is System.Windows.Forms.TextBox)
                     {
         System.Windows.Forms.TextBox tb = (System.Windows.Forms.TextBox)control ;
    tb.Text = String.Empty ;
                     }
      }

5、在.net中,配件的意思是?
答:程序集。(中间语言,源数据,资源,装配清单)

6、C#中索引器是否只能根据数字进行索引?是否允许多个索引器参数?

参数的个数和类型都是任意的。加分的补充回答:用reflector反编译可以看出,索引器的内部本质上就是set_item、get_item方法。

 

基础知识:

索引的语法:

public string this[string s],通过get、set块来定义取值、赋值的逻辑

索引可以有多个参数、参数类型任意

索引可以重载。

如果只有get没有set就是只读的索引。

索引其实就是set_Item、get_Item两个方法。

 

7.概述反射和序列化
反射:程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性
序列化:序列化是将对象转换为容易传输的格式的过程。例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象。在另一端,反序列化将从该流重新构造对象。

8、关于拆箱装箱:

1)什么是装箱(boxing)和拆箱(unboxing)(*

Object是引用类型,但是它的子类Int32竟然不能去Object能去的“要求必须是引用类型”

 

的地方,违反了继承的原则,所以需要把Int32装在Object中才能传递。

装箱:从值类型接口转换到引用类型。

拆箱:从引用类型转换到值类型。 

            object obj = null;//引用类型

            obj = 1;//装箱,boxing。把值类型包装为引用类型。

            int i1 = (int)obj;//拆箱。unboxing

 

2)下面三句代码有没有错,以inboxing或者unboxing为例,解释一下内存是怎么变化的

int i=10;

object obj = i;

int j = obj;

 

分析:在inboxing(装箱)时是不需要显式的类型转换的,不过unboxing(拆箱)需要显式的类型转换,所以第三行代码应该改为:

3 int j = (int)obj;   

要掌握装箱与拆箱,就必须了解CTS及它的特点:

    NET重要技术和基础之一的CTS(Common Type System)。CTS是为了实现在应用程序声明和使用这些类型时必须遵循的规则而存在的通用类型系统。.Net将整个系统的类型分成两大类 :值类型和引用类型。

CTS中的所有东西都是对象;所有的对象都源自一个基类——System.Object类型。值类型的一个最大的特点是它们不能为null,值类型的变量总有一个值。为了解决值类型不可以为null,引用类型可以为null的问题,微软在.Net中引入了装箱和拆箱:装箱就是将值类型用引用类型包装起来转换为引用类型;而从引用类型中拿到被包装的值类型数据进行拆箱。      

object.ReferenceEquals();//用来判断两个对象是否是同一个对象

Console.WriteLine(object.ReferenceEquals(3,3));//因为两个3被装到了两个箱子中,所以是false

 

Equals ==的关系

9、CTS、CLS、CLR分别作何解释(*)把英文全称背过来。

C#和.Net的关系。

C#只是抽象的语言,可以把C#编译生成Java平台的二进制代码,也可以把Java代码编译生成.Net平台的二进制代码。所以C#只是提供了if、while、+-*/、定义类、int、string等基础的语法,而Convert.ToInt32、FileStream、SqlConnection、String.Split等都属于.Net的东西。深蓝色是C#的,浅蓝色是.Net的。

C# new→IL:newobj

C# string →.Net中的String

 

类型的差别:.net中的Int32在C#中是int,在VB.Net中是Integer

语法的差别:IL中创建一个对象的方法是L_0001: newobj instance void 索引.C1::.ctor()

C#中是new C1();VB.net中是 Dim c1 As New C1

CTS:Common Type System 通用类型系统。Int32、Int16→int、String→string、Boolean→bool。每种语言都定义了自己的类型,.Net通过CTS提供了公共的类型,然后翻译生成对应的.Net类型。

CLS:Common Language Specification 通用语言规范。不同语言语法的不同。每种语言都有自己的语法,.Net通过CLS提供了公共的语法,然后不同语言翻译生成对应的.Net语法。

CLR:Common Language Runtime 公共语言运行时,就是GC、JIT等这些。有不同的CLR,比如服务器CLR、Linux CLR(Mono)、Silverlight CLR(CoreCLR)。相当于一个发动机,负责执行IL。

10、能用foreach遍历访问的对象的要求

需要实现IEnumerable接口或声明GetEnumerator方法的类型。

 

11、GC是什么? 为什么要有GC? 

C/C++中由程序员进行对象的回收像学校食堂中由学生收盘子,.Net中由GC进行垃圾回收像餐馆中店员去回收。

GC是垃圾收集器(Garbage Collection)。程序员不用担心内存管理,因为垃圾收集器会自动进行管理。GC只能处理托管内存资源的释放,对于非托管资源则不能使用GC进行回收,必须由程序员手工回收,一个例子就是FileStream或者SqlConnection需要程序员调用Dispose进行资源的回收。GC不能回收非托管资源,对于非托管资源一般都实现了IDisposable接口,然后使用using关键字进行资源的回收。

 

要请求垃圾收集,可以调用下面的方法:GC.Collect()一般不需要手动调用GC.Collect()。当一个对象没有任何变量指向(不再能使用)的时候就可以被回收了。

 

基础知识:当没有任何变量指向一个对象的时候对象就可以被回收掉了,但不一定会立即被回收。

            object obj = new object();//只有new才会有新对象

            Console.WriteLine(obj);

 

            object obj2 = obj;

            obj = null;

            obj2 = null;

            Console.WriteLine();

12、using关键字有什么用?什么是IDisposable?

using可以声明namespace的引入,还可以实现非托管资源的释放,实现了IDisposiable的类在using中创建,using结束后会自动调用该对象的Dispose方法,释放资源。Idisosiable类含Close 、Dispose、finalize方法。加分的补充回答:using其实等价于try……finally,用起来更方便。

13、传入某个属性的set方法的隐含参数的名称是什么?

value,它的类型和属性所声名的类型相同。

 

14、说出一些常用的类、接口,请各举5个

要让人家感觉你对.Net开发很熟,所以,不能仅仅只列谁都能想到的那些东西,要多列你在做项目中涉及的那些东西。就写你最近写的那些程序中涉及的那些类。

常用的类:StreamReader、WebClient、Dictionary<K,V>、StringBuilder、SqlConnection、FileStream、File、Regex、List<T>

常用的接口:IDisposable、IEnumerable、IDbConnection、IComparable、ICollection、IList、IDictionary

要出乎意料!不要仅仅完成任务!笔试不是高考!处处要显出牛!

是非型

1、不是说字符串是不可变的吗?string s="abc";s="123"不就是变了吗?

String是不可变的在这段代码中,s原先指向一个String对象,内容是 "abc",然后我们将s指向"123",那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另一个 String对象,内容为"123",原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。

 

2、是否可以从一个static方法内部发出对非static方法的调用?

不可以。因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而static方法调用时不需要创建对象,可以直接调用。也就是说,当一个static方法被调用时,可能还没有创建任何实例对象,如果从一个static方法中发出对非static方法的调用,那个非static方法是关联到哪个对象上的呢?这个逻辑无法成立,所以,一个static方法内部不能发出对非static方法的调用。

3、C#支持多重继承么?

类之间不支持,接口之间支持。类对接口叫做实现,不叫继承。 类是爹、接口是能力,能有多个能力,但不能有多个爹。

4、public static const; int  A = 1;这段代码有错误么?

错误:const不能被修饰为static ;因为定义为常量 (const )后就是静态的(static )。

5、int、DateTime、string是否可以为null?

 

null表示“不知道”,而不是“没有”。没有年龄和不知道年龄是不一样。

数据库中null不能用0表示。0岁和不知道多少岁不一样。

Sex is zero。《色即是空》

 

            //int i1 = null;

            //int? i2 = null;//值类型后加?就成了可空数据类型

            ////int i3 = i2;//所以把可空的赋值给一定不能为空的会报错。

            //int i4 = (int)i2;//可以显式转换,由程序员来保证“一定不为空”

            //int? i5 = i4;//一定会成功!

 

            //using()→try{]catch{}finally{}

 

 

            //int?是微软的一个语法糖。是一种和int没有直接关系的Nullable类型

 

            Nullable<int> d1 = new Nullable<int>();//int? d1=null;

            Nullable<int> d2 = new Nullable<int>(3);//int? d2=3;

            Console.WriteLine(d1==null);

 

int、DateTime不能,因为其为Struct类型,而结构属于值类型,值类型不能为null,只有引用类型才能被赋值null。string可以为null。

C#中int等值类型不可以为null、而数据库中的int可以为null,这就是纠结的地方。int?就变成了可空的int类型。bool?、DateTime?

            int i1 = 3;

            int? i2 = i1;

            //int i3 = i2;//不能把可能为空的赋值给一定不能为空的变量

            int i3 = (int)i2;//显式转换

可空数据类型经典应用场景:三层中的Model属性的设计。

int?翻译生成.Net的Nullable<int>,CTS。

6、能否脱离VS用类似editplus的工具手写代码?你觉得如何才能提高代码的效率和性能?

可以,使用CSC.exe 来编译.cs文件!

可以根据业务流程、业务数据的特点进行优化,比如可以采用缓存、索引、表驱动等来提升代码的效率和性能,同时不要进行无意义的代码优化,重点优化系统的性能瓶颈。

7、利用operator声明且仅声明了==,有什么错误么? 要同时修改Equale和GetHash() ? 重载了"==" 就必须重载 "!="

8、short s=1;s=s+1;有错吗?short s=1;s+=1;有错吗?

答案:第一个有错,因为1是int类型,int+short结果是int,无法隐式转换,改为s = (short)(s + 1);就可以了。

第二个没错,经过反编译发现编译器自动优化成s = (short)(s + 1);

代码分析型

1

        static void Main(string[] args)

        {

            Console.WriteLine(GetIt());

            Console.ReadKey();

        }

 

        static int GetIt()

        {

            int i = 8;

            try

            {

                i++;

                Console.WriteLine("a");

                return i;//把返回值设定为i,然后“尽快”返回(没啥事就回去吧)

            }

            finally

            {

                Console.WriteLine("b");

                i++;

            }

        }

上面程序的执行结果是ab9

 

try {}里有一个return语句,那么紧跟在这个try后的finally {}里的代码会不会被执行,什么时候被执行?

会执行,在return后执行。

 

        static void Main(string[] args)

        {

            //Console.WriteLine(GetIt());

            Console.WriteLine(GetPerson().Age);

            Console.ReadKey();

        }

 

        static int GetIt()

        {

            int i = 8;

            try

            {

                i++;

                Console.WriteLine("a");

                return i;//把返回值设定为i,然后“尽快”返回(没啥事就回去吧。搞完就走)

            }

            finally

            {

                Console.WriteLine("b");

                i++;

            }

        }

 

        static Person GetPerson()

        {

            Person p = new Person();

            p.Age = 8;

            try

            {

                p.Age++;

                Console.WriteLine("a");

                return p;//把返回值设定为i,然后“尽快”返回(没啥事就回去吧。搞完就走)

            }

            finally

            {

                Console.WriteLine("b");

                p.Age++;

            }

        }

 

 

加分的补充回答(也助记):读取数据库中数据的条数的程序

public int QueryCount()

{

   …..

   try

   {

      return cmd.ExecuteScalar();

   }

   finally

   {

      cmd.Dispose();

   }

}

先执行cmd.ExecuteScalar(),把返回值暂时存起来,然后再去执行finally(钱放在这,我去劫个色),然后把返回值返回。return都是最后执行,但是return后的表达式的计算则是在finally之前。

 

如果C#设计的是先执行cmd.Dispose()再执行return就会出现return执行失败了,因为cmd已经Dispose了。

2、下面的程序执行结果是什么?

    class Person

    {

        public int Age { get; set; }

    }

            int i1 = 20;

            int i2 = i1;

            i1++;

            Console.WriteLine(i2);

 

            Person p1 = new Person();

            p1.Age = 20;

            Person p2 = p1;

            p1.Age++;

            Console.WriteLine(p2.Age);

答案:

20

21

 

解释:引用类型和值类型的区别。

3、下面程序的执行结果是什么?

            int i = 10;

            Console.WriteLine(i++);

            Console.WriteLine(++i);

            Console.WriteLine(i=20);

              Console.WriteLine(i==20);

答案:

10

12

20

True       

解答:前两个参考第80题,i++表达式的值为i自增之前的值,所以打印10,执行完成后i变成11,执行++i的时候表达式的值为自增以后的值12。C#中赋值表达式的值为变量被赋值的值,因此最后一个就输出i赋值后的值20

while((line=reader.ReadLine())!=null)

{

}

 

4、下面程序的执行结果是什么?

    public struct Point

    {

        public int x;

        public int y;

        public Point(int x, int y)

        {

            this.x = x;

            this.y = y;

        }

    }

            Point p1 = new Point(100, 100);

            Point p2 = p1;

            p1.x = 200;

            Console.WriteLine("{0},{1}", p1.x, p2.x);

答案:

200,100

 

解答:结构体是复制传递的。

5、请编程遍历WinForm页面上所有TextBox控件并给它赋值为string.Empty?
答:
foreach (System.Windows.Forms.Control control in this.Controls)
{
if (control is System.Windows.Forms.TextBox)
{
System.Windows.Forms.TextBox tb = (System.Windows.Forms.TextBox)control ; 
tb.Text = String.Empty ;
}
}

 

6、

            int i=2000;

            object o=i;

            i=2001;

            int j=(int)o;

            Console.WriteLine("i={0},o={1},j={2}",i,o,j);

输出的结果是:

7、一个数组:1,1,2,3,5,8,13,21...+m,求第30位数是多少?用递归实现;

写递归要确定两个:递归的终止条件;递归表达式。

解答:总结递归规律:F(n)=F(n-2)+F(n-1) Fibonacci数列

        static int F(int n)

        {

            if (n == 1)

            {

                return 1;

            }

            if (n == 2)

            {

                return 1;

            }

            return F(n - 2) + F(n - 1);

        }

非递归(有bug吗?),递归算法的缺点:测试大数据

           int n = Convert.ToInt32(Console.ReadLine());

           if(n<=0)

           {Console.WRiteLine("必须大于0");return;}

           if(n==1)  //时刻注意边界值!!!

           {
                Console.WriteLine("1");

               return;

           }

            int[] data =new int[n];

            data[0] = 1;

            data[1] = 1;

            for(int i=2;i<n;i++)

            {

                data[i] = data[i-1]+data[i-2];

            }

            Console.WriteLine(data[n-1]);

上面程序的时间复杂度为O(n),空间复杂度为O(n)

 

参考:

算法复杂度:时间复杂度:算法运行需要消耗的时间的数量级、空间复杂度:算法运行需要消耗的内存的数量级。

消耗的时间或者内存随着问题规模的扩大而成正比增加,就是O(n)。

消耗的时间或者内存随着问题规模的扩大而不变,就是O(1)。

消耗的时间或者内存随着问题规模的扩大而n*n增加,就是O(n*n)

 

算法复杂度只考虑最差情况(从一个数组中找出第一个大于10的数,时间复杂度为O(n)),并且算法复杂度忽略常量和低阶。把数组数一遍和数两遍的时间复杂度都是O(n)。把长度为n的数组数n/2遍的时间复杂度还是O(n*n)。

上课时把数组前后颠倒的算法的时间复杂度为O(n),空间复杂度为O(1)。

 

8、产生一个int数组,长度为100,并向其中随机插入1-100,并且不能重复。(要求使用两种方法)

 

解答:Random类是一个伪随机数算法,原理:

r(n)=(sed+r(n-1))%100;

    class MyRand

    {

        private int seed;

        private int prevNumber = 10;

        public MyRand(int seed)

        {

            this.seed = seed;

        }

 

        public int Next()

        {

            int newRand = (seed + prevNumber) % 100;

            prevNumber = newRand;

            return newRand;

        }

    }

生成的随机数是依赖于上一个的,所以叫“随机数序列”。,确定的种子产生确定的随机数序列。

为了避免每次生成的随机数序列都一样,所以一般采用当前系统运行的毫秒数Environment.TickCount做种子。

这就明白为什么

            //for (int i = 0; i < 10; i++)

            //{

            //    Random rand = new Random();

            //    Console.WriteLine(rand.Next(100));

            //}

是错的。

经过反编译,Random类的无参构造函数最终也是调用有参的,传递的就是当前毫秒数

public Random() : this(Environment.TickCount)

这就验证了

            //for (int i = 0; i < 10; i++)

            //{

            //    Random rand = new Random();

            //    Console.WriteLine(rand.Next(100));

            //}

为什么一样,或者一半一样,是因为在同一毫秒内。

 

做法1:

            List<int> list = new List<int>();

            Random rand = new Random();

            while (list.Count < 100)

            {

                int number = rand.Next(1, 101);//>=1,<101

                if (!list.Contains(number))//如果list中已经含有这个数,则不插入

                {

                    list.Add(number);

                }

            }

 

            foreach (int i in list)

            {

                Console.WriteLine(i);

            }

 

做法2:

先把1-100这100个数按顺序放入数组arr,再重复100次下面的操作,生成两个介于 >=0,<100 之间的随机数m、n,颠倒arr[m]和arr[n]的数。

 

            int[] arr = new int[100];

            //把100个数顺序放入

            for (int i = 0; i < 100; i++)

            {

                arr[i] = i + 1;

            }

 

            Random rand = new Random();

            for (int i = 0; i < 100; i++)

            {

                //随机生成两个位置

                int m = rand.Next(0, 100);

                int n = rand.Next(0, 100);

 

                //颠倒两个位置

                int temp = arr[m];

                arr[m] = arr[n];

                arr[n] = temp;

            }

效率比第一种高的多,这个只需要N次,而上一种至少需要N*N次

常规笔试题:计算数组{1,1,2,3,5,8....}第30位的值。

解答:斐波那契数列。最前两个数是1,之后每个数都是前两个数的和。

f(n)=f(n-2)+f(n-1)

第一种解法:

        static int Fib(int n)

        {

            //面试时要格外注意检查参数的合法值

            //拿个卷面分也是好的

            if (n < 0)

            {

                throw new ArgumentException("n不能为负数");

            }

            //return Fib(n - 2) + Fib(n - 1);

            if (n == 0 || n == 1)

            {

                return 1;//递归一定要有终止条件

            }

            else

            {

                return Fib(n - 2) + Fib(n - 1);

            }

        }

第二种解法

        //效率非常高!

        static int Fib2(int n)

        {

            if (n < 0)

            {

                throw new ArgumentException("n不能为负数");

            }

            if (n == 0)

            {

                return 1;

            }

            int[] nums = new int[n+1];//题目不清,我就假定他认为的30位从0开始

            nums[0] = 1;

            nums[1] = 1;

            //从前往后一个个的加,最后一位就是计算结果

            for (int i = 2; i <= n; i++)

            {

                nums[i] = nums[i - 2] + nums[i - 1];

            }

            return nums[n];

        }

 

2、冒泡排序

   for (int j = 0; j < nums.Length - 1; j++)

            {

                for (int i = 0; i < nums.Length - 1 -j; i++)

                {

                    if (nums[i] > nums[i + 1])

                    {

                        int temp = nums[i];

                        nums[i] = nums[i + 1];

                        nums[i + 1] = temp;

                    }

                }

            }

 

For(int i = 0;i<nums.length-1; i ++)

{

For(int j= 0;j<nums.length-1-i;j++)

{

If(nums[j]>nums[j+1])

{

Int temp=nums[j];

Nums[i]=nums[j+1];

Nums[j+1]=temp;

}

}

}

如果只是调用集合的Sort方法,是不满足人家要求的!一定要自己写!

 

8有一个10个数的数组,计算其中不重复数字的个数。{3,5,9,8,10,5,3}

工程化的非最优解答:用HashSet

            int[] values = { 3, 5, 9, 8, 10, 5, 3 };

            HashSet<int> set = new HashSet<int>();

            foreach (int i in values)

            {

                set.Add(i);

            }

 

            foreach (int i in set)

            {

                Console.WriteLine(i);

            }

 

9、下面程序的执行结果是什么?

class Program

    {

        static void Main(string[] args)

        {

            Person p1 = new Person();//1:00

            Console.WriteLine(DateTime.Now);

            Thread.Sleep(1000 * 10);

            Console.WriteLine(DateTime.Now);

            Console.WriteLine(p1.BirthDay);

            //打印出来的是对象New出来的时间

            //因为字段是对象被new出来的时候初始化的

            Console.ReadKey();

        }

    }

    class Person

    {

       public DateTime BirthDay = DateTime.Now;

    }

 

再难一点

    class Person

    {

 

        public static int A = 30;

        static Person()//静态构造函数在static字段初始化完成后执行

        {//静态构造函数只执行一次

            A++;

        }

 

        public int B = A++;//字段的初始化赋值代码只是在new一个对象的时候执行,而不是每次用字段的时候都执行

    }

程序的执行结果是什么?

            Person p1 = new Person();

            Console.WriteLine(p1.B);

            Console.WriteLine(Person.A);

            Console.WriteLine(p1.B);

            Console.WriteLine(Person.A);

 

            Person p2 = new Person();

            Console.WriteLine(p2.B);

            Console.WriteLine(Person.A);

10、下面的代码中有什么错误吗?_ abstract override 是不可以一起修饰______
using System;
class A
{
public virtual void F(){
Console.WriteLine("A.F");
}
}
abstract class B:A
{
public abstract override void F();
}

11、当类T只声明了私有实例构造函数时,则在T的程序文本外部,_不可以_(可以 or 不可以)从T派生出新的类,_不可以_(可以 or 不可以)直接创建T的任何实例。
12.下面这段代码有错误么?case():不行 default;
switch (i){
case 0:
CaseZero();
break;
case 1:
CaseOne();
break;
case 2:
dufault;
CaseTwo();
break;
}

13.以下代码段中能否编译通过?请给出理由。

try

{

}

catch(FileNotFoundException e1)

{

}

catch(Exception e2)

{

}

catch(IOException e3)

{

}

catch

{

}

在ioexception的时候出错,因为上一个catch(exception)已经包含了超类型的所有异常

 

14.class Class1

{

    private static int count = 0;

    static Class1()

    {

        count++;

    }

    public Class1()

    {

        count++;

    }

}

Class1 o1 = new Class1();

Class1 o2 = new Class1();

请问,o1.Count的值是多少?( 3 )

填空选择题

1、在.Net中所有可序列化的类都被标记为__[serializable]___?
2、在.Net托管代码中我们不用担心内存漏洞,这是因为有了_ gC __?
3、以下叙述正确的是: B C

A. 接口中可以有虚方法。B. 一个类可以实现多个接口。 C. 接口不能被实例化。 D. 接口中可以包含已实现的方法。

4、对于一个实现了IDisposable接口的类,以下哪些项可以执行与释放或重置非托管资源相关的应用程序定义的任务?(多选)   ( ABC)  
  A.Close B.Dispose C.Finalize  
  D.using E.Quit  
5、在DOM中,装载一个XML文档的方法(B)

a)save方法   b)load方法 c)loadXML方法 d)send方法

6、接口是一种引用类型,在接口中可以声明(A),但不可以声明公有的域或私有的成员变量。

a)方法、属性、索引器和事件; b)方法、属性信息、属性; c)索引器和字段;    d)事件和字段;

7、Net Framework通过什么与COM组件进行交互操作?(Interop)

A.Side By SideB.Web Service

C.InteropD.PInvoke

8、Net依赖以下哪项技术实现跨语言互用性?(  C  )

A.CLR               B.CTS               C.CLS                  D.CTT

9、面向对象的语言具有________性、_________性、________性

答:封装、继承、多态。

不要背,脑子中要有联想。

10、接口是一种引用类型,在接口中可以声明(   a),但不可以声明公有的域或私有的成员变量。  
  a) 方法、属性、索引器和事件;  

  b) 索引器和字段;    
  c) 事件和字段;  

 

解读:接口中不能声明字段只能声明方法,属性、索引器和事件 最终都编译生成方法。因为字段属于实现层面的东西,只有存取值的时候才会用到字段,所以中接口中不能定义字段。
12. 下列关于C#中索引器理解正确的是(c   )  
  a) 索引器的参数必须是两个或两个以上  
  b) 索引器的参数类型必须是整数型  
  c) 索引器没有名字  
  d) 以上皆非  

12、 下面关于XML的描述错误的是(d)。  
  a) XML提供一种描述结构化数据的方法;  
  b) XML   是一种简单、与平台无关并被广泛采用的标准;  
  c) XML文档可承载各种信息;  
  d) XML只是为了生成结构化文档;  


13、以下的C#代码,试图用来定义一个接口:  
  public   interface   IFile  
  {  
              int   A;  
              int   delFile()  
              {  
                      A   =   3;  
              }  
              void   disFile();  
  }  
  关于以上的代码,以下描述错误的是(d   )。  
  a) 以上的代码中存在的错误包括:不能在接口中定义变量,所以int   A代码行将出现错误;  
  b) 以上的代码中存在的错误包括:接口方法delFile是不允许实现的,所以不能编写具体的实现函数;  
  c) 代码void   disFile();声明无错误,接口可以没有返回值;  
  d) 代码void   disFile();应该编写为void   disFile(){};  

 

14 不定项选择:

(1) 以下叙述正确的是: B C

A. 接口中可以有虚方法。B. 一个类可以实现多个接口。 C. 接口不能被实例化。 D. 接口中可以包含已实现的方法。

(3).对于一个实现了IDisposable接口的类,以下哪些项可以执行与释放或重置非托管资源相关的应用程序定义的任务?(多选)   (        ABCD         )  

  A.Close B.Dispose C.Finalize  

  D.using E.Quit  

(4)以下关于ref和out的描述哪些项是正确的?(多选)   (    ACD   )  

  A.使用ref参数,传递到ref参数的参数必须最先初始化。  

  B.使用out参数,传递到out参数的参数必须最先初始化。  

  C.使用ref参数,必须将参数作为ref参数显式传递到方法。  

  D.使用out参数,必须将参数作为out参数显式传递到方法。 

15单项选择:

(1)下列选项中,(C)是引用类型。

a)enum类型   b)struct类型 c)string类型   d)int类型

(3).下列描述错误的是(D)

a)类不可以多重继承而接口可以;

b)抽象类自身可以定义成员而接口不可以;

c)抽象类和接口都不能被实例化;

d)一个类可以有多个基类和多个基接口;

16、声明一个委托public   delegate   int   myCallBack(int   x);   则用该委托产生的回调方法的原型应该是(b   )。  
  a) void   myCallBack(int   x)   ;  
  b) int   receive(int   num)   ;  
  c) string   receive(int   x)   ;  
  d) 不确定的;  

posted @ 2022-05-09 13:43  好学Ace  阅读(989)  评论(0编辑  收藏  举报