} }
static void Main(string[] args) {
C tmpObj = new C(); //调用时也要显式转换
Console.WriteLine("Count property: {0}", ((IA)tmpObj).Count); Console.WriteLine("Count ((IB)tmpObj).Count());
Console.ReadLine(); } } } 结果:
Count property: 100 Count function: 10000
18.抽象类和接口的区别? 答:
抽象类(abstract class)可以包含功能定义和实现,接口(interface)只能包含功能定义
抽象类是从一系列相关对象中抽象出来的概念, 因此反映的是事物的内部共性;接口是为了满足外部调用而定义的一个功能约定, 因此反映的是事物的外部特性
分析对象,提炼内部共性形成抽象类,用以表示对象本质,即“是什么” 为外部提供调用或功能需要扩充时优先使用接口
19.别名指示符是什么? 答:
通过别名指示符我们可以为某个类型起一个别名
主要用于解决两个命名空间内有同名类型的冲突或避免使用冗余的命名空间 别名指示符只在一个单元文件内起作用 示例:
Class1.cs: using System;
using System.Collections.Generic; using System.Text;
namespace com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01 {
class Class1 {
public override string ToString()
function:
{0}",
{
return Class1"; } } }
Class2.cs
using System;
using System.Collections.Generic; using System.Text;
namespace com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02 {
class Class1 {
public override string ToString() {
return Class1"; } } }
主单元(Program.cs): using System;
using System.Collections.Generic; using System.Text;
//使用别名指示符解决同名类型(51aspx)的冲突
using Lib01Class1 = com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01.Class1; using Lib02Class2 = com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02.Class1; namespace Example19 {
class Program {
static void Main(string[] args) {
Lib01Class1 tmpObj1 = new Lib01Class1(); Lib02Class2 tmpObj2 = new Lib02Class2();
"com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02's "com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01's
Console.WriteLine(tmpObj1); Console.WriteLine(tmpObj2); Console.ReadLine(); } } } 结果:
com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01's Class1 com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02's Class1
20.如何释放非托管资源? 答:
.NET 平台在内存管理方面提供了GC(Garbage Collection),负责自动释放托管资源和内存回收的工作,但它无法对非托管资源进行释放,这时我们必须自己提供方法来释放对象内分配的非托管资源,比如你在对象的实现代码中使用了一个COM对象
最简单的办法,可以通过实现protected void Finalize()(析构函数会在编译时变成这个东东)来释放非托管资源,因为GC在释放对象时会检查该对象是否实现了 Finalize() 方法,如果是则调用它。但,据说这样会降低效率。。。
有一种更好的,那就是通过实现一个接口显式的提供给客户调用端手工释放对象的方法,而不是傻傻的等着GC来释放我们的对象(何况效率又那么低)
System 命名空间内有一个 IDisposable 接口,拿来做这事非常合适,就省得我们自己再声明一个接口了 另外补充一句,这种实现并不一定要使用了非托管资源后才用,如果你设计的类会在运行时有大些的实例(象 GIS 中的Geometry),为了优化程序性能,你也可以通过实现该接口让客户调用端在确认不需要这些对象时手工释放它们 示例:
using System;
using System.Collections.Generic; using System.Text; namespace Example20 {
class Program {
class Class1 : IDisposable {
//析构函数,编译后变成 protected void Finalize(),GC会在回收对象前会调用调用该方法 ~Class1() {
Dispose(false); }
//通过实现该接口,客户可以显式地释放对象,而不需要等待GC来释放资源,据说那样会降低效率
void IDisposable.Dispose() {
Dispose(true); }
//将释放非托管资源设计成一个虚函数,提供在继承类中释放基类的资源的能力 protected virtual void ReleaseUnmanageResources() {
//Do something... }
//私有函数用以释放非托管资源 private void Dispose(bool disposing) {
ReleaseUnmanageResources();
//为true时表示是客户显式调用了释放函数,需通知GC不要再调用对象的Finalize方法
//为false时肯定是GC调用了对象的Finalize方法,所以没有必要再告诉GC你不要调用我的Finalize方法啦
if (disposing) {
GC.SuppressFinalize(this); } } }
static void Main(string[] args) {
//tmpObj1没有手工释放资源,就等着GC来慢慢的释放它吧 Class1 tmpObj1 = new Class1();
//tmpObj2调用了Dispose方法,传说比等着GC来释放它效率要调一些 //个人认为是因为要逐个对象的查看其元数据,以确认是否实现了Dispose方法吧 //当然最重要的是我们可以自己确定释放的时间以节省内存,优化程序运行效率 Class1 tmpObj2 = new Class1(); ((IDisposable)tmpObj2).Dispose(); } } }
21.P/Invoke是什么? 答:
在受控代码与非受控代码进行交互时会产生一个事务(transition) ,这通常发生在使用平台调用服务(Platform Invocation Services),即P/Invoke
如调用系统的 API 或与 COM 对象打交道,通过 System.Runtime.InteropServices 命名空间
虽然使用 Interop 非常方便,但据估计每次调用事务都要执行 10 到 40 条指令,算起来开销也不少,所以我们要尽量少调用事务
如果非用不可,建议本着一次调用执行多个动作,而不是多次调用每次只执行少量动作的原则
22.StringBuilder 和 String 的区别? 答:
String 虽然是一个引用类型,但在赋值操作时会产生一个新的对象,而 StringBuilder 则不会 所以在大量字符串拼接或频繁对某一字符串进行操作时最好使用 StringBuilder,不要使用 String 示例:
using System;
using System.Collections.Generic; using System.Text; namespace Example22 {
class Program {
static void Main(string[] args) {
const int cycle = 100000;
long vTickCount = Environment.TickCount; String str = null;
for (int i = 0; i < cycle; i++) str += i.ToString();
Console.WriteLine("String: {0} MSEL", Environment.TickCount - vTickCount);
vTickCount = Environment.TickCount;
//看到这个变量名我就生气,奇怪为什么大家都使它呢? :) StringBuilder sb = new StringBuilder(); for (int i = 0; i < cycle; i++) sb.Append(i);
Console.WriteLine("StringBuilder: Environment.TickCount - vTickCount); Console.ReadLine(); } } } 结果:
String: 102047 MSEL StringBuilder: 46 MSEL
{0}
MSEL",