1. 简述 private、 protected、 public、internal修饰符的访问权限。
private: 只能在类的内部访问,即使子类也不能够访问
protected: 可以在类的内部访问,子类也可以访问,但无法通过外部访问。
public: 可以在类或子类的内部访问,也可以通过外部访问。
internal: 只能在程序集内部访问。
2. 列举ASP.NET 页面之间传递值的几种方式,并简单描述各自的优缺点。
QueryString, Session, Application, Cookie, Transfer.
QueryString: 存放于URL中,优点是使用方便,占用服务器资源少,缺点是安全性较差(暴露在了URL中),而且只能传递值,不能传递对象;
Session: 数据存放于服务器端,是用户级的全局资源,优点是使用方便,并且可以进行对象传递, 缺点是消耗服务器资源较大,容易丢失(在数据量比较大的情况下);
Application: 应用程序级全局变量,占用服务资源少,但因为是全局变量,所以应该尽量避免用户级的更改。
Cookie: 存放于客户端,依赖客户端,在客户端禁止Cookie的情况下则无法使用,此外,Cookie存在被盗窃的风险。
Transfer: ?
3. 一列数的规则如下: 1、1、2、3、5、8、13、21、34...... 求第30位数是多少, 用递归算法实现
public class FibonacciSequence
{
public static int GetValueFromIndex(int index)
{
if (index < 0) throw new ArgumentOutOfRangeException("Index");
if (index == 0) return 0;
return index == 1 ? 1 : (GetValueFromIndex(index - 1) + GetValueFromIndex(index - 2));
}
}
4. C#中的委托是什么?事件是不是一种委托?
事件是一种特殊的委托,但它delegate要多了一些限制。
5. 请说出override与overload的区别,并举例说明
这个问题可能需要从3个方面来讲:重载函数,覆盖函数与普通函数。
先看下面的代码:
namespace ConsoleApplication
{
class TestClass
{
public void MethodToOverload()
{
Console.WriteLine(String.Format("Class: {0}, Signature: {1}", "TestClass", "void MethodToOverload()"));
}
public void MethodToOverload(String para)
{
Console.WriteLine(String.Format("Class: {0}, Signature: {1}", "TestClass", "void MethodToOverload(String para)"));
}
public virtual void MethodToOverride()
{
Console.WriteLine(String.Format("Class: {0}, Signature: {1}", "TestClass", "void MethodToOerride()"));
}
public void NormalMethod()
{
Console.WriteLine(String.Format("Class: {0}, Signature: {1}", "TestClass", "void NormalMethod()"));
}
}
class TestInheritClass : TestClass
{
public override void MethodToOverride()
{
Console.WriteLine(String.Format("Class: {0}, Signature: {1}", "TestInheritClass", "void MethodToOerride()"));
}
public void NormalMethod()
{
Console.WriteLine(String.Format("Class: {0}, Signature: {1}", "TestInheritClass", "void NormalMethod()"));
}
}
class Program
{
static void Main(string[] args)
{
TestClass inheritClass = new TestInheritClass();
inheritClass.MethodToOverride();
inheritClass.NormalMethod();
((TestInheritClass)inheritClass).NormalMethod();
inheritClass.MethodToOverload();
inheritClass.MethodToOverload("para");
}
}
}
运行结果如下:
Class: TestInheritClass, Signature: void MethodToOerride()
Class: TestClass, Signature: void NormalMethod()
Class: TestInheritClass, Signature: void NormalMethod()
Class: TestClass, Signature: void MethodToOverload()
Class: TestClass, Signature: void MethodToOverload(String para)
第一个是被override的函数,我已经将继承类转换成了基类,但是,被override的函数依然调用的是继承类的成员函数,这是覆盖函数的特性。其关键是两个单词:virtual和override;所以,virtual函数是可以被派生类覆盖的(abstract也可以)。
第二个是普通的函数,我在派生类中又进行了重新定义,由于我已经将派生类的实例转换成了基类,所以,调用时,显示出来的是基类的特性。再将它转换成派生类,调用时有调用了派生类的函数,显然,这里没有看到覆盖这一特性。
第三个是重载函数,与覆盖函数不同的是,重载函数的依据是函数签名(除返回值),对于相同的函数名,根据参数的类型或数量来决定调用哪个函数。
6. 描述一下C#中索引器的实现过程,是否只能根据数字进行索引?
索引可以使用对象,详见示例代码:namespace ConsoleApplication
{
public class IndexClass
{
public string this[int index]
{
get{
return String.Format("{0}th element", index);
}
}
public string this[string key]
{
get
{
return String.Format("element with key = {0}", key);
}
}
}
}
7.请指出下面代码中可能出现的编译错误
namespace PlayAround
{
class Program
{
static void Main(string[] args)
{
int ival = 1024;
{
double ival = 3.14; // error 1
string str = "hello";
}
{
double str = 3.1415926;
}
string str = "world"; // error 2
}
}
}
两个编译错误的原因都是因为本地区块的声明与顶层的声明冲突,与声明的先后顺序无关。
8.下列选项中,哪些选项会导致编译失败
namespace PlayAround
{
internal class SampleClass
{
}
class Program
{
public const SampleClass objA = new SampleClass(); // option A
public const int iB = 0; // option B
public readonly SampleClass objC = new SampleClass(); // option C
public static SampleClass objD = new SampleClass(); // option D
public const SampleClass objE = null; // option E
static void Main(string[] args)
{
}
}
}
经测试发现,只有option A会导致编译失败,因为const在编译期间值就需要确定了,而new操作需要在堆上申请内存,两者存在时间上的冲突,注定无法共存。
9.下列程序会输出什么结果,并分析原因。
namespace ConsoleApplication
{
class Program
{
static IEnumerable<int> WithNoYield()
{
IList<int> list = new List<int>();
for (int i = 0; i < 5; i++)
{
Console.WriteLine(i.ToString());
if (i > 2) list.Add(i);
}
return list;
}
static IEnumerable<int> WithYield()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine(String.Format("Yield: {0}", i));
if (i > 2) yield return i;
}
}
static void Print(IEnumerable<int> list)
{
foreach (int i in list)
{
Console.WriteLine(i.ToString());
}
}
static void Main(string[] args)
{
var list1 = WithNoYield();
var list2 = WithYield();
Console.WriteLine("list1: ");
Print(list1);
Console.WriteLine("list2: ");
Print(list2);
}
}
输出结果:
0
1
2
3
4
list1:
3
4
list2:
Yield: 0
Yield: 1
Yield: 2
Yield: 3
3
Yield: 4
4
从结果中,可以发现,yield的计算并不是在返回时执行,而是在进行遍历的时候执行,这是一种延迟计算的概念。