最近找工作面的面试题目汇总(二)

引言

今天继续的是去那几家面试公司的一些面试题目,适合刚出来或者两三年经验的,大牛可以适量吐槽下,找工作的可以补补基础。

这系列的第一篇文章请查看这里:最近找工作面的面试题目汇总(一)

1.请从中选择引用类型或值类型

说起题,我们不由得会想到几个名词:栈和堆,值类型和引用类型,装箱和拆箱。

这些概念可是C#的入门基础呀,咱们还是要搞懂的,详细可以查看参考文章。

这里要提下一个知识点:C#新手都有一个误区:值类型的值都是保存在栈里,实际应该是:值类型的值是在它声明的位置存储的。即局部变量(参数)的值会在栈里,而当值类型作为引用类型的类型成员的话,会跟随对象,即存储在托管堆里。

顺便提供几张经典图片便于读者理解:

image

image

最后在这些概念里,我觉得比较重要的精华提要:

  1. 对于栈和堆的概念,我们声明值类型和引用类型的时候,为什么会分配两种内存而不是一种呢?只要我们仔细想一下,基本类型他们并不复杂,很多时候只包含简单的值,比如 i=1,而对象类型则相对复杂,它们则会引用其他对象或基本类型。简单来说,对象类型需要动态内存而基本类型则需要静态内存。若需要分配动态内存,那么就分配到堆上;反之在栈上。
  2. 对于值类型和引用类型的概念,值类型,它们的内存值都分配在栈上,当我们把一个int值分配给另外一个int值时,需要创建一个完全不同的拷贝。换句话说,你可以改变其中任何一个而不会影响另外一个。这种数据类型被称为值类型;引用类型,当我们创建一个对象,并把一个对象赋给另外一个对象时,它们的指针指向相同的内存(如下图,当我们把obj赋给obj1时,它们指向相同的内存)。换句话说,我们改变其中一个,会影响到另外一个。

参考文章

  1. 图解C#的值类型,引用类型,栈,堆,ref,out
  2. 6个重要的.NET概念:栈,堆,值类型,引用类型,装箱,拆箱强烈推荐此篇文章
  3. .NET下的内存分配机制
  4. 从栈和堆中来看值传递和引用传递

2.判断一个string变量是否为空

最普通的用法当然就是:

  • s.Length == 0
  • s == string.Empty
  • s == ""

效率上:s.Length == 0 > s == string.Empty > s == ""

不过 net 2.0后,可用String.IsNullOrEmpty(s) 来进行判断

参考文章

String.IsNullOrWhiteSpace和String.IsNullOrEmpty的区别

3.Lock的作用

关于lock的介绍就到这里,有下面几点需要注意的地方

  1. lock的是引用类型的对象,string类型除外。
  2. lock推荐的做法是使用静态的、只读的、私有的对象。
  3. 保证lock的对象在外部无法修改才有意义,如果lock的对象在外部改变了,对其他线程就会畅通无阻,失去了lock的意义。

还有摘自本篇评论的精彩回复:

this 表示的就是当前实例,当你再new一个的时候,锁定的就不再是同一个对象了。 不能锁定值类型的原因是,当这个值类型传递到另一个线程的时候,会创建一个副本,锁定的也不再是同一个对象了。 锁定字符串带来的问题是,字符串在CLR中会暂存在 内存中,如果有两个变量被分配了相同的字符串内容,那么这两个引用会指向同一块内存,实际锁定也就是同一个对象,这就会导致整个应用程序的阻塞。所以锁定字符串是非常危险的行为。

参考文章:

1.[C#基础]说说lock到底锁谁?](http://www.cnblogs.com/wolf-sun/p/4209521.html)

4.abstract class 和 interface 有什么区别

这篇已经有提过了:请参阅:最近找工作面的面试题目汇总(一)

5.关键字params是什么

参考文章:

为了将方法声明为可以接受可变数量参数的方法,我们可以使用params关键字来声明数组

要求:

  1. 在方法声明中的 params 关键字之后不允许任何其他参数,并且在方法声明中只允许一个 params 关键字
  2. 该参数必须标识一个一维数组,但类型不限,对该参数传递null或者0个数目的数组的引用都是合法的

参考文章:

  1. C# 参数关键字params
  2. C#中的params关键字的用法

6.怎么获取用户请求的Url地址

  1. 获取 完整url:
string url=Request.Url.ToString(); 

参考文章:

  1. asp.net获取当前网址url

写出三元运算符实例

这个我就不说了。 ?:

7.说明Stack和Queue的区别

1.Queue的定义

Queue是先进先出(first in first-out)数据结构,表示放进queue的第一个数据,会是第一个拿出来使用

示例:

//建立Queue的对象
            var myqueue = new Queue<string>();
            //新增数据进入queue
            myqueue.Enqueue("第1个");
            myqueue.Enqueue("第2个");
            myqueue.Enqueue("第3个");
            //查看queue的所有资料
            foreach (string queue in myqueue)
            {
                Console.WriteLine(queue);
            }
            //使用Peek()方法查看queue里的第一条数据
            Console.WriteLine("");
            Console.WriteLine("使用peek方法的輸出值:" + myqueue.Peek());
            //使用Dequeue()方法从queue中拿出值
            //记得是先进先出的获取方法
            Console.WriteLine("第一个被我拿走了:" + myqueue.Dequeue());
            //查看剩下的值
            Console.WriteLine("查看myqueue剩下的值");
            foreach (string queue in myqueue)
            {
                Console.WriteLine(queue.ToString());
            }
            //查看某位置的值
            Console.WriteLine("查看特定位置的值");
            Console.WriteLine(myqueue.ElementAt(1));

运行结果

第1个
第2个
第3个

使用peek方法的輸出值:第1个
第一个被我拿走了:第1个
查看myqueue剩下的值
第2个
第3个
查看特定位置的值
第3个
第3个
第2个
第1个

Peek看到的数据:第3个
被拿走了:第3个
查看剩下的数据
第2个
第1个

2.Stack的定义

Stack为后进先出(first-in last-out)的数据结构,表示第一个进去的数据,反而是最后一个出来。

示例代码

 //建立Stack对象
            var mystack = new Stack<string>();
            //插入数据
            mystack.Push("第1个");
            mystack.Push("第2个");
            mystack.Push("第3个");
            //查看数据
            foreach(string stack in mystack)
            {
                Console.WriteLine(stack.ToString());
            }
            //Stack与Queue一样有Peek方法查看第一条数据
            Console.WriteLine("");
            Console.WriteLine("Peek看到的数据:"+mystack.Peek());

            //使用Pop方法取出数据
            Console.WriteLine("被拿走了:"+mystack.Pop());
            Console.WriteLine("查看剩下的数据");
            foreach (string stack in mystack)
            {
                Console.WriteLine(stack.ToString());
            }

运行结果

第3个
第2个
第1个

Peek看到的数据:第3个
被拿走了:第3个
查看剩下的数据
第2个
第1个

参考文章:

1..NET源码Stack和Queue的实现

2.C#資料群集系列菜單-『Queue與Stack』

8.Overload和Override的区别

1.Overload的定义

重载应该叫overload,重载某个方法是在同一个类或父子关系类中发生的!重载(overload)是提供了一种机制, 相同函数名通过不同的返回值类型以及参数来表来区分的机制

overload:  同一类中或父子关系类中皆可.
                   public string ToString(){return "a";}
                   public string ToString(int id){return id.ToString();}

2.Override的定义

重写叫override,重写是在子类中重写父类中的方法。重写(override)是用于重写基类的虚方法,这样在派生类中提供一个新的方法

override:   父类:public virtual string ToString(){return "a";}
                   子类:public override string ToString(){return "b";}

3.两者间的区别

很本质的区别就是看函数特征:覆写(Override)的两个函数的函数特征相同,重载(Overload)的两个函数的函数名虽然相同,但函数特征不同。

  1. override 是在继承的时候,如果你写的函数与要继承的函数函数特征相同,那么,加上这个关键字,在使用这个子类的这个函数的时候就看不见父类(或超类)的函数了,它被覆盖掉了。
    比如:Derived继承了Base,Base里面有void A(int a) ,那么如果你Derived里面觉得A写得不好或不适合这个类,你想重新再写一遍A里的代码,那么就写override void A(int a)这样,原来的那个函数就被你新写的这个覆盖掉了。
    overload 是重载,就是说函数名相同,函数特征不同,系统会根据你提供的参数来调相应的函数。比如:void A(int a)和void A(int a,int b) ,如果你用的是A(1)那么调的是第一个,如果是A(1,1)那么调的是第二个。

参考文章:

  1. C#中override和overload的区别

9. using的意义

简述:

提供能确保正确使用 IDisposable 对象的方便语法。它的用途就是清理非托管资源,不受GC控制的资源。Using结束后会隐式的调用Disposable方法

  1. 有许多其他类别的非托管资源和封装这些资源的类库类型,所有这些类型都必须实现IDisposable接口,而using语句确保即使在调用对象上的方法时发生异常Dispose方法也会被调用。
  2. 实际上,将对象放入 try 块中并在finally块中调用Dispose一样可以达到同样的效果,而这功能就是编译器转换using语句的方式。

以下两段代码的作用是一致的

using (Font font1 = new Font("Arial", 10.0f)) 
{
    byte charset = font1.GdiCharSet;
}
 {
  Font font1 = new Font("Arial", 10.0f);
  try
  {
    byte charset = font1.GdiCharSet;
  }
  finally
  {
    if (font1 != null)
      ((IDisposable)font1).Dispose();
  }
}

参考文章:

  1. using 语句(C# 参考)
  2. 三种C# using的用法

10.List是什么

因为ArrayList存在不安全类型与装箱拆箱的缺点,所以出现了泛型的概念。List类是ArrayList类的泛型等效类,它的大部分用法都与ArrayList相似,因为List类也继承了IList接口。最关键的区别在于,在声明List集合时,我们同时需要为其声明List集合内数据的对象类型。

优点

  1. 类型安全
  2. 性能增强
  3. 代码服用

参考文章:

  1. 数组排序方法的性能比较(3):LINQ排序实现分析
  2. C# 泛型List的定义、作用、用法
  3. C#中数组、ArrayList和List三者的区别
posted @ 2017-04-09 10:32  任意球  阅读(2720)  评论(4编辑  收藏  举报