C#基础概念二十五问
2007-07-13 16:32
435 查看
注:本文部份资料来自网络,如有侵权,请与我联系,我会在第一时间声明引用或将其删除!
当初学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();
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
结果:
Class1'sstaticStr:Class
tmpObj1'snotstaticStr:tmpObj1
tmpObj2'snotstaticStr:tmpObj2
2.const和staticreadonly区别?
答:
const
用const修饰符声明的成员叫常量,是在编译期初始化并嵌入到客户端程序
staticreadonly
用staticreadonly修饰符声明的成员依然是变量,只不过具有和常量类似的使用方法:通过类进行访问、初始化后不可以修改。但与常量不同的是这种变量是在运行期初始化
示例:
测试类:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
namespaceExample02Lib
{
publicclassClass1
{
publicconstStringstrConst="Const";
publicstaticreadonlyStringstrStaticReadonly="StaticReadonly";
//publicconstStringstrConst="ConstChanged";
//publicstaticreadonlyStringstrStaticReadonly="StaticReadonlyChanged";
}
}
客户端代码:
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
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();
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
结果:
strConst:Const
strStaticReadonly:StaticReadonly
修改后的示例:
测试类:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
namespaceExample02Lib
{
publicclassClass1
{
//publicconstStringstrConst="Const";
//publicstaticreadonlyStringstrStaticReadonly="StaticReadonly";
publicconstStringstrConst="ConstChanged";
publicstaticreadonlyStringstrStaticReadonly="StaticReadonlyChanged";
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
结果
strConst:Const
strStaticReadonly:StaticReadonlyChanged
3.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);
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
结果:
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();
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
结果:
1234567
7
6
5
4
3
2
1
5.internal修饰符起什么作用?
答:
internal修饰符可以用于类型或成员,使用该修饰符声明的类型或成员只能在同一程集内访问
接口的成员不能使用internal修饰符
值得注意的是,如果为internal成员加上了protected修饰符,这时的访问级别为internal或protected。只是看字面意思容易弄错,许多人认为internalprotected应该是“只有同一个程序集中的子类可以访问”,但其实它表示“同一个程序集中的所有类,以及所有程序集中的子类都可以访问”
示例
Example05Lib项目的Class1
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
namespaceExample05Lib
{
publicclassClass1
{
internalStringstrInternal=null;
publicStringstrPublic;
internalprotectedStringstrInternalProtected=null;
}
}
.csharpcode,.csharpcodepre{ font-size:small; color:black; font-family:consolas,"CourierNew",courier,monospace; background-color:#ffffff; /*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{ background-color:#f4f4f4; width:100%; margin:0em;}.csharpcode.lnum{color:#606060;}
结果
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();
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
结果:
类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表示重载,用于同一个类中同名方法不同参数(包括类型不同或个数不同)的实现
示例:
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();
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
结果:
BaseClass.F
DeriveClass.F
AddforInt:3
Addforint:3.3
8.什么是索引指示器?
答:
实现索引指示器(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();
}
}
}
.csharpcode,.csharpcodepre{ font-size:small; color:black; font-family:consolas,"CourierNew",courier,monospace; background-color:#ffffff; /*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{ background-color:#f4f4f4; width:100%; margin:0em;}.csharpcode.lnum{color:#606060;}
结果:
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
Todayiscloudy!
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();
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
结果:
3.1415
3.1415926
10.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();
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
结果:
37.5Celsius=99.5Fahrenheit
Havethis.:4375MSEL
Don'thavethis.:4406MSEL
11.可以使用抽象函数重写基类中的虚函数吗?
答:
可以
需使用new修饰符显式声明,表示隐藏了基类中该函数的实现
或增加override修饰符,表示抽象重写了基类中该函数的实现
示例:
classBaseClass
{
publicvirtualvoidF()
{
Console.WriteLine("BaseClass.F");
}
}
abstractclassDeriveClass1:BaseClass
{
publicabstractnewvoidF();
}
//感谢watsonhua(http://huazhihao.cnblogs.com/)的指点
//是他提醒了我还可以用这种方法抽象重写基类的虚方法
abstractclassDeriveClass2:BaseClass
{
publicabstractoverridevoidF();
}
.csharpcode,.csharpcodepre{ font-size:small; color:black; font-family:consolas,"CourierNew",courier,monospace; background-color:#ffffff; /*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{ background-color:#f4f4f4; width:100%; margin:0em;}.csharpcode.lnum{color:#606060;}
12.密封类可以有虚函数吗?
答:
可以,基类中的虚函数将隐式的转化为非虚函数,但密封类本身不能再增加新的虚函数
示例:
classBaseClass
{
publicvirtualvoidF()
{
Console.WriteLine("BaseClass.F");
}
}
sealedclassDeriveClass:BaseClass
{
//基类中的虚函数F被隐式的转化为非虚函数
//密封类中不能再声明新的虚函数G
//publicvirtualvoidG()
//{
//Console.WriteLine("DeriveClass.G");
//}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
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)
{
}
}
}
.csharpcode,.csharpcodepre{ font-size:small; color:black; font-family:consolas,"CourierNew",courier,monospace; background-color:#ffffff; /*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{ background-color:#f4f4f4; width:100%; margin:0em;}.csharpcode.lnum{color:#606060;}
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();
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
结果:
X:1,Y:2,Z:3
X:4,Y:5,Z:6
X:7,Y:8,Z:9
X:7,Y:8,Z:9
17.接口的多继承会带来哪些问题?
答:
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();
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
结果:
Countproperty:100
Countfunction:10000
18.抽象类和接口的区别?
答:
抽象类(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";
}
}
}
.csharpcode,.csharpcodepre{ font-size:small; color:black; font-family:consolas,"CourierNew",courier,monospace; background-color:#ffffff; /*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{ background-color:#f4f4f4; width:100%; margin:0em;}.csharpcode.lnum{color:#606060;}
Class2.cs:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
namespacecom.nblogs.reonlyrun.CSharp25QExample.Example19.Lib02
{
classClass1
{
publicoverridestringToString()
{
return"com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib02'sClass1";
}
}
}
.csharpcode,.csharpcodepre{ font-size:small; color:black; font-family:consolas,"CourierNew",courier,monospace; background-color:#ffffff; /*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{ background-color:#f4f4f4; width:100%; margin:0em;}.csharpcode.lnum{color:#606060;}
主单元(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();
}
}
}
}
.csharpcode,.csharpcodepre{ font-size:small; color:black; font-family:consolas,"CourierNew",courier,monospace; background-color:#ffffff; /*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{ background-color:#f4f4f4; width:100%; margin:0em;}.csharpcode.lnum{color:#606060;}
结果:
com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib01'sClass1
com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib02'sClass1
com.nblogs.reonlyrun.CSharp25QExample.Example19.Lib01'sClass1
20.如何手工释放资源?
答:
.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();
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
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();
}
}
}
.csharpcode,.csharpcodepre{ font-size:small; color:black; font-family:consolas,"CourierNew",courier,monospace; background-color:#ffffff; /*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{ background-color:#f4f4f4; width:100%; margin:0em;}.csharpcode.lnum{color:#606060;}
结果:
String:375MSEL
StringBuilder:16MSEL
A
A
B
A
23.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();
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
结果:
紫霞仙子:神仙变妖怪?偷偷下凡即可。。。
孙悟空:妖怪想当神仙?再去修炼五百年!
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();
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
结果:
1
a
b
c
2
d
100
33.33
System.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);
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
反射实例化对象并调用其方法,属性和事件的反射调用略去
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();
}
}
}
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}
结果:
Name:Clark,Age:0
Name:ClarkZheng,Age:27
示例下载:
如果你认为还有哪些概念比较重要或容易混淆,可以在回复中提出,我会及时更新这篇随笔
一些话:
To:
To:xiao,谢谢你关于“实例化”的详细解释,让这篇随笔中的措词更加精确!
To:
To:
本贴子以“现状”提供且没有任何担保,同时也没有授予任何权利
Thispostingisprovided"ASIS"withnowarranties,andconfersnorights.
postedon2007-04-0513:44
//Sys.WebForms.PageRequestManager._initialize('AjaxHolder$scriptmanager1',document.getElementById('Form1'));
Sys.WebForms.PageRequestManager.getInstance()._updateControls(['tAjaxHolder$UpdatePanel1'],[],[],90);
//]]>
评论:
[align=left]很重要的基础概念,每个C#的开发人员都应该弄明白先,不过......
[align=left]还有,我很喜欢用sb,呵呵,个人作品,公司的东西还是严格按照公司规范来
[align=left]C#基础概念二十五问注:本文部份资料来自网络,如有侵权,请与我联系,我会在第一时间声明引用或将其删除!
[align=left]我先自测了一遍,按100分算只能打80+,惭愧……
[align=left]什么时候做个VisualBasic的测验?
这些东西基本都看不懂,除了一些CLR共通的……
[align=left]C#基础概念二十五问注:本文部份资料来自网络,如有侵权,请与我联系,我会在第一时间声明引用或将其删除!
[align=left]还有"unbox"和"box"这两个概念,很容易被人忽略.
[align=left]@亚历山大同志
:)
[align=left]@非我
不要以偶的参考答案评分,里面可能错误多多呢
[align=left]@随风流月
我不会推出VB.NET的版本了,如果感兴趣,你可以写一个?
[align=left]@fly_bluewolf
关于装箱和拆箱。。。我再看看吧,个人感觉真的没什么好写的
[align=left]不错,不错。收藏了。
[align=left]@随风流月
如果想要VB的话……
1.静态变量和非静态变量的区别?
2.Const和SharedReadonly区别?
3.Declare是什么意思?
4.MustInherit/MustOverride是什么意思?
5.Friend修饰符起什么作用?
6.NotInheritable/NotOverridable修饰符是干什么的?
7.Overrides和Overloads的区别?
8.什么是默认属性?
9.Shadows修饰符是起什么作用?
10.Me关键字的含义?
11.可以使用抽象函数重写基类中的虚函数吗?(答案与C#不同)
12.不可继承类可以有虚函数吗?
13.如果基类中的虚属性只有一个属性访问器,那么继承类重写该属性后可以有几个属性访问器?如果基类中有Get和Set两个呢?(答案与C#不同)
14.MustOverride可以和Overridable一起使用吗?可以和Overrides一起使用吗?
15.接口可以包含哪些成员?
16.类和结构体的区别?
17.接口的多继承会带来哪些问题?
18.抽象类和接口的区别?
19.Global关键字是什么?
20.如何释放非托管资源?
21.P/Invoke是什么?
22.StringBuilder和String的区别?
23.Narrowing和Widening的含义?
24.ParamArray有什么用?
25.什么是反射?
[align=left]不错的文章,
追问:
静态方法和普通方法的区别?
显示接口与隐式接口的区别?
为什么要使用Get/Set而不是直接操作类成员?
GC释放资源有哪几种方法?
[align=left]不错的文章
以前看着这些名词都不知道什么意思,其实自已一直在用,呵呵
[align=left]我来回答@charleschen
用Get/Set因为是可以独立设置只写或只能写吧
[align=left]@装配脑袋
真厉害…………人工翻译阿
[align=left]汗...我连及格都不行.
基础太差.
[align=left]C#基础概念二十五问
[align=left]override表示重载,用于同一个类中同名方法不同参数(包括类型不同或个数不同)的实现
------------
这里的override应为overload
[align=left]@装配脑袋
汗,真是C#.NET和VB.NET同时精通的人才呀。。。
[align=left]@没剑
感谢你的细心,正文已修正!
[align=left]是一次做复习的机会
[align=left]呵呵,不错,学了不少,用C#写了一段时间的程序了,但是你里面的东西我还有好多没用过,继续研究C#:)
[align=left]嗯,不错,基本上都清楚这些概念.
这一年没白混,呵呵
[align=left]很好很好,图文并茂。11、12还是有点儿迷惑,主要是new,virtual,abstract,sealed这几个概念容易混淆~
[align=left]@charleschen
1.静态方法和普通方法的区别?
答:静态方法是通过类型调用,在对象未实例化前就可以使用,因此在其实现中不能使用非静态的成员(如:非静态的变量、非静态的方法)
2.显式接口和隐式接口?
答:你是指显式接口成员吗?
3.为什么要使用Get/Set而不是直接操作类成员?
答:get/set是C#为实现属性而设计的访问器,提出这概念主要是为了实现OO中的“封装”。根据该思想,字段最好设为private,一个精巧的类最好不要直接把字段设为公有提供给客户调用端直接访问
另外,在设计模式中提到如果你有一个public的方法,那么最好再设计一个protected的虚函数以方便子类中对该方法进行扩展,属性访问器的设计可以让我们完成该模式,不是吗?
4.GC释放资源有哪几种方法?
答:我不太理解这个题目,你想问什么呢?GC释放资源的流程?
以上回答还希望大家多多提意见,确认比较全面并且无误后,我会加到随笔正文
[align=left]@BolerGuo
new比较特殊,这个关键字既是修饰符又是运算符,做为运算符时用于实例化类型,做为修饰符时,请参照第9点的答案
sealed起密封作用,意味该类型或成员不会再有继承,所以虚函数会自动变成非虚函数。非函数在CLR查找其实现时速度会快一点,这也是我提到sealed有时可以提高程序运行效率的原因
virtual没什么好说的吧,虚函数OO中多态的基础呀
abstract请参照第4点的答案
[align=left]大体上正确,但不精确,建议参照框架设计,说的深入一些。
还有一些个人看法,不算纠正,只算补充。
索引指示器实质是含参属性,也就是说,它的参数不限于int,把它比作数组未免...
可以使用抽象函数重写基类中的虚函数吗?这个问题我觉得楼主理解有一些问题,不需要加new修饰。所谓abstract,new,virtual只是虚函数的版本元数据的几个向量而已,当然你也可以把那个函数理解为new的,但这个new不是atttribute,而是函数的元数据里的一个标志位。所以你即便不加new修饰,编译器也不会报warning。
其他还有几个地方用词也值得斟酌。
算然已经很好了,但我们应该有更高的目标。
[align=left]@ClarkZheng
quote"静态方法是通过类型调用,在类未实例时就可以使用"
类事例化前是不能使用的,我想你的意思应该是对象实例化前吧。
[align=left]关于THIS的用法
限定被相似的名称隐藏的成员,例如:
publicEmployee(stringname,stringalias)
{
this.name=name;
this.alias=alias;
}
我有疑问:
什么是被相似的名称隐藏的成员??
相似指什么?什么时候会出现隐藏?
[align=left]真强,我一直想做这么一件事情,可一直都没有做,谢谢你为我提供了这么好的快速复习小册子。
[align=left]@watsonhua
谢谢你关于索引指示器的讲解,正文我已修订,并将示例更新了
关于”使用抽象函数重写基类中的虚函数“,如果不加new修饰符编译时会给警告。你可以把Example11示例中,DeriveClass的F函数去掉new修饰符,然后编译试一下
关于类实例化和对象实例化,我还是听从了你的意见,上面的回复也已改正
ps:非常希望你能帮其它几处用词不当的地方指出,我们就是应该有更高的目标!
[align=left]@lavy
我的原文是”this保留字一般用于限定同名的隐藏成员、将对象本身做为参数、声明索引访问器、判断传入参数的对象是否为本身“,并没有”限定被相似的名称隐藏的成员“
针对第一个用处,我再详细说明一下:
//正确,由于类本身的变量c和构造函数的参数c重名,这时就需要使用this显式指出是将参数的c赋值给类的变量c
privatedoublec;
publicClass1(doublec)
{
//限定同名的隐藏成员
this.c=c;
}
//错误,会给出一个警告:对同一变量赋值。编译器没有看到我们实际想赋值的成员变量c,所以我们称它”隐藏“了
privatedoublec;
publicClass1(doublec)
{
c=c;
}
[align=left]@HuacnLee
@Aの扬凡
@戴眼镜的太阳
@郭志坚
@BolerGuo
@小鬼
谢谢你们的支持!
[align=left]@ClarkZheng
能够严肃的讨论技术实在是人生快事。
11.可以使用抽象函数重写基类中的虚函数吗?
不知这个重写是override还是overload,还是重名函数就算。
若是override,当然是可以的,但不要忘了加override修饰,编译器推倒不出的。
若是overload,当然是可以的,但凡不写new,是要抛warning的。
[align=left]@watsonhua
本文把overload译为重载,把override译为重写,请参见第7点答案
我的问题是抽象函数(abstract)是否可以重写(override)基类中的虚函数,而abstract和override修饰符是不可以同时使用的,会产生二义性,所以谈不上把override忘了
出本题的目的缘于我在引用一个类库时发现其中一个基类设计不合理,仅仅设为虚函数(virtual)并没有设成抽象函数(abstract)--具体情况这里略去。所以我想把它改成抽象函数,以便使用我的类库的开发人员如果继承该类必须强制性的重写(override)该函数。So,我碰到了一个警告,通过增加new修饰符解决了该问题。
[align=left]usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
namespaceExample11
{
classBaseClass
{
publicvirtualvoidF()
{
Console.WriteLine("BaseClass.F");
}
}
abstractclassDeriveClass:BaseClass
{
publicabstractoverridevoidF();
}
classProgram
{
staticvoidMain(string[]args)
{
}
}
}
试一下吧
[align=left]C#基础概念二十五问注:本文部份资料来自网络,如有侵权,请与我联系,我会在第一时间声明引用或将其删除!
[align=left]@watsonhua
请原谅我的浅见拙识,原文中已改正了所有关于abstract和override关系的地方,下载文件也已替换。
在此只好是”再次“感谢你了:)
[align=left]再补充一点我的理解吧,我觉得@ClarkZheng的这篇文章写得相当用心,也足见@ClarkZheng做人,做程序员的责任心。
1.静态变量和非静态变量的区别?
静态变量在类实例化时创建,非静态变量在对象实例化时创建,
即静态变量在类构造函数调用的时候创建,非静态变量在对象构造函数调用时创建。
@ClarkZheng的sample里用的是一个可以使用一条语句就可以完成创建的静态变量,所以掩盖了这个问题。
@ClarkZheng说静态变量是类被装载时创建的,我不清楚他说的类被装载是什么时候。
assembly被装载到appdomain时是不会的,类在被使用前的一瞬,会调用静态构造函数完成类的实例化。
2.const和staticreadonly区别?
完全正确,不再补充。
3.extern是什么意思?
不知道存不存在其他功能,如cpp里的extern"C"。
4.abstract是什么意思?
6.sealed修饰符是干什么的?
7.override和overload的区别?
5.internal修饰符起什么作用?
没什么好说的,搞清楚和protected的区别就可以了,当然internalprotected的语意是internal&&protected
8.什么是索引指示器?
已经补充过了。
9.new修饰符是起什么作用?
不是很喜欢这种关键词重载。
10.this关键字的含义?
this很好理解。java里可以使用对象访问类属属性,相比起来,更喜欢c#里的设计。
11.可以使用抽象函数重写基类中的虚函数吗?
讨论过,答案是可以。absolutely。
12.密封类可以有虚函数吗?
问题是"密封类可以有虚函数吗",结果回答的是"密封类的父类可以有虚函数"。照clark兄的意思是密封类可以包含虚的成员方法,我觉这个问题问得没有意义。sealed的类最起码是object的派生类,object有ToString(),答案是明显的。
13.如果基类中的虚属性只有一个属性访问器,那么继承类重写该属性后可以有几个属性访问器?如果基类中有get和set两个呢?
属性的实质是方法,若有一个virtual方法自然可以override。所以,派生类可以override父类的get,只要父类有get,set同理。
quote"如果基类中的虚属性只有一个属性访问器,那么继承类重写该属性后也应只有一个。如果基类中有get和set两个属性访问器,那么继承类中可以只有一个也可以同时有两个属性访问器".@clarkzhang表述的不够准确。
14.abstract可以和virtual一起使用吗?可以和override一起使用吗?
参见
15.接口可以包含哪些成员?
interface可以包含所有的可以被继承且可以被派生类实例化的成员,并且interface自己不能被实例化。interface遵照此原则。因为比较宽松,所以才会出现范型接口这种病态的但又不可或缺的东西。
16.类和结构的区别?
class和struct的区别再不交互时是很容易辨认的,当class里有struct,struct里又有class就很热闹了。
class和struct的比较重要的问题是,eqauls方法和原型方法的逻辑。当class里有struct,或struct里有class,不能靠object.equals了,既要考虑怎样重用父类里的equals,又要考虑便于派生类拓展,怎么样才算equals,非常复杂。而原型方法就是(iclonable).clone,深的还是浅的,多深多浅,要结合equals方法考虑。当然不要忘了重载==。
再提一句,stuctA{}的实质是classA:System.ValueType{},但是这个System.ValueType是一个class。想来有趣,有一点像夏娃,是用亚当的一根肋骨做程的。
17.接口的多继承会带来哪些问题?
这个问题改成"多接口的继承会带来哪些问题?"比较好。@clarkzhang解释的还是正确的,只要不造成二义性,可以不用显示转型。
18.抽象类和接口的区别?
抽象类的原则是存在不可以被实例化的方法的类,接口的原则前面提到过。
19.别名指示符是什么?
using的一个作用,作用范围是using的所在名空间内,并且在using的语句后面。并非只在一个单元文件内起作用。
当然别名像多接口继承一样,也是要"去二义化"的。
20.如何释放非托管资源?
问题改成"如何强制释放资源"比较好。
不建议强制释放资源,除非逼不得已。gc,人间凶器阿...
21.P/Invoke是什么?
没怎么用过,不敢发表意见。
22.StringBuilder和String的区别?
区别还是很大的。倒是string和值类型的近似行为更值得讲解。
另外cycle=100000还是少一到两个零比较好,意思一下就好了么。
23.explicit和implicit的含义?
例子很形象。Explicit存在的意义相当于c++里的拷贝构造函数。
24.params有什么用?
publicstaticvoidUseParams(intid,paramsobject[]list)
{
Console.WriteLine(id);
for(inti=0;i<list.Length;i++)
{
Console.WriteLine(list[i]);
}
}
...
UseParams(2,"d",100,33.33,newdouble[]{1.1,2.2});
最后会输出System.Double[]是因为cw((object)o)的逻辑是cw(o.ToString()),而(object).ToString()默认输出(object).GetType().ToString()
params的实质是一个ParamArrayAttribute,因为c#不支持默认参数,所以不定秩的形参只有靠它来。对比c++,可读性无疑增强了。c++里好像也有vlist...的东西,名称记不得了,好像还要加一个头文件,用起来非常麻烦,还不可以做编译期的类型检查。不过c#里没有默认参数,如果要和vb,c++交互的话,要用Type.Missing来补空位,不可以用null。曾经调用office的com,一个函数里用了40个Type.Missing,无语啊。
25.什么是反射?
用string可以在runtime为所欲为的东西。实际上就是一个.netframework内建的万能工厂。
结:
这里的大部分问题的讨论结果已经不能算是基础概念了,而是底层概念了,希望能对大家有帮助。如果有不足的地方还请大家补充,如果我有理解错误的地方还请多指教。
[align=left]@watsonhua
关于”类实例化“和”对象实例化“,有没有更好的说法?我一直表达不清楚两个概念,请教一下这两个词的”完美“程度如何?
关于extern,本文只讨论C#语言范畴,前面有说
internalprotected的语意是internal&&protected,perfect!
关于第12题,密封类可以有虚函数吗?你的这个object举得非常恰当,我也感觉这个问题没有意义了。。。
索引指示器的实质是带参属性、属性的实质是方法,佩服你的见解
偶再补充一句:事件的实质是属性
关于第19题,我的试验结果是只在一个单元文件内起作用
关于第20题我觉得你的提法比我强
关于cycle=100000,不知道是不是偶的机器太过强悍,少一个零后,输出结果如下:(我只是想让第二个不是0呀,另外特别声明一个常量做为循环次数就是方便各位朋友修改,不是我编码风格有多好。。。)
String:343MSEL
StringBuilder:0MSEL
ps:偶的名字是ClarkZheng,不是@ClarkZheng。:)
[align=left]Attribute重要不?
[align=left]不错,很详细~