您的位置:首页 > 编程语言 > C#

C#基础概念二十五问

2011-10-13 16:25 369 查看

C#基础概念二十五问

注:本文部份资料来自网络,如有侵权,请与我联系,我会在第一时间声明引用或将其删除!当初学C#时是找个人大概问了一下数据类型和分支语句就开始做项目了。这两天又全面的看了一下相关的基础知识(学而时习之嘛),总结了25个问题:1.静态成员和非静态成员的区别?2.const和staticreadonly区别?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修饰符声明的变量称做非静态变量,在对象被实例化时创建,通过对象进行访问一个类的所有实例的同一静态变量都是同一个值,同一个类的不同实例的同一非静态变量可以是不同的值静态函数的实现里不能使用非静态成员,如非静态变量、非静态函数等示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample01

{

classProgram

{

classClass1

{

publicstaticStringstaticStr="Class";

publicStringnotstaticStr="Obj";

}

staticvoidMain(string[]args)

{

//静态变量通过类进行访问,该类所有实例的同一静态变量都是同一个值

Console.WriteLine("Class1'sstaticStr:{0}",Class1.staticStr);


Class1tmpObj1=newClass1();

tmpObj1.notstaticStr="tmpObj1";

Class1tmpObj2=newClass1();

tmpObj2.notstaticStr="tmpObj2";


//非静态变量通过对象进行访问,不同对象的同一非静态变量可以有不同的值

Console.WriteLine("tmpObj1'snotstaticStr:{0}",tmpObj1.notstaticStr);

Console.WriteLine("tmpObj2'snotstaticStr:{0}",tmpObj2.notstaticStr);


Console.ReadLine();

}

}

}
结果:Class1'sstaticStr:ClasstmpObj1'snotstaticStr:tmpObj1tmpObj2'snotstaticStr:tmpObj22.const和staticreadonly区别?答:const用const修饰符声明的成员叫常量,是在编译期初始化并嵌入到客户端程序staticreadonly用staticreadonly修饰符声明的成员依然是变量,只不过具有和常量类似的使用方法:通过类进行访问、初始化后不可以修改。但与常量不同的是这种变量是在运行期初始化示例:测试类:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample02Lib

{

publicclassClass1

{

publicconstStringstrConst="Const";

publicstaticreadonlyStringstrStaticReadonly="StaticReadonly";

//publicconstStringstrConst="ConstChanged";

//publicstaticreadonlyStringstrStaticReadonly="StaticReadonlyChanged";

}

}


客户端代码:

usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;

usingExample02Lib;


namespaceExample02

{

classProgram

{

staticvoidMain(string[]args)

{

//修改Example02中Class1的strConst初始值后,只编译Example02Lib项目

//然后到资源管理器里把新编译的Example02Lib.dll拷贝Example02.exe所在的目录,执行Example02.exe

//切不可在IDE里直接调试运行因为这会重新编译整个解决方案!!


//可以看到strConst的输出没有改变,而strStaticReadonly的输出已经改变

//表明Const变量是在编译期初始化并嵌入到客户端程序,而StaticReadonly是在运行时初始化的

Console.WriteLine("strConst:{0}",Class1.strConst);

Console.WriteLine("strStaticReadonly:{0}",Class1.strStaticReadonly);


Console.ReadLine();

}

}

}
结果:strConst:ConststrStaticReadonly:StaticReadonly修改后的示例:测试类:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample02Lib

{

publicclassClass1

{

//publicconstStringstrConst="Const";

//publicstaticreadonlyStringstrStaticReadonly="StaticReadonly";

publicconstStringstrConst="ConstChanged";

publicstaticreadonlyStringstrStaticReadonly="StaticReadonlyChanged";

}

}
结果strConst:ConststrStaticReadonly:StaticReadonlyChanged3.extern是什么意思?答:extern修饰符用于声明由程序集外部实现的成员函数经常用于系统API函数的调用(通过DllImport)。注意,和DllImport一起使用时要加上static修饰符也可以用于对于同一程序集不同版本组件的调用(用extern声明别名)不能与abstract修饰符同时使用示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;

usingSystem.Runtime.InteropServices;


namespaceExample03

{

classProgram

{

//注意DllImport是一个AttributeProperty,在System.Runtime.InteropServices命名空间中定义

//extern与DllImport一起使用时必须再加上一个static修饰符

[DllImport("User32.dll")]

publicstaticexternintMessageBox(intHandle,stringMessage,stringCaption,intType);


staticintMain()

{

stringmyString;

Console.Write("Enteryourmessage:");

myString=Console.ReadLine();

returnMessageBox(0,myString,"MyMessageBox",0);

}

}

}
结果:

4.abstract是什么意思?答:abstract修饰符可以用于类、方法、属性、事件和索引指示器(indexer),表示其为抽象成员abstract不可以和static、virtual一起使用声明为abstract成员可以不包括实现代码,但只要类中还有未实现的抽象成员(即抽象类),那么它的对象就不能被实例化,通常用于强制继承类必须实现某一成员示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample04

{

#region基类,抽象类

publicabstractclassBaseClass

{

//抽象属性,同时具有get和set访问器表示继承类必须将该属性实现为可读写

publicabstractStringAttribute

{

get;

set;

}


//抽象方法,传入一个字符串参数无返回值

publicabstractvoidFunction(Stringvalue);


//抽象事件,类型为系统预定义的代理(delegate):EventHandler

publicabstracteventEventHandlerEvent;


//抽象索引指示器,只具有get访问器表示继承类必须将该索引指示器实现为只读

publicabstractCharthis[intIndex]

{

get;

}

}

#endregion


#region继承类

publicclassDeriveClass:BaseClass

{

privateStringattribute;


publicoverrideStringAttribute

{

get

{

returnattribute;

}

set

{

attribute=value;

}

}

publicoverridevoidFunction(Stringvalue)

{

attribute=value;

if(Event!=null)

{

Event(this,newEventArgs());

}

}

publicoverrideeventEventHandlerEvent;

publicoverrideCharthis[intIndex]

{

get

{

returnattribute[Index];

}

}

}

#endregion


classProgram

{

staticvoidOnFunction(objectsender,EventArgse)

{

for(inti=0;i<((DeriveClass)sender).Attribute.Length;i++)

{

Console.WriteLine(((DeriveClass)sender)[i]);

}

}

staticvoidMain(string[]args)

{

DeriveClasstmpObj=newDeriveClass();


tmpObj.Attribute="1234567";

Console.WriteLine(tmpObj.Attribute);


//将静态函数OnFunction与tmpObj对象的Event事件进行关联

tmpObj.Event+=newEventHandler(OnFunction);


tmpObj.Function("7654321");


Console.ReadLine();

}

}

}
结果:123456776543215.internal修饰符起什么作用?答:internal修饰符可以用于类型或成员,使用该修饰符声明的类型或成员只能在同一程集内访问接口的成员不能使用internal修饰符值得注意的是,如果为internal成员加上了protected修饰符,这时的访问级别为internal或protected。只是看字面意思容易弄错,许多人认为internalprotected应该是“只有同一个程序集中的子类可以访问”,但其实它表示“同一个程序集中的所有类,以及所有程序集中的子类都可以访问”示例Example05Lib项目的Class1
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample05Lib

{

publicclassClass1

{

internalStringstrInternal=null;

publicStringstrPublic;

internalprotectedStringstrInternalProtected=null;

}

}
结果Example05Lib项目的Class2类可以访问到Class1的strInternal成员,当然也可以访问到strInternalProtected成员,因为他们在同一个程序集里Example05项目里的Class3类无法访问到Class1的strInternal成员,因为它们不在同一个程序集里。但却可以访问到strInternalProtected成员,因为Class3是Class1的继承类Example05项目的Program类既无法访问到Class1的strInternal成员,也无法访问到strInternalProtected成员,因为它们既不在同一个程序集里也不存在继承关系

6.sealed修饰符是干什么的?答:sealed修饰符表示密封用于类时,表示该类不能再被继承,不能和abstract同时使用,因为这两个修饰符在含义上互相排斥用于方法和属性时,表示该方法或属性不能再被重写,必须和override关键字一起使用,因为使用sealed修饰符的方法或属性肯定是基类中相应的虚成员通常用于实现第三方类库时不想被客户端继承,或用于没有必要再继承的类以防止滥用继承造成层次结构体系混乱恰当的利用sealed修饰符也可以提高一定的运行效率,因为不用考虑继承类会重写该成员示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample06

{

classProgram

{

classA

{

publicvirtualvoidF()

{

Console.WriteLine("A.F");

}

publicvirtualvoidG()

{

Console.WriteLine("A.G");

}

}

classB:A

{

publicsealedoverridevoidF()

{

Console.WriteLine("B.F");

}

publicoverridevoidG()

{

Console.WriteLine("B.G");

}

}

classC:B

{

publicoverridevoidG()

{

Console.WriteLine("C.G");

}

}

staticvoidMain(string[]args)

{

newA().F();

newA().G();

newB().F();

newB().G();

newC().F();

newC().G();


Console.ReadLine();

}

}

}
结果:类B在继承类A时可以重写两个虚函数,如图所示:

由于类B中对F方法进行了密封,类C在继承类B时只能重写一个函数,如图所示:

控制台输出结果,类C的方法F只能是输出类B中对该方法的实现:A.FA.GB.FB.GB.FC.G7.override和overload的区别?答:override表示重写,用于继承类对基类中虚成员的实现overload表示重载,用于同一个类中同名方法不同参数(包括类型不同或个数不同)的实现示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample07

{

classProgram

{

classBaseClass

{

publicvirtualvoidF()

{

Console.WriteLine("BaseClass.F");

}

}

classDeriveClass:BaseClass

{

publicoverridevoidF()

{

base.F();

Console.WriteLine("DeriveClass.F");

}

publicvoidAdd(intLeft,intRight)

{

Console.WriteLine("AddforInt:{0}",Left+Right);

}

publicvoidAdd(doubleLeft,doubleRight)

{

Console.WriteLine("Addforint:{0}",Left+Right);

}

}

staticvoidMain(string[]args)

{

DeriveClasstmpObj=newDeriveClass();

tmpObj.F();

tmpObj.Add(1,2);

tmpObj.Add(1.1,2.2);


Console.ReadLine();

}

}

}
结果:BaseClass.FDeriveClass.FAddforInt:3Addforint:3.38.什么是索引指示器?答:实现索引指示器(indexer)的类可以象数组那样使用其实例后的对象,但与数组不同的是索引指示器的参数类型不仅限于int简单来说,其本质就是一个含参数属性示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample08

{

publicclassPoint

{

privatedoublex,y;

publicPoint(doubleX,doubleY)

{

x=X;

y=Y;

}

//重写ToString方法方便输出

publicoverridestringToString()

{

returnString.Format("X:{0},Y:{1}",x,y);

}

}

publicclassPoints

{

Point[]points;

publicPoints(Point[]Points)

{

points=Points;

}

publicintPointNumber

{

get

{

returnpoints.Length;

}

}

//实现索引访问器

publicPointthis[intIndex]

{

get

{

returnpoints[Index];

}

}

}


//感谢watsonhua(http://huazhihao.cnblogs.com/)的指点

//索引指示器的实质是含参属性,参数并不只限于int

classWeatherOfWeek

{

publicstringthis[intIndex]

{

get

{

//注意case段使用return直接返回所以不需要break

switch(Index)

{

case0:

{

return"Todayiscloudy!";

}

case5:

{

return"Todayisthundershower!";

}

default:

{

return"Todayisfine!";

}

}

}

}

publicstringthis[stringDay]

{

get

{

stringTodayWeather=null;

//switch的标准写法

switch(Day)

{

case"Sunday":

{

TodayWeather="Todayiscloudy!";

break;

}

case"Friday":

{

TodayWeather="Todayisthundershower!";

break;

}

default:

{

TodayWeather="Todayisfine!";

break;

}

}

returnTodayWeather;

}

}

}

classProgram

{

staticvoidMain(string[]args)

{

Point[]tmpPoints=newPoint[10];

for(inti=0;i<tmpPoints.Length;i++)

{

tmpPoints[i]=newPoint(i,Math.Sin(i));

}


PointstmpObj=newPoints(tmpPoints);

for(inti=0;i<tmpObj.PointNumber;i++)

{

Console.WriteLine(tmpObj[i]);

}



string[]Week=newstring[]{"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Staurday"};

WeatherOfWeektmpWeatherOfWeek=newWeatherOfWeek();

for(inti=0;i<6;i++)

{

Console.WriteLine(tmpWeatherOfWeek[i]);

}

foreach(stringtmpDayinWeek)

{

Console.WriteLine(tmpWeatherOfWeek[tmpDay]);

}


Console.ReadLine();

}

}

}
结果:X:0,Y:0X:1,Y:0.841470984807897X:2,Y:0.909297426825682X:3,Y:0.141120008059867X:4,Y:-0.756802495307928X:5,Y:-0.958924274663138X:6,Y:-0.279415498198926X:7,Y:0.656986598718789X:8,Y:0.989358246623382X:9,Y:0.412118485241757Todayiscloudy!Todayisfine!Todayisfine!Todayisfine!Todayisfine!Todayisthundershower!Todayiscloudy!Todayisfine!Todayisfine!Todayisfine!Todayisfine!Todayisthundershower!Todayisfine!9.new修饰符是起什么作用?答:new修饰符与new操作符是两个概念new修饰符用于声明类或类的成员,表示隐藏了基类中同名的成员。而new操作符用于实例化一个类型new修饰符只能用于继承类,一般用于弥补基类设计的不足new修饰符和override修饰符不可同时用在一个成员上,因为这两个修饰符在含义上互相排斥示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample09

{

classBaseClass

{

//基类设计者声明了一个PI的公共变量,方便进行运算

publicstaticdoublePI=3.1415;

}

classDervieClass:BaseClass

{

//继承类发现该变量的值不能满足运算精度,于是可以通过new修饰符显式隐藏基类中的声明

publicnewstaticdoublePI=3.1415926;

}

classProgram

{

staticvoidMain(string[]args)

{

Console.WriteLine(BaseClass.PI);

Console.WriteLine(DervieClass.PI);


Console.ReadLine();

}

}

}
结果:3.14153.141592610.this关键字的含义?答:this是一个保留字,仅限于构造函数和方法成员中使用在类的构造函数中出现表示对正在构造的对象本身的引用,在类的方法中出现表示对调用该方法的对象的引用,在结构的构造上函数中出现表示对正在构造的结构的引用,在结构的方法中出现表示对调用该方法的结果的引用this保留字不能用于静态成员的实现里,因为这时对象或结构并未实例化在C#系统中,this实际上是一个常量,所以不能使用this++这样的运算this保留字一般用于限定同名的隐藏成员、将对象本身做为参数、声明索引访问器、判断传入参数的对象是否为本身示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample10

{

classClass1

{

privatedoublec;

privatestringvalue;


publicdoubleC

{

get

{

returnc;

}

}

publicClass1(doublec)

{

//限定同名的隐藏成员

this.c=c;

}

publicClass1(Class1value)

{

//用对象本身实例化自己没有意义

if(this!=value)

{

c=value.C;

}

}

publicoverridestringToString()

{

//将对象本身做为参数

returnstring.Format("{0}Celsius={1}Fahrenheit",c,UnitTransClass.C2F(this));

}


//由于好奇,在这做了一个效率测试,想看看到底哪种方式访问成员变量更快,结论:区别不大。。。

publicstringTest1()

{

longvTickCount=Environment.TickCount;

for(inti=0;i<10000000;i++)

this.value=i.ToString();

returnstring.Format("Havethis.:{0}MSEL",Environment.TickCount-vTickCount);

}

publicstringTest2()

{

longvTickCount=Environment.TickCount;

for(inti=0;i<10000000;i++)

value=i.ToString();

returnstring.Format("Don'thavethis.:{0}MSEL",Environment.TickCount-vTickCount);

}

}

classUnitTransClass

{

publicstaticdoubleC2F(Class1value)

{

//摄氏到华氏的转换公式

return1.8*value.C+32;

}

}

classProgram

{

staticvoidMain(string[]args)

{

Class1tmpObj=newClass1(37.5);


Console.WriteLine(tmpObj);


Console.WriteLine(tmpObj.Test1());

Console.WriteLine(tmpObj.Test2());


Console.ReadLine();

}

}

}
结果:37.5Celsius=99.5FahrenheitHavethis.:4375MSELDon'thavethis.:4406MSEL11.可以使用抽象函数重写基类中的虚函数吗?答:可以需使用new修饰符显式声明,表示隐藏了基类中该函数的实现或增加override修饰符,表示抽象重写了基类中该函数的实现示例:
classBaseClass

{

publicvirtualvoidF()

{

Console.WriteLine("BaseClass.F");

}

}

abstractclassDeriveClass1:BaseClass

{

publicabstractnewvoidF();

}


//感谢watsonhua(http://huazhihao.cnblogs.com/)的指点

//是他提醒了我还可以用这种方法抽象重写基类的虚方法

abstractclassDeriveClass2:BaseClass

{

publicabstractoverridevoidF();

}
12.密封类可以有虚函数吗?答:可以,基类中的虚函数将隐式的转化为非虚函数,但密封类本身不能再增加新的虚函数示例:
classBaseClass

{

publicvirtualvoidF()

{

Console.WriteLine("BaseClass.F");

}

}

sealedclassDeriveClass:BaseClass

{

//基类中的虚函数F被隐式的转化为非虚函数


//密封类中不能再声明新的虚函数G

//publicvirtualvoidG()

//{

//Console.WriteLine("DeriveClass.G");

//}

}
13.什么是属性访问器?答:属性访问器(PropertyAccessor),包括get访问器和set访问器分别用于字段的读写操作其设计目的主要是为了实现面向对象(OO)中的封装思想。根据该思想,字段最好设为private,一个精巧的类最好不要直接把字段设为公有提供给客户调用端直接访问另外要注意属性本身并不一定和字段相联系14.abstract可以和virtual一起使用吗?可以和override一起使用吗?答:abstract修饰符不可以和static、virtual修饰符一起使用abstract修饰符可以和override一起使用,参见第11点示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample14

{

classBaseClass

{

publicvirtualvoidF()

{

Console.WriteLine("BaseClass.F");

}

}

abstractclassDeriveClass1:BaseClass

{

//在这里,abstract是可以和override一起使用的

publicabstractoverridevoidF();

}

classProgram

{

staticvoidMain(string[]args)

{

}

}

}
15.接口可以包含哪些成员?答:接口可以包含属性、方法、索引指示器和事件,但不能包含常量、域、操作符、构造函数和析构函数,而且也不能包含任何静态成员16.类和结构的区别?答:类:类是引用类型在堆上分配,类的实例进行赋值只是复制了引用,都指向同一段实际对象分配的内存类有构造和析构函数类可以继承和被继承结构:结构是值类型在栈上分配(虽然栈的访问速度比较堆要快,但栈的资源有限放),结构的赋值将分配产生一个新的对象。结构没有构造函数,但可以添加。结构没有析构函数结构不可以继承自另一个结构或被继承,但和类一样可以继承自接口示例:根据以上比较,我们可以得出一些轻量级的对象最好使用结构,但数据量大或有复杂处理逻辑对象最好使用类。如:Geoemtry(GIS里的一个概论,在OGC标准里有定义)最好使用类,而Geometry中点的成员最好使用结构
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample16

{

interfaceIPoint

{

doubleX

{

get;

set;

}

doubleY

{

get;

set;

}

doubleZ

{

get;

set;

}

}

//结构也可以从接口继承

structPoint:IPoint

{

privatedoublex,y,z;

//结构也可以增加构造函数

publicPoint(doubleX,doubleY,doubleZ)

{

this.x=X;

this.y=Y;

this.z=Z;

}

publicdoubleX

{

get{returnx;}

set{x=value;}

}

publicdoubleY

{

get{returnx;}

set{x=value;}

}

publicdoubleZ

{

get{returnx;}

set{x=value;}

}

}

//在此简化了点状Geometry的设计,实际产品中还包含Project(坐标变换)等复杂操作

classPointGeometry

{

privatePointvalue;


publicPointGeometry(doubleX,doubleY,doubleZ)

{

value=newPoint(X,Y,Z);

}

publicPointGeometry(Pointvalue)

{

//结构的赋值将分配新的内存

this.value=value;

}

publicdoubleX

{

get{returnvalue.X;}

set{this.value.X=value;}

}

publicdoubleY

{

get{returnvalue.Y;}

set{this.value.Y=value;}

}

publicdoubleZ

{

get{returnvalue.Z;}

set{this.value.Z=value;}

}

publicstaticPointGeometryoperator+(PointGeometryLeft,PointGeometryRigth)

{

returnnewPointGeometry(Left.X+Rigth.X,Left.Y+Rigth.Y,Left.Z+Rigth.Z);

}

publicoverridestringToString()

{

returnstring.Format("X:{0},Y:{1},Z:{2}",value.X,value.Y,value.Z);

}

}

classProgram

{

staticvoidMain(string[]args)

{

PointtmpPoint=newPoint(1,2,3);


PointGeometrytmpPG1=newPointGeometry(tmpPoint);

PointGeometrytmpPG2=newPointGeometry(tmpPoint);

tmpPG2.X=4;

tmpPG2.Y=5;

tmpPG2.Z=6;


//由于结构是值类型,tmpPG1和tmpPG2的坐标并不一样

Console.WriteLine(tmpPG1);

Console.WriteLine(tmpPG2);


//由于类是引用类型,对tmpPG1坐标修改后影响到了tmpPG3

PointGeometrytmpPG3=tmpPG1;

tmpPG1.X=7;

tmpPG1.Y=8;

tmpPG1.Z=9;

Console.WriteLine(tmpPG1);

Console.WriteLine(tmpPG3);


Console.ReadLine();

}

}

}
结果:X:1,Y:2,Z:3X:4,Y:5,Z:6X:7,Y:8,Z:9X:7,Y:8,Z:917.接口的多继承会带来哪些问题?答:C#中的接口与类不同,可以使用多继承,即一个子接口可以有多个父接口。但如果两个父成员具有同名的成员,就产生了二义性(这也正是C#中类取消了多继承的原因之一),这时在实现时最好使用显式的声明示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample17

{

classProgram

{

//一个完整的接口声明示例

interfaceIExample

{

//属性

stringP

{

get;

set;

}

//方法

stringF(intValue);

//事件

eventEventHandlerE;

//索引指示器

stringthis[intIndex]

{

get;

set;

}

}

interfaceIA

{

intCount{get;set;}

}

interfaceIB

{

intCount();

}

//IC接口从IA和IB多重继承

interfaceIC:IA,IB

{

}

classC:IC

{

privateintcount=100;

//显式声明实现IA接口中的Count属性

intIA.Count

{

get{return100;}

set{count=value;}

}

//显式声明实现IB接口中的Count方法

intIB.Count()

{

returncount*count;

}

}

staticvoidMain(string[]args)

{

CtmpObj=newC();


//调用时也要显式转换

Console.WriteLine("Countproperty:{0}",((IA)tmpObj).Count);

Console.WriteLine("Countfunction:{0}",((IB)tmpObj).Count());


Console.ReadLine();

}

}

}
结果:Countproperty:100Countfunction:1000018.抽象类和接口的区别?答:抽象类(abstractclass)可以包含功能定义和实现,接口(interface)只能包含功能定义抽象类是从一系列相关对象中抽象出来的概念,因此反映的是事物的内部共性;接口是为了满足外部调用而定义的一个功能约定,因此反映的是事物的外部特性分析对象,提炼内部共性形成抽象类,用以表示对象本质,即“是什么”为外部提供调用或功能需要扩充时优先使用接口19.别名指示符是什么?答:通过别名指示符我们可以为某个类型起一个别名主要用于解决两个命名空间内有同名类型的冲突或避免使用冗余的命名空间别名指示符在所有命名空间最外层定义,作用域为整个单元文件。如果定义在某个命名空间内,那么它只在直接隶属的命名空间内起作用示例:Class1.cs:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespacecom.nblogs.reonlyrun.CSharp25QExample.Example19.Lib01

{

classClass1

{

publicoverridestringToString()

{

return"com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib01'sClass1";

}

}

}
Class2.cs:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespacecom.nblogs.reonlyrun.CSharp25QExample.Example19.Lib02

{

classClass1

{

publicoverridestringToString()

{

return"com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib02'sClass1";

}

}

}
主单元(Program.cs):
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


//使用别名指示符解决同名类型的冲突

//在所有命名空间最外层定义,作用域为整个单元文件

usingLib01Class1=com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib01.Class1;

usingLib02Class2=com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib02.Class1;


namespaceExample19

{

namespaceTest1

{

//Test1Class1在Test1命名空间内定义,作用域仅在Test1之内

usingTest1Class1=com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib01.Class1;


classClass1

{

//Lib01Class1和Lib02Class2在这可以正常使用

Lib01Class1tmpObj1=newLib01Class1();

Lib02Class2tmpObj2=newLib02Class2();

//TestClass1在这可以正常使用

Test1Class1tmpObj3=newTest1Class1();

}

}

namespaceTest2

{

usingTest1Class2=com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib01.Class1;


classProgram

{

staticvoidMain(string[]args)

{

//Lib01Class1和Lib02Class2在这可以正常使用

Lib01Class1tmpObj1=newLib01Class1();

Lib02Class2tmpObj2=newLib02Class2();


//注意这里,TestClass1在这不可以正常使用。

//因为,在Test2命名空间内不能使用Test1命名空间定义的别名

//Test1Class1tmpObj3=newTest1Class1();


//TestClass2在这可以正常使用

Test1Class2tmpObj3=newTest1Class2();


Console.WriteLine(tmpObj1);

Console.WriteLine(tmpObj2);

Console.WriteLine(tmpObj3);


Console.ReadLine();

}

}

}

}
结果:com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib01'sClass1com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib02'sClass1com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib01'sClass120.如何手工释放资源?答:.NET平台在内存管理方面提供了GC(GarbageCollection),负责自动释放托管资源和内存回收的工作。但在以下两种情况需要我们手工进行资源释放:一、由于它无法对非托管资源进行释放,所以我们必须自己提供方法来释放对象内分配的非托管资源,比如你在对象的实现代码中使用了一个COM对象;二、你的类在运行是会产生大量实例(象GIS中的Geometry),必须自己手工释放这些资源以提高程序的运行效率最理想的办法是通过实现一个接口显式的提供给客户调用端手工释放对象,System命名空间内有一个IDisposable接口,拿来做这事非常合适,省得我们自己再声明一个接口了
示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample20

{

classProgram

{

classClass1:IDisposable

{

//析构函数,编译后变成protectedvoidFinalize(),GC会在回收对象前会调用调用该方法

~Class1()

{

Dispose(false);

}


//通过实现该接口,客户可以显式地释放对象,而不需要等待GC来释放资源,据说那样会降低效率

voidIDisposable.Dispose()

{

Dispose(true);

}


//将释放非托管资源设计成一个虚函数,提供在继承类中释放基类的资源的能力

protectedvirtualvoidReleaseUnmanageResources()

{

//Dosomething...

}


//私有函数用以释放非托管资源

privatevoidDispose(booldisposing)

{

ReleaseUnmanageResources();


//为true时表示是客户显式调用了释放函数,需通知GC不要再调用对象的Finalize方法

//为false时肯定是GC调用了对象的Finalize方法,所以没有必要再告诉GC你不要调用我的Finalize方法啦

if(disposing)

{

GC.SuppressFinalize(this);

}

}

}

staticvoidMain(string[]args)

{

//tmpObj1没有手工释放资源,就等着GC来慢慢的释放它吧

Class1tmpObj1=newClass1();


//tmpObj2调用了Dispose方法,传说比等着GC来释放它效率要调一些

//个人认为是因为要逐个对象的查看其元数据,以确认是否实现了Dispose方法吧

//当然最重要的是我们可以自己确定释放的时间以节省内存,优化程序运行效率

Class1tmpObj2=newClass1();

((IDisposable)tmpObj2).Dispose();

}

}

}
21.P/Invoke是什么?答:在受控代码与非受控代码进行交互时会产生一个事务(transition),这通常发生在使用平台调用服务(PlatformInvocationServices),即P/Invoke如调用系统的API或与COM对象打交道,通过System.Runtime.InteropServices命名空间虽然使用Interop非常方便,但据估计每次调用事务都要执行10到40条指令,算起来开销也不少,所以我们要尽量少调用事务如果非用不可,建议本着一次调用执行多个动作,而不是多次调用每次只执行少量动作的原则22.StringBuilder和String的区别?答:String在进行运算时(如赋值、拼接等)会产生一个新的实例,而StringBuilder则不会。所以在大量字符串拼接或频繁对某一字符串进行操作时最好使用StringBuilder,不要使用String另外,对于String我们不得不多说几句:1.它是引用类型,在堆上分配内存2.运算时会产生一个新的实例3.String对象一旦生成不可改变(Immutable)3.定义相等运算符(==!=)是为了比较String对象(而不是引用)的值示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample22

{

classProgram

{

staticvoidMain(string[]args)

{

constintcycle=10000;


longvTickCount=Environment.TickCount;

Stringstr=null;

for(inti=0;i<cycle;i++)

str+=i.ToString();

Console.WriteLine("String:{0}MSEL",Environment.TickCount-vTickCount);


vTickCount=Environment.TickCount;

//看到这个变量名我就生气,奇怪为什么大家都使它呢?:)

StringBuildersb=newStringBuilder();

for(inti=0;i<cycle;i++)

sb.Append(i);

Console.WriteLine("StringBuilder:{0}MSEL",Environment.TickCount-vTickCount);


stringtmpStr1="A";

stringtmpStr2=tmpStr1;

Console.WriteLine(tmpStr1);

Console.WriteLine(tmpStr2);

//注意后面的输出结果,tmpStr1的值改变并未影响到tmpStr2的值

tmpStr1="B";

Console.WriteLine(tmpStr1);

Console.WriteLine(tmpStr2);


Console.ReadLine();

}

}

}
结果:String:375MSELStringBuilder:16MSELAABA23.explicit和implicit的含义?答:explicit和implicit属于转换运算符,如用这两者可以让我们自定义的类型支持相互交换explicti表示显式转换,如从A->B必须进行强制类型转换(B=(B)A)implicit表示隐式转换,如从B->A只需直接赋值(A=B)隐式转换可以让我们的代码看上去更漂亮、更简洁易懂,所以最好多使用implicit运算符。不过!如果对象本身在转换时会损失一些信息(如精度),那么我们只能使用explicit运算符,以便在编译期就能警告客户调用端示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample23

{

classProgram

{

//本例灵感来源于大话西游经典台词“神仙?妖怪?”--主要是我实在想不出什么好例子了

classImmortal

{

publicstringname;

publicImmortal(stringName)

{

name=Name;

}

publicstaticimplicitoperatorMonster(Immortalvalue)

{

returnnewMonster(value.name+":神仙变妖怪?偷偷下凡即可。。。");

}

}

classMonster

{

publicstringname;

publicMonster(stringName)

{

name=Name;

}

publicstaticexplicitoperatorImmortal(Monstervalue)

{

returnnewImmortal(value.name+":妖怪想当神仙?再去修炼五百年!");

}

}

staticvoidMain(string[]args)

{

ImmortaltmpImmortal=newImmortal("紫霞仙子");

//隐式转换

MonstertmpObj1=tmpImmortal;

Console.WriteLine(tmpObj1.name);


MonstertmpMonster=newMonster("孙悟空");

//显式转换

ImmortaltmpObj2=(Immortal)tmpMonster;

Console.WriteLine(tmpObj2.name);


Console.ReadLine();

}

}

}
结果:紫霞仙子:神仙变妖怪?偷偷下凡即可。。。孙悟空:妖怪想当神仙?再去修炼五百年!24.params有什么用?答:params关键字在方法成员的参数列表中使用,为该方法提供了参数个数可变的能力它在只能出现一次并且不能在其后再有参数定义,之前可以示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceConsoleApplication1

{

classApp

{

//第一个参数必须是整型,但后面的参数个数是可变的。

//而且由于定的是object数组,所有的数据类型都可以做为参数传入

publicstaticvoidUseParams(intid,paramsobject[]list)

{

Console.WriteLine(id);

for(inti=0;i<list.Length;i++)

{

Console.WriteLine(list[i]);

}

}


staticvoidMain()

{

//可变参数部分传入了三个参数,都是字符串类型

UseParams(1,"a","b","c");

//可变参数部分传入了四个参数,分别为字符串、整数、浮点数和双精度浮点数数组

UseParams(2,"d",100,33.33,newdouble[]{1.1,2.2});


Console.ReadLine();

}

}

}
结果:1abc2d10033.33System.Double[]25.什么是反射?答:反射,Reflection,通过它我们可以在运行时获得各种信息,如程序集、模块、类型、字段、属性、方法和事件通过对类型动态实例化后,还可以对其执行操作简单来说就是用string可以在runtime为所欲为的东西,实际上就是一个.netframework内建的万能工厂一般用于插件式框架程序和设计模式的实现,当然反射是一种手段可以充分发挥其能量来完成你想做的任何事情(前面好象见过一位高人用反射调用一个官方类库中未说明的函数。。。)示例:
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


namespaceExample25Lib

{

publicclassClass1

{

privatestringname;

privateintage;


//如果显式的声明了无参数构造函数,客户端只需要用程序集的CreateInstance即可实例化该类

//在此特意不实现,以便在客户调用端体现构造函数的反射实现

//publicClass1()

//{

//}

publicClass1(stringName,intAge)

{

name=Name;

age=Age;

}

publicvoidChangeName(stringNewName)

{

name=NewName;

}

publicvoidChangeAge(intNewAge)

{

age=NewAge;

}

publicoverridestringToString()

{

returnstring.Format("Name:{0},Age:{1}",name,age);

}

}

}
反射实例化对象并调用其方法,属性和事件的反射调用略去
usingSystem;

usingSystem.Collections.Generic;

usingSystem.Text;


//注意添加该反射的命名空间

usingSystem.Reflection;


namespaceExample25

{

classProgram

{

staticvoidMain(string[]args)

{

//加载程序集

AssemblytmpAss=Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory+"Example25Lib.dll");


//遍历程序集内所有的类型,并实例化

Type[]tmpTypes=tmpAss.GetTypes();

foreach(TypetmpTypeintmpTypes)

{

//获取第一个类型的构造函数信息

ConstructorInfo[]tmpConsInfos=tmpType.GetConstructors();

foreach(ConstructorInfotmpConsInfointmpConsInfos)

{

//为构造函数生成调用的参数集合

ParameterInfo[]tmpParamInfos=tmpConsInfo.GetParameters();

object[]tmpParams=newobject[tmpParamInfos.Length];

for(inti=0;i<tmpParamInfos.Length;i++)

{

tmpParams[i]=tmpAss.CreateInstance(tmpParamInfos[i].ParameterType.FullName);

if(tmpParamInfos[i].ParameterType.FullName=="System.String")

{

tmpParams[i]="Clark";

}

}


//实例化对象

objecttmpObj=tmpConsInfo.Invoke(tmpParams);

Console.WriteLine(tmpObj);


//获取所有方法并执行

foreach(MethodInfotmpMethodintmpType.GetMethods())

{

//为方法的调用创建参数集合

tmpParamInfos=tmpMethod.GetParameters();

tmpParams=newobject[tmpParamInfos.Length];

for(inti=0;i<tmpParamInfos.Length;i++)

{

tmpParams[i]=tmpAss.CreateInstance(tmpParamInfos[i].ParameterType.FullName);

if(tmpParamInfos[i].ParameterType.FullName=="System.String")

{

tmpParams[i]="ClarkZheng";

}

if(tmpParamInfos[i].ParameterType.FullName=="System.Int32")

{

tmpParams[i]=27;

}

}

tmpMethod.Invoke(tmpObj,tmpParams);

}


//调用完方法后再次打印对象,比较结果

Console.WriteLine(tmpObj);

}

}


Console.ReadLine();

}

}

}
结果:Name:Clark,Age:0Name:ClarkZheng,Age:27示例下载:http://files.cnblogs.com/reonlyrun/CSharp25QExample07.rar如果你认为还有哪些概念比较重要或容易混淆,可以在回复中提出,我会及时更新这篇随笔

一些话:To:watsonhua,谢谢你帮我改正了第4、11、14和19点的错误,并且让我对索引指示器的理解更全面!To:xiao,谢谢你关于“实例化”的详细解释,让这篇随笔中的措词更加精确!To:charleschen,谢谢你追问,让第1、第8的提法更恰当!To:装配脑袋,谢谢你提供internalprotected含义的正确答案!分类:
A..NET绿色通道:好文要顶关注我收藏该文与我联系


ClarkZheng关注-3粉丝-12+加关注2
0(请您对文章做出评价)«
博主前一篇:我也搞了个皮肤»
博主后一篇:访问过万,抓张图留个纪念Postedon2007-04-0513:44
ClarkZheng阅读(37948)
评论(162)
编辑
收藏

Feedback

评论共2页:上一页12

#44楼 回复 引用 查看 

2007-04-0610:50by
天龙之吻signed:)

#45楼 回复 引用 

2007-04-0610:52by
xiao[未注册用户]
@天下类实例化指的是调用类的静态构造器,一个类只能有一个无参无修饰符的静态构造器,它只能调用一次,用于初始化类中静态字段的状态对象实例化指的是调用通常意义上的构造器,它用于创建类的一个具体实例,这种构造器一个类中可以有多个重载版本代码如下:sealedclassXiao{privatestaticstringstr;//此为类构造器,可查看IL,其中编译为cctorstaticXiao(){str="qpppp";}//此为实例构造器,可查看IL,其中编译为ctorpublicXiao(){//当然实例构造器也能作用于静态字段str="eeeeeeeeeee";}}

#46楼 回复 引用 查看 

2007-04-0611:03by
lavy@ClarkZhengthis"限定被相似的名称隐藏的成员"是MSDN的解释:)你的解释,我懂了!~谢谢!

#47楼 回复 引用 查看 

2007-04-0611:11by
watsonhua@ClarkZheng类实例化和对象实例化xiao兄已经举例,不赘述。最初听到类实例化是在李建忠老师那里,我的理解也是建立在他翻译的applied.netframeworkprogramming,这里的问题书上都有讲道。谢谢Sephil兄,帮我复习一下久违的c++,怀念...关于第19题,我说的语气没表述出来。我的语气是带一点反话的。我的意思是using只在它所在的名空间内有用,换而言之,如果单个文件有多个嵌套名空间,using只在它所直接隶属的名空间内可用。当然,如果它没有放在名空间内的话,作用域就是整个文件了。

#48楼[楼主] 回复 引用 查看 

2007-04-0611:17by
ClarkZheng@lavyMSDN。。。不是忘看了就是看了忘记了,谢谢你的提醒

#49楼 回复 引用 查看 

2007-04-0611:22by
watsonhua@ClarkZhengclark兄,关于称呼抱歉抱歉。:)我有的地方是直接粘的。这篇post貌似verypopular。clark兄可以整理一下,再置顶于个人主页上,长期讨论...当然欢迎clark兄就其他技术问题作讨论。

#50楼[楼主] 回复 引用 查看 

2007-04-0612:17by
ClarkZheng@xiao感谢,正文已修改

#51楼[楼主] 回复 引用 查看 

2007-04-0613:07by
ClarkZheng@watsonhua改了第20题的提法改了第22题的内容和示例,增加了对String的解释示例下载已更新

#52楼[楼主] 回复 引用 查看 

2007-04-0613:38by
ClarkZheng@charleschen你的追问中关于静态函数与非静态函数的问题,我已在第1题中做了修改关于属性访问器的用处,也已在第8题中做了修改其它两个问题我还不能理解题面的意思,期待着你的回复

#53楼 回复 引用 查看 

2007-04-0613:49by
yunhuasheng楼主挺认真的!!;)

#54楼 回复 引用 查看 

2007-04-0614:26by
想飞的猪支持LZ,让俺重温了一遍....

#55楼 回复 引用 查看 

2007-04-0614:31by
装配脑袋internalprotected的语意应该是internal|protected,而不是internal&&protected。相信不少没有用过的老手也不知道。

#56楼 回复 引用 查看 

2007-04-0614:58by
阿齐类实例化指的是把一个类实例化的过程,即把类的静态成员初始化的过程;而对象实例化,就是构造一个该类的实例,然后初始化该实例的实例成员

#57楼 回复 引用 查看 

2007-04-0615:38by
装配脑袋我尝试从这些语法的最直接效果作答这些问题:1.静态成员和非静态成员的区别?静态字段为类型所有实例所共享,而实例字段则为每个对象实例所专有。静态方法不能访问实例成员,而且无需对象实例即可调用。静态构造方法仅在AppDomain初始化类型时执行一次,而且具有AppDomain级别的线程安全性。而实例构造函数则在对象初始化时执行。2.const和staticreadonly区别?const的字段在编译时会将其字面值直接写到引用它的地方,而且必须用字面常量在编译时初始化。staticreadonly可以在运行时初始化。3.extern是什么意思?extern表示函数体在其他地方定义。因此这样的函数只有声明没有定义。4.abstract是什么意思?abstract用于类表示该类不能实例化,用于成员表示它是子类必须重写的过程。5.internal修饰符起什么作用?表示该类型或成员仅在同一程序集中可被其他类型所访问。6.sealed修饰符是干什么的?表示该类型不能被继承,或该(虚拟)成员不能再被子类所重写。7.override和overload的区别?我其实不太愿意讨论两个范畴的东西有什么区别,因为硬要说区别就等于把两者的定义分别写一遍。8.什么是索引指示器?是一种带参数的属性,通过obj[params]的语法进行调用。9.new修饰符是起什么作用?它指示该过程在MethodTable中开启一个新的“slot”,从而断开基类同名虚成员对其的调用关系。10.this关键字的含义?在引用类型中,它表示所调用的对象实例的引用。在值类型中,它是一个符号,将this上的各种操作转嫁给所调用的值类型对象实例。11.可以使用抽象函数重写基类中的虚函数吗?当且仅当基类中相应的函数也为抽象函数。12.密封类可以有虚函数吗?可以,甚至有些封闭类带有新声明的虚函数(所有委托类型)。13.什么是属性访问器?使用访问变量的语法访问属性时,实际被调用的过程。14.abstract可以和virtual一起使用吗?可以和override一起使用吗?可以和override一起使用,当且仅当重写一个父类的虚成员时。15.接口可以包含哪些成员?接口仅可以包含过程,即方法、属性、事件。而且只能定义过程的签名,不能定义他们的方法体、访问级别等。16.类和结构的区别?类为引用类型结构为值类型结构没有终结器结构不能继承结构必须有一个默认构造方法,且不能自定义无参数构造方法结构的成员不能在声明时赋值结构默认有专门实现的不同==和GetHashCode方法17.接口的多继承会带来哪些问题?任何特性均在正确使用时发挥正确的效果。因此我们应该看到多继承的优势。18.抽象类和接口的区别?抽象类可以有构造方法抽象类可以有终结器抽象类可以有字段抽象类可以有静态成员抽象类可以没有抽象方法抽象类可以有实例方法一个类只能继承一个抽象类,而可以实现多个接口抽象类的成员可以带有访问性级别抽象类之间不能多继承,接口则可以抽象类可以有静态构造方法19.别名指示符是什么?命名空间别名限定符::专名用来限定所用的名字要从指定别名所表示的范围开始搜索。别名为extern或using关键字定义的命名空间或根别名。20.如何手工释放资源?没有手工释放托管资源的功能,尽管有些看其来是在释放其实不是。只能手工释放非托管资源。21.P/Invoke是什么?通过Runtime进行封送(Marshal),调用公开方式的非托管代码,包括DLL和COM等。22.StringBuilder和String的区别?基本上是写出两者定义的那种“区别”23.explicit和implicit的含义?这是两个类型转换运算符,explicit运算符需要明确指出目标类型名,而implicit会根据上下文自动调用。24.params有什么用?一种提供参数数目可变的参数传递方法。任意数目的参数可转化为一个数组。25.什么是反射?通过程序基内的元数据和Runtime的支持在运行时读取程序集、模块、类型和成员的信息,以及在运行时通过这种途径访问对象的成员或执行对象的方法。甚至动态改变类型和方法的组成。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: