1.静态成员和非静态成员的区别?
2.const 和 static readonly 区别?
3.extern 是什么意思?
4.abstract 是什么意思?
5.internal 修饰符起什么作用?
6.sealed 修饰符是干什么的?
7.override 和 overload 的区别?
8.什么是索引指示器?
9.new 修饰符是起什么作用?
10.this 关键字的含义?
11.可以使用抽象函数重写基类中的虚函数吗?
12.密封类可以有虚函数吗?
13.什么是属性访问器?
14.abstract 可以和 virtual 一起使用吗?可以和 override 一起使用吗?
15.接口可以包含哪些成员?
16.类和结构的区别?
17.接口的多继承会带来哪些问题?
18.抽象类和接口的区别?
19.别名指示符是什么?
20.如何手工释放资源?
21.P/Invoke是什么?
22.StringBuilder 和 String 的区别?
23.explicit 和 implicit 的含义?
24.params 有什么用?
25.什么是反射?
以下是我做的一份参考答案(C# 语言范畴之内),如果有不准确、不全面的,欢迎各位朋友指正!
1.静态成员和非静态成员的区别?
答:
静态变量使用 static 修饰符进行声明,在类被实例化时创建,通过类进行访问
不带有 static 修饰符声明的变量称做非静态变量,在对象被实例化时创建,通过对象进行访问
一个类的所有实例的同一静态变量都是同一个值,同一个类的不同实例的同一非静态变量可以是不同的值
静态函数的实现里不能使用非静态成员,如非静态变量、非静态函数等

2

3

4

5

6


7

8


9

10


11

12

13

14

15


16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

Class1's staticStr: Class
tmpObj1's notstaticStr: tmpObj1
tmpObj2's notstaticStr: tmpObj2
2.const 和 static readonly 区别?
答:
const
用 const 修饰符声明的成员叫常量,是在编译期初始化并嵌入到客户端程序
static readonly
用 static readonly 修饰符声明的成员依然是变量,只不过具有和常量类似的使用方法:通过类进行访问、初始化后不可以修改。但与常量不同的是这种变量是在运行期初始化
示例:
测试类:
1using System;
2using System.Collections.Generic;
3using System.Text;
4![]()
5namespace Example02Lib
6{
7public class Class1
8{
9public const String strConst = "Const";
10public static readonly String strStaticReadonly = "StaticReadonly";
11//public const String strConst = "Const Changed";
12//public static readonly String strStaticReadonly = "StaticReadonly Changed";
13}
14}
客户端代码:
1using System;
2using System.Collections.Generic;
3using System.Text;
4using Example02Lib;
5![]()
6namespace Example02
7{
8class Program
9{
10static void Main(string[] args)
11{
12//修改Example02中Class1的strConst初始值后,只编译Example02Lib项目
13//然后到资源管理器里把新编译的Example02Lib.dll拷贝Example02.exe所在的目录,执行Example02.exe
14//切不可在IDE里直接调试运行因为这会重新编译整个解决方案!!
15![]()
16//可以看到strConst的输出没有改变,而strStaticReadonly的输出已经改变
17//表明Const变量是在编译期初始化并嵌入到客户端程序,而StaticReadonly是在运行时初始化的
18Console.WriteLine("strConst : {0}", Class1.strConst);
19Console.WriteLine("strStaticReadonly : {0}", Class1.strStaticReadonly);
20![]()
21Console.ReadLine();
22}
23}
24}
结果:
strConst : Const
strStaticReadonly : StaticReadonly
修改后的示例:
测试类:
1using System;
2using System.Collections.Generic;
3using System.Text;
4![]()
5namespace Example02Lib
6{
7public class Class1
8{
9//public const String strConst = "Const";
10//public static readonly String strStaticReadonly = "StaticReadonly";
11public const String strConst = "Const Changed";
12public static readonly String strStaticReadonly = "StaticReadonly Changed";
13}
14}
结果
strConst : Const
strStaticReadonly : StaticReadonly Changed
3.extern 是什么意思?
答:
extern 修饰符用于声明由程序集外部实现的成员函数
经常用于系统API函数的调用(通过 DllImport )。注意,和DllImport一起使用时要加上 static 修饰符
也可以用于对于同一程序集不同版本组件的调用(用 extern 声明别名)
不能与 abstract 修饰符同时使用
示例:
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Runtime.InteropServices;
5![]()
6namespace Example03
7{
8class Program
9{
10//注意DllImport是一个Attribute Property,在System.Runtime.InteropServices命名空间中定义
11//extern与DllImport一起使用时必须再加上一个static修饰符
12[DllImport("User32.dll")]
13public static extern int MessageBox(int Handle, string Message, string Caption, int Type);
14![]()
15static int Main()
16![]()
{
17string myString;
18Console.Write("Enter your message: ");
19myString = Console.ReadLine();
20return MessageBox(0, myString, "My Message Box", 0);
21}
22}
23}
4.abstract 是什么意思?
答:
abstract 修饰符可以用于类、方法、属性、事件和索引指示器(indexer),表示其为抽象成员
abstract 不可以和 static 、virtual 一起使用
声明为 abstract 成员可以不包括实现代码,但只要类中还有未实现的抽象成员(即抽象类),那么它的对象就不能被实例化,通常用于强制继承类必须实现某一成员
示例:
1using System;
2using System.Collections.Generic;
3using System.Text;
4![]()
5namespace Example04
6![]()
{
7基类,抽象类#region 基类,抽象类
8public abstract class BaseClass
9![]()
{
10//抽象属性,同时具有get和set访问器表示继承类必须将该属性实现为可读写
11public abstract String Attribute
12![]()
{
13get;
14set;
15}
16![]()
17//抽象方法,传入一个字符串参数无返回值
18public abstract void Function(String value);
19![]()
20//抽象事件,类型为系统预定义的代理(delegate):EventHandler
21public abstract event EventHandler Event;
22![]()
23//抽象索引指示器,只具有get访问器表示继承类必须将该索引指示器实现为只读
24public abstract Char this[int Index]
25![]()
{
26get;
27}
28}
29#endregion
30![]()
31继承类#region 继承类
32public class DeriveClass : BaseClass
33![]()
{
34private String attribute;
35![]()
36public override String Attribute
37![]()
{
38get
39![]()
{
40return attribute;
41}
42set
43![]()
{
44attribute = value;
45}
46}
47public override void Function(String value)
48![]()
{
49attribute = value;
50if (Event != null)
51![]()
{
52Event(this, new EventArgs());
53}
54}
55public override event EventHandler Event;
56public override Char this[int Index]
57![]()
{
58get
59![]()
{
60return attribute[Index];
61}
62}
63}
64#endregion
65![]()
66class Program
67![]()
{
68static void OnFunction(object sender, EventArgs e)
69![]()
{
70for (int i = 0; i < ((DeriveClass)sender).Attribute.Length; i++)
71![]()
{
72Console.WriteLine(((DeriveClass)sender)[i]);
73}
74}
75static void Main(string[] args)
76![]()
{
77DeriveClass tmpObj = new DeriveClass();
78![]()
79tmpObj.Attribute = "1234567";
80Console.WriteLine(tmpObj.Attribute);
81![]()
82//将静态函数OnFunction与tmpObj对象的Event事件进行关联
83tmpObj.Event += new EventHandler(OnFunction);
84![]()
85tmpObj.Function("7654321");
86![]()
87Console.ReadLine();
88}
89}
90}
结果:
1234567
7
6
5
4
3
2
1
5.internal 修饰符起什么作用?
答:
internal 修饰符可以用于类型或成员,使用该修饰符声明的类型或成员只能在同一程集内访问
接口的成员不能使用 internal 修饰符
值得注意的是,如果为 internal 成员加上了 protected 修饰符,这时的访问级别为 internal 或 protected。只是看字面意思容易弄错,许多人认为 internal protected 应该是“只有同一个程序集中的子类可以访问”,但其实它表示“同一个程序集中的所有类,以及所有程序集中的子类都可以访问”
示例
Example05Lib 项目的 Class1
1using System;
2using System.Collections.Generic;
3using System.Text;
4![]()
5namespace Example05Lib
6![]()
{
7public class Class1
8![]()
{
9internal String strInternal = null;
10public String strPublic;
11internal protected String strInternalProtected = null;
12}
13}
结果
Example05Lib 项目的 Class2 类可以访问到 Class1 的 strInternal 成员,当然也可以访问到 strInternalProtected 成员,因为他们在同一个程序集里
Example05 项目里的 Class3 类无法访问到 Class1 的 strInternal 成员,因为它们不在同一个程序集里。但却可以访问到 strInternalProtected 成员,因为 Class3 是 Class1 的继承类
Example05 项目的 Program 类既无法访问到 Class1 的 strInternal 成员,也无法访问到 strInternalProtected 成员,因为它们既不在同一个程序集里也不存在继承关系
答:
sealed 修饰符表示密封
用于类时,表示该类不能再被继承,不能和 abstract 同时使用,因为这两个修饰符在含义上互相排斥
用于方法和属性时,表示该方法或属性不能再被重写,必须和 override 关键字一起使用,因为使用 sealed 修饰符的方法或属性肯定是基类中相应的虚成员
通常用于实现第三方类库时不想被客户端继承,或用于没有必要再继承的类以防止滥用继承造成层次结构体系混乱
恰当的利用 sealed 修饰符也可以提高一定的运行效率,因为不用考虑继承类会重写该成员
示例:
1using System;
2using System.Collections.Generic;
3using System.Text;
4![]()
5namespace Example06
6![]()
{
7class Program
8![]()
{
9class A
10![]()
{
11public virtual void F()
12![]()
{
13Console.WriteLine("A.F");
14}
15public virtual void G()
16![]()
{
17Console.WriteLine("A.G");
18}
19}
20class B : A
21![]()
{
22public sealed override void F()
23![]()
{
24Console.WriteLine("B.F");
25}
26public override void G()
27![]()
{
28Console.WriteLine("B.G");
29}
30}
31class C : B
32![]()
{
33public override void G()
34![]()
{
35Console.WriteLine("C.G");
36}
37}
38static void Main(string[] args)
39![]()
{
40new A().F();
41new A().G();
42new B().F();
43new B().G();
44new C().F();
45new C().G();
46![]()
47Console.ReadLine();
48}
49}
50}
结果:
类 B 在继承类 A 时可以重写两个虚函数,如图所示:
由于类 B 中对 F 方法进行了密封, 类 C 在继承类 B 时只能重写一个函数,如图所示:
控制台输出结果,类 C 的方法 F 只能是输出 类B 中对该方法的实现:
A.F
A.G
B.F
B.G
B.F
C.G
7.override 和 overload 的区别?
答:
override 表示重写,用于继承类对基类中虚成员的实现
overload 表示重载,用于同一个类中同名方法不同参数(包括类型不同或个数不同)的实现
示例:
1using System;
2using System.Collections.Generic;
3using System.Text;
4![]()
5namespace Example07
6![]()
{
7class Program
8![]()
{
9class BaseClass
10![]()
{
11public virtual void F()
12![]()
{
13Console.WriteLine("BaseClass.F");
14}
15}
16class DeriveClass : BaseClass
17![]()
{
18public override void F()
19![]()
{
20base.F();
21Console.WriteLine("DeriveClass.F");
22}
23public void Add(int Left, int Right)
24![]()
{
25Console.WriteLine("Add for Int: {0}", Left + Right);
26}
27public void Add(double Left, double Right)
28![]()
{
29Console.WriteLine("Add for int: {0}", Left + Right);
30}
31}
32static void Main(string[] args)
33![]()
{
34DeriveClass tmpObj = new DeriveClass();
35tmpObj.F();
36tmpObj.Add(1, 2);
37tmpObj.Add(1.1, 2.2);
38![]()
39Console.ReadLine();
40}
41}
42}
结果:
BaseClass.F
DeriveClass.F
Add for Int: 3
Add for int: 3.3
8.什么是索引指示器?
答:
实现索引指示器(indexer)的类可以象数组那样使用其实例后的对象,但与数组不同的是索引指示器的参数类型不仅限于int
简单来说,其本质就是一个含参数属性
示例:
1using System;
2using System.Collections.Generic;
3using System.Text;
4![]()
5namespace Example08
6![]()
{
7public class Point
8![]()
{
9private double x, y;
10public Point(double X, double Y)
11![]()
{
12x = X;
13y = Y;
14}
15//重写ToString方法方便输出
16public override string ToString()
17![]()
{
18return String.Format("X: {0} , Y: {1}", x, y);
19}
20}
21public class Points
22![]()
{
23Point[] points;
24public Points(Point[] Points)
25![]()
{
26points = Points;
27}
28public int PointNumber
29![]()
{
30get
31![]()
{
32return points.Length;
33}
34}
35//实现索引访问器
36public Point this[int Index]
37![]()
{
38get
39![]()
{
40return points[Index];
41}
42}
43}
44![]()
45//感谢watson hua(http://huazhihao.cnblogs.com/)的指点
46//索引指示器的实质是含参属性,参数并不只限于int
47class WeatherOfWeek
48![]()
{
49public string this[int Index]
50![]()
{
51get
52![]()
{
53//注意case段使用return直接返回所以不需要break
54switch (Index)
55![]()
{
56case 0:
57![]()
{
58return "Today is cloudy!";
59}
60case 5:
61![]()
{
62return "Today is thundershower!";
63}
64default:
65![]()
{
66return "Today is fine!";
67}
68}
69}
70}
71public string this[string Day]
72![]()
{
73get
74![]()
{
75string TodayWeather = null;
76//switch的标准写法
77switch (Day)
78![]()
{
79case "Sunday":
80![]()
{
81TodayWeather = "Today is cloudy!";
82break;
83}
84case "Friday":
85![]()
{
86TodayWeather = "Today is thundershower!";
87break;
88}
89default:
90![]()
{
91TodayWeather = "Today is fine!";
92break;
93}
94}
95return TodayWeather;
96}
97}
98}
99class Program
100![]()
{
101static void Main(string[] args)
102![]()
{
103Point[] tmpPoints = new Point[10];
104for (int i = 0; i < tmpPoints.Length; i++)
105![]()
{
106tmpPoints[i] = new Point(i, Math.Sin(i));
107}
108![]()
109Points tmpObj = new Points(tmpPoints);
110for (int i = 0; i < tmpObj.PointNumber; i++)
111![]()
{
112Console.WriteLine(tmpObj[i]);
113}
114![]()
115![]()
116string[] Week = new string[]
{ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Staurday"};
117WeatherOfWeek tmpWeatherOfWeek = new WeatherOfWeek();
118for (int i = 0; i < 6; i++)
119![]()
{
120Console.WriteLine(tmpWeatherOfWeek[i]);
121}
122foreach (string tmpDay in Week)
123![]()
{
124Console.WriteLine(tmpWeatherOfWeek[tmpDay]);
125}
126![]()
127Console.ReadLine();
128}
129}
130}
结果:
X: 0 , Y: 0
X: 1 , Y: 0.841470984807897
X: 2 , Y: 0.909297426825682
X: 3 , Y: 0.141120008059867
X: 4 , Y: -0.756802495307928
X: 5 , Y: -0.958924274663138
X: 6 , Y: -0.279415498198926
X: 7 , Y: 0.656986598718789
X: 8 , Y: 0.989358246623382
X: 9 , Y: 0.412118485241757
Today is cloudy!
Today is fine!
Today is fine!
Today is fine!
Today is fine!
Today is thundershower!
Today is cloudy!
Today is fine!
Today is fine!
Today is fine!
Today is fine!
Today is thundershower!
Today is fine!