最近找工作面的面试题目汇总(二)
引言
今天继续的是去那几家面试公司的一些面试题目,适合刚出来或者两三年经验的,大牛可以适量吐槽下,找工作的可以补补基础。
这系列的第一篇文章请查看这里:最近找工作面的面试题目汇总(一)
1.请从中选择引用类型或值类型
说起题,我们不由得会想到几个名词:栈和堆,值类型和引用类型,装箱和拆箱。
这些概念可是C#的入门基础呀,咱们还是要搞懂的,详细可以查看参考文章。
这里要提下一个知识点:C#新手都有一个误区:值类型的值都是保存在栈里,实际应该是:值类型的值是在它声明的位置存储的。即局部变量(参数)的值会在栈里,而当值类型作为引用类型的类型成员的话,会跟随对象,即存储在托管堆里。
顺便提供几张经典图片便于读者理解:
最后在这些概念里,我觉得比较重要的精华提要:
- 对于栈和堆的概念,我们声明值类型和引用类型的时候,为什么会分配两种内存而不是一种呢?只要我们仔细想一下,基本类型他们并不复杂,很多时候只包含简单的值,比如 i=1,而对象类型则相对复杂,它们则会引用其他对象或基本类型。简单来说,对象类型需要动态内存而基本类型则需要静态内存。若需要分配动态内存,那么就分配到堆上;反之在栈上。
- 对于值类型和引用类型的概念,值类型,它们的内存值都分配在栈上,当我们把一个int值分配给另外一个int值时,需要创建一个完全不同的拷贝。换句话说,你可以改变其中任何一个而不会影响另外一个。这种数据类型被称为值类型;引用类型,当我们创建一个对象,并把一个对象赋给另外一个对象时,它们的指针指向相同的内存(如下图,当我们把obj赋给obj1时,它们指向相同的内存)。换句话说,我们改变其中一个,会影响到另外一个。
参考文章:
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的介绍就到这里,有下面几点需要注意的地方
- lock的是引用类型的对象,string类型除外。
- lock推荐的做法是使用静态的、只读的、私有的对象。
- 保证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关键字来声明数组
要求:
- 在方法声明中的 params 关键字之后不允许任何其他参数,并且在方法声明中只允许一个 params 关键字。
- 该参数必须标识一个一维数组,但类型不限,对该参数传递null或者0个数目的数组的引用都是合法的
参考文章:
6.怎么获取用户请求的Url地址
- 获取 完整url:
string url=Request.Url.ToString();
参考文章:
写出三元运算符实例
这个我就不说了。 ?:
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个
参考文章:
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)的两个函数的函数名虽然相同,但函数特征不同。
- 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)那么调的是第二个。
参考文章:
9. using的意义
简述:
提供能确保正确使用 IDisposable 对象的方便语法。它的用途就是清理非托管资源,不受GC控制的资源。Using结束后会隐式的调用Disposable方法
- 有许多其他类别的非托管资源和封装这些资源的类库类型,所有这些类型都必须实现IDisposable接口,而using语句确保即使在调用对象上的方法时发生异常Dispose方法也会被调用。
- 实际上,将对象放入 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();
}
}
参考文章:
10.List是什么
因为ArrayList存在不安全类型与装箱拆箱的缺点,所以出现了泛型的概念。List类是ArrayList类的泛型等效类,它的大部分用法都与ArrayList相似,因为List类也继承了IList接口。最关键的区别在于,在声明List集合时,我们同时需要为其声明List集合内数据的对象类型。
优点
- 类型安全
- 性能增强
- 代码服用
参考文章: