??xml version="1.0" encoding="utf-8" standalone="yes"?> 问题描述 在多U程环境下,单例对象的同步问题主要体现在两个斚wQ单例对象的初始化和单例对象的属性更新?/p> 本文描述的方法有如下假设Q?/p> 1. 单例对象的属性(或成员变量)(j)的获取,是通过单例对象的初始化实现的。也是_(d)在单例对象初始化Ӟ?x)从文g或数据库中读取最新的配置信息?/p> 2. 其他对象不能直接改变单例对象的属性,单例对象属性的变化来源于配|文件或配置数据库数据的变化?/p> 1.1 单例对象的初始化 首先Q讨Z下单例对象的初始化同步。单例模式的通常处理方式是,在对象中有一个静态成员变量,其类型就是单例类型本w;如果该变量ؓ(f)nullQ则创徏该单例类型的对象Qƈ该变量指向q个对象Q如果该变量不ؓ(f)nullQ则直接使用该变量?/p> 其过E如下面代码所C:(x) 1.2 单例对象的属性更?/strong> 通常Qؓ(f)了实现配|信息的实时更新Q会(x)有一个线E不停检配|文件或配置数据库的内容Q一旦发现变化,更新到单例对象的属性中。在更新q些?息的时候,很可能还?x)有其他U程正在dq些信息Q造成意想不到的后果。还是以通过单例对象属性停止线E服务ؓ(f)例,如果更新属性时d不同步,可能讉K?属性时q个属性正好ؓ(f)I(nullQ,E序׃(x)抛出异常?/p> 解决Ҏ(gu) 2.1 单例对象的初始化同步 对于初始化的同步Q可以通过如下代码所采用的方式解冟?/p> q种处理方式虽然引入了同步代码,但是因ؓ(f)q段同步代码只会(x)在最开始的时候执行一ơ或多次Q所以对整个pȝ的性能不会(x)有媄(jing)响?/p>2.2 单例对象的属性更新同?/strong> Z解决W?个问题,有两U方法:(x) 1Q参照读?写者的处理方式 讄一个读计数器,每次d配置信息前,计数器?Q读完后计数器?.只有在读计数器ؓ(f)0Ӟ才能更新数据Q同时要d所有读属性的调用。代码如下?/p> 2Q采?影子实例"的办?/strong> 具体_(d)是在更新属性时Q直接生成另一个单例对象实例,q个新生成的单例对象实例从数据库或文g中读取最新的配置信息Q然后将q些配置信息直接赋值给旧单例对象的属性。如下面代码所C?/p> 注意Q在更新Ҏ(gu)中,通过生成新的GlobalConfig的实例,从文件或数据库中得到最新配|信息,q存攑ֈproperties属性中?/p> 上面两个Ҏ(gu)比较hQ第二个Ҏ(gu)更好Q首先,~程更简单;其次Q没有那么多的同步操作,Ҏ(gu)能的媄(jing)响也不大?/p>public class GlobalConfig {
private static GlobalConfig instance = null;
private Vector properties = null;
private GlobalConfig() {
//Load configuration information from DB or file
//Set values for properties
}
public static GlobalConfig getInstance() {
if (instance == null) {
instance = new GlobalConfig();
}
return instance;
}
public Vector getProperties() {
return properties;
}
}
q种处理方式在单U程的模式下可以很好的运行;但是在多U程模式下,可能产生问题。如果第一个线E发现成员变量ؓ(f)nullQ准备创建对象;q是W二 个线E同时也发现成员变量为nullQ也?x)创建新对象。这׃(x)造成在一个JVM中有多个单例cd的实例。如果这个单例类型的成员变量在运行过E中变化Q会(x) 造成多个单例cd实例的不一_(d)产生一些很奇怪的现象。例如,某服务进E通过(g)查单例对象的某个属性来停止多个U程服务Q如果存在多个单例对象的实例Q就 ?x)造成部分U程服务停止Q部分线E服务不能停止的情况?
public class GlobalConfig {
private static GlobalConfig instance = null;
private Vector properties = null;
private GlobalConfig() {
//Load configuration information from DB or file
//Set values for properties
}
private static synchronized void syncInit() {
if (instance == null) {
instance = new GlobalConfig();
}
}
public static GlobalConfig getInstance() {
if (instance == null) {
syncInit();
}
return instance;
}
public Vector getProperties() {
return properties;
}
} public class GlobalConfig {
private static GlobalConfig instance;
private Vector properties = null;
private boolean isUpdating = false;
private int readCount = 0;
private GlobalConfig() {
//Load configuration information from DB or file
//Set values for properties
}
private static synchronized void syncInit() {
if (instance == null) {
instance = new GlobalConfig();
}
}
public static GlobalConfig getInstance() {
if (instance==null) {
syncInit();
}
return instance;
}
public synchronized void update(String p_data) {
syncUpdateIn();
//Update properties
}
private synchronized void syncUpdateIn() {
while (readCount > 0) {
try {
wait();
} catch (Exception e) {
}
}
}
private synchronized void syncReadIn() {
readCount++;
}
private synchronized void syncReadOut() {
readCount--;
notifyAll();
}
public Vector getProperties() {
syncReadIn();
//Process data
syncReadOut();
return properties;
}
} public class GlobalConfig {
private static GlobalConfig instance = null;
private Vector properties = null;
private GlobalConfig() {
//Load configuration information from DB or file
//Set values for properties
}
private static synchronized void syncInit() {
if (instance = null) {
instance = new GlobalConfig();
}
}
public static GlobalConfig getInstance() {
if (instance = null) {
syncInit();
}
return instance;
}
public Vector getProperties() {
return properties;
}
public void updateProperties() {
//Load updated configuration information by new a GlobalConfig object
GlobalConfig shadow = new GlobalConfig();
properties = shadow.getProperties();
}
}
]]>
我想?/span>
JSP
面中向数据库表
staff
插入一条记录:(x)面能蟩转到
addStaffSuccess.jsp
Q?/span>
h面Ӟd的记录能在页面中昄出来Q我q以为成功了Q于是我想通过企业理器打开数据库表看是不是数据库表更新了,发现数据库表不显C数据,我插入的是第二条记录Q于是我让它q回首行Q首行正常显C。我用查询分析器查询
Staff
表的数据Q一直是“正在执行批查询状?/span>
……?/span>
”说明我Ҏ(gu)据库的操作媄(jing)响到了数据库Q但是出了我不知道的异常或错误,但是又不报错Q我不知道怎么L原因。忘了,当我停止
tomcat
Ӟ数据表正常显C,但插入的数据没有昄在表里,说明没有插入。难道是我没停止
tomcat
前,数据库一直在更新么?
我用的环境是Q?/span>
eclipse3.2+Myeclipse5.5 + MS SQL server2000+ tomcat5.5
我添加了
struts
?/span>
hibernate
支持Q各个持久化cd
DAO
c?/span>
,
是通过映射?/span>
hibernate
自动生成的?/span>
Action
代码是我写的Q不道有错没,h正?/span>
d后,q回原页面,昄了添加的信息Q(但数据库没更斎ͼ郁闷Q?/b>Q?/span>
数据库:(x)
staff (staffid,// 主键
staffname,//not null
password,//not null
departmentname;// 允许I?/span>
sex;// 允许I?/span>
email;// 允许I?/span>
mobile;// 允许I?/span> )
?/span> StaffDAO 里面Q(q是 hibernate 映射数据库表后自动生成的Ҏ(gu)Q?/span>
public
void
save(Staff transientInstance) {
log
.debug(
"saving Staff instance"
);
try
{
getSession().save(transientInstance);
log
.debug(
"save successful"
);
}
catch
(
RuntimeException
re) {
log
.error(
"save failed"
, re);
throw
re;
}
}
?/span>
AddStaffAction
里面Q?/span>
public
ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
//
获得表单信息Q?/span>
AddStaffForm addStaffForm = (AddStaffForm) form;
//
TODO
Auto-generated method stub
String staffid = addStaffForm.getStaffid();
String staffname = addStaffForm.getStaffname();
String departmentname = addStaffForm.getDepartmentname();
String sex = addStaffForm.getSex();
String password =
new
String(
"000000"
);
//
转换字符格式
try
{
staffid =
new
String(staffid.getBytes(
"ISO-8859-1"
),
"GB2312"
);
staffname =
new
String(staffname.getBytes(
"ISO-8859-1"
),
"GB2312"
);
departmentname =
new
String(departmentname.getBytes(
"ISO-8859-1"
),
"GB2312"
);
sex =
new
String(sex.getBytes(
"ISO-8859-1"
),
"GB2312"
);
password =
new
String(password.getBytes(
"ISO-8859-1"
),
"GB2312"
);
}
catch
(UnsupportedEncodingException e) {
//
TODO
Auto-generated catch block
e.printStackTrace();
}
Staff staff =
new
Staff();
staff.setStaffid(staffid);
staff.setStaffname(staffname);
staff.setPassword(password);
staff.setDepartmentname(departmentname);
staff.setSex(sex);
try
{
StaffDAO dao =
new
StaffDAO();
dao.save(staff);
}
catch
(Exception e) {
//
TODO
Auto-generated catch block
e.printStackTrace();
}
return
mapping.findForward(
"addStaffSuccess"
); }
action:
1.新徏一个ClientTest1.jsp文gQ代码如下:(x)
<%@ page language="java" contentType="text/html; charset=Gb2312" pageEncoding="GB2312"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=Gb2312"> <title>客户端限刉复提?lt;/title> <script language="javascript" <!--定义重复提交标志变量 --> var repeatSubmitFlag = false; <!-- 重复提交(g)查函?--> function checkSubmit() { if(repeatSubmitFlag) <!-- 如果标志为trueQ则说明面已经提交 --> { window.alert('止重复提交Q?); return false; } else { repeatSubmitFlag = true; return true; } } </script> </head> <body bcolor="#ffffff"> <form name="form_client" action="http://www.dlmu.edu.cn" onsubmit="return checkSubmit();"> <input type="checkbox" name="check_1" checked=true/>大连事大学 <input type="submit" name="submitok"/> </form> </body> </html> |
2.如果重复提交表单׃(x)弹出错误提示对话?br />
采用W二U方法:(x)
1.新徏一个ClientTest2.jsp文gQ代码如下:(x)
<%@ page language="java" contentType="text/html; charset=GB2312" pageEncoding="Gb2312"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=Gb2312"> <title>客户端限刉复提?2</title> </head> <body bgcolor="#ffffff"> <form name="form_client" action="http://www.dlmu.edu.cn" onsubmit="window.document.form_client.submitok.disabled=true; return true;"> <input type="checkbox" name="check_1" checked="true"/>大连事大学 <input type="submit" name="submitok"/> </form> </body> </html> |
<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.io.*"%> <html> <head> <title>文g的徏立、检查与删除</title> </head> <body> <% String path=request.getRealPath(""); //out.println(path); File f=new File(path,"File.txt"); //out.println(f); //out.println(f.exists()); if(f.exists()){//(g)查File.txt是否存在 f.delete();//删除File.txt文g out.println(path + "\\File.txt 存在Q已删除?); }else{ f.createNewFile();//在当前目录下建立一个名为File.txt的文?br />out.println(path + "\\File.txt 不存在,已徏立?);//输出目前所在的目录路径 } %> |
目录的徏?(g)查与删除
<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.io.*"%> <html> <head> <title>目录的徏?(g)查与删除</title> </head> <body> <% String path=request.getRealPath(""); path=path + "\\Sub";//要建立的目录\?br />File d=new File(path);//建立代表Sub目录的File对象Qƈ得到它的一个引?br />if(d.exists()){//(g)查Sub目录是否存在 d.delete(); out.println("Sub目录存在Q已删除"); }else{ d.mkdir();//建立Sub目录 out.println("Sub目录不存在,已徏?); } %> </body> </html> |
如何在JSP中处理虚拟目?/p>
<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.io.*"%> <html> <head> <title>JSP中如何处理虚拟目?lt;/title> </head> <body> 取得虚拟目录对应的磁盘\?lt;br> Web站点ȝ录的位置?lt;font color=#ff0000><%=request.getRealPath("/")%></font><br> JSP|页所在的目录位置<font color=#ff0000><%=request.getRealPath("./")%></font><br> JSP|页所在目录上一层目录的位置<font color=#ff0000><%=request.getRealPath("../")%></font><br> </body> </html> |
文g属性的取得
<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.util.Date,java.io.*"%> <html> <head> <title>文g属性的取得</title> </head> <body> <% String path=request.getRealPath("/"); File f=new File(path,"ReadData.txt"); if(f.exists()){ %> <%=f.getName()%>的属性如下:(x)<br><br> 文g长度为:(x)<%=f.length()%> <%=f.isFile()?"是文?:"不是文g"%><br> <%=f.isDirectory()?"是目?:"不是目录"%><br> <%=f.canRead()?"可读?:"不可d"%><br> <%=f.canWrite()?"可写?:"不可写入"%><br> <%=f.isHidden()?"是隐藏文?:"不是隐藏文g"%><br> 文g的最后修Ҏ(gu)期ؓ(f)Q?lt;%=new Date(f.lastModified())%><br> <% }else{ f.createNewFile();//在当前目录下建立一个名为ReaData.txt的文?br />%> <%=f.getName()%>的属性如下:(x)<br><br> 文g长度为:(x)<%=f.length()%> <%=f.isFile()?"是文?:"不是文g"%><br> <%=f.isDirectory()?"是目?:"不是目录"%><br> <%=f.canRead()?"可读?:"不可d"%><br> <%=f.canWrite()?"可写?:"不可写入"%><br> <%=f.isHidden()?"是隐藏文?:"不是隐藏文g"%><br> 文g的最后修Ҏ(gu)期ؓ(f)Q?lt;%=new Date(f.lastModified())%><br> <% } %> </body> </html> |
取出目录中文件的Ҏ(gu)
<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.io.*"%> <html> <head> <title>取出目录中文件的Ҏ(gu)--列出目录中的文g</title> </head> <body> <% String path=request.getRealPath("/"); File d=new File(path);//建立当前目录中文件的File对象 File list[]=d.listFiles();//取得代表目录中所有文件的File对象数组 out.println("<font color=#ff0000>" + path + "目录下的文gQ?lt;/font><br>"); for(int i=0;i<list.length;i++){ if(list<I>.isFile()){ out.println(list<I>.getName() + "<br>"); } } out.println("<br><font color=#ff0000>" + path + "目录下的目录Q?lt;/font><br>"); for(int i=0;i<list.length;i++){ if(list<I>.isDirectory()){ out.println(list<I>.getName() + "<br>"); } } %> </body> </html> |
判断是否为空白文?/p>
<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.io.*"%> <html> <head> <title>判断是否为空白文?lt;/title> </head> <body> <% String path=request.getRealPath("/"); out.println(path); FileReader fr=new FileReader(path + "\\AtEnd.txt");//建立FileReader对象Qƈ实例化ؓ(f)fr //对FileReadercȝ成的对象使用read()Ҏ(gu)Q可以从字符中d下一个字W?br />if(fr.read()==-1)//判断是否已读到文件的l尾 { out.print("AtEnd.txt文g中没有数?lt;br>"); }else{ out.println("AtEnd.txt文g中有数据"); } fr.close(); %> </body> </html> |
d所有的文g数据
<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.io.*,java.lang.*"%> <html> <head> <title>d所有的文g数据</title> </head> <body> <% String path=request.getRealPath("."); FileReader fr=new FileReader(path + "\\ReadData.txt"); //关键在于dq程中,要判断所d的字W是否已l到了文件的末尾Qƈ且这个字W是不是文g中的断行W,卛_断该字符值是否ؓ(f)13?br />int c=fr.read();//从文件中d一个字W?br />//判断是否已读到文件结?br />while(c!=-1){ out.print((char)c);//输出d的数?br />c=fr.read();//从文件中l箋d数据 if(c==13){//判断是否为断行字W?br />out.print("<br>");//输出分行标签 fr.skip(1);//略过一个字W?br />//c=fr.read();//d一个字W?br />} } fr.close(); %> </body> </html> |
一行一行读取数?/p>
<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.io.*"%> <html> <head> <title>文gd</title> </head> <body> <% String path=request.getRealPath("");//取得当前目录的\?br />FileReader fr=new FileReader(path + "\\file\\inc\\t.txt");//建立FileReader对象Qƈ实例化ؓ(f)fr BufferedReader br=new BufferedReader(fr);//建立BufferedReader对象Qƈ实例化ؓ(f)br String Line=br.readLine();//从文件读取一行字W串 //判断d到的字符串是否不为空 while(Line!=null){ out.println(Line + "<br>");//输出从文件中d的数?br />Line=br.readLine();//从文件中l箋d一行数?br />} br.close();//关闭BufferedReader对象 fr.close();//关闭文g %> </body> </html> |
略过文g中的字符不读?/p>
<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.io.*"%> <html> <head> <title>略过字节不读?lt;/title> </head> <body> <% String path=request.getRealPath("."); FileReader fr=new FileReader(path + "\\ReadData.txt"); fr.skip(2);//跌2个字?br />int c=fr.read();//d一个字?br />while(c!=-1){ out.print((char)c); c=fr.read(); } fr.close(); %> </body> </html> |
数据写入文?/p>
<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.io.*"%> <html> <head> <title>数据写入文?lt;/title> </head> <body> <% String path=request.getRealPath("."); FileWriter fw=new FileWriter(path + "\\WriteData.txt");//建立FileWriter对象Qƈ实例化fw //字W串写入文g fw.write("大家好!"); fw.write("本书是《JSP~程技巧?); fw.write("请多多指教!"); fw.write("email:stride@sina.com"); fw.close(); FileReader fr=new FileReader(path + "\\WriteData.txt"); BufferedReader br=new BufferedReader(fr);//建立BufferedReader对象Qƈ实例化ؓ(f)br String Line=br.readLine(); //d一行数?br />out.println(Line + "<br>"); br.close();//关闭BufferedReader对象 fr.close(); %> </body> </html> |
写入文件的数据分行
<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.io.*"%> <html> <head> <title>写入文件的数据分行</title> </head> <body> <% String path=request.getRealPath("."); FileWriter fw=new FileWriter(path + "\\WriteData.txt"); BufferedWriter bw=new BufferedWriter(fw); bw.write("大家好!"); bw.write("本书是《JSP~程技巧》?); bw.newLine();//断行 bw.write("请多多指教!"); bw.newLine();//断行 bw.write("email: stride@sina.com"); bw.flush();//数据更新至文g fw.close();//关闭文g?br />out.println("写入文g内容为:(x)<br>"); FileReader fr=new FileReader(path + "\\WriteData.txt"); BufferedReader br=new BufferedReader(fr); String Line=br.readLine();//d一行数?br />while(Line!=null){ out.println(Line + "<br>"); Line=br.readLine(); } fr.close(); %> </body> </html> |
如何数据追加写入到文g
<%@ page contentType="text/html;charset=gb2312"%> <%@ page import="java.io.*"%> <html> <head> <title>写入文件的数据分行</title> </head> <body> <% String path=request.getRealPath("."); RandomAccessFile rf=new RandomAccessFile(path + "\\WriteData.txt","rw");//定义一个类RandomAccessFile的对象,q实例化 rf.seek(rf.length());//指针移动到文g末尾 rf.writeBytes("\nAppend a line to the file!"); rf.close();//关闭文g?br />out.println("写入文g内容为:(x)<br>"); FileReader fr=new FileReader(path + "\\WriteData.txt"); BufferedReader br=new BufferedReader(fr);//d文g的BufferedRead对象 String Line=br.readLine(); while(Line!=null){ out.println(Line + "<br>"); Line=br.readLine(); } fr.close();//关闭文g %> </body> </html> |
2.const ?static readonly 区别Q?/p>
3.extern 是什么意思?
4.abstract 是什么意思?
5.internal 修饰Wv什么作用?
6.sealed 修饰W是q什么的Q?/p>
7.override ?overload 的区别?
8.什么是索引指示器?
9.new 修饰W是起什么作用?
10.this 关键字的含义Q?/p>
11.可以使用抽象函数重写基类中的虚函数吗Q?/p>
12.密封cd以有虚函数吗Q?/p>
13.如果基类中的虚属性只有一个属性访问器Q那么承类重写该属性后可以有几个属性访问器Q如果基cM?get ?set 两个呢?
14.abstract 可以?virtual 一起用吗Q可以和 override 一起用吗Q?/p>
15.接口可以包含哪些成员Q?/p>
16.cdl构的区别?
17.接口的多l承?x)带来哪些问题?/p>
18.抽象cd接口的区别?
19.别名指示W是什么?
20.如何释放非托资源?
21.P/Invoke是什么?
22.StringBuilder ?String 的区别?
23.explicit ?implicit 的含义?
24.params 有什么用Q?/p>
25.什么是反射Q?/p>
以下是我做的一份参考答案(C# 语言范畴之内Q,如果有不准确、不全面的,Ƣ迎各位朋友指正Q?/p>
1.静态变量和非静态变量的区别Q?/p>
{:(x)
静态变量:(x)
静态变量?static 修饰W进行声?/p>
在所属类被装载时创徏
通过c进行访?/p>
所属类的所有实例的同一静态变量都是同一个?/p>
非静态变量:(x)
不带?static 修饰W声明的变量U做非静态变?/p>
在类被实例化时创?/p>
通过对象q行讉K
同一个类的不同实例的同一非静态变量可以是不同的?/p>
CZQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example01 { class Program { class Class1 { public static String staticStr = "Class"Qpublic String notstaticStr = "Obj"Q} static void MainQstring[] argsQ?/p>
{ //静态变量通过c进行访问,该类所有实例的同一静态变量都是同一个值Console.WriteLineQ?Class1's staticStrQ?{0}"Q?Class1.staticStrQ;
Class1 tmpObj1 = new Class1Q)(j)QtmpObj1.notstaticStr = "tmpObj1"QClass1 tmpObj2 = new Class1Q)(j)QtmpObj2.notstaticStr = "tmpObj2"Q?/p>
//非静态变量通过对象q行讉KQ不同对象的同一非静态变量可以有不同的值Console.WriteLineQ?tmpObj1's notstaticStrQ?{0}"Q?tmpObj1.notstaticStrQ;Console.WriteLineQ?tmpObj2's notstaticStrQ?{0}"Q?tmpObj2.notstaticStrQ;
Console.ReadLineQ)(j)Q}l果QClass1's staticStrQ?Class tmpObj1's notstaticStrQ?tmpObj1 tmpObj2's notstaticStrQ?tmpObj2
2.const ?static readonly 区别Q?/p>
{:(x)
const
?const 修饰W声明的成员叫常量,是在~译期初始化q嵌入到客户端程?/p>
static readonly
?static readonly 修饰W声明的成员依然是变量,只不q具有和帔RcM的用方法:(x)通过c进行访问、初始化后不可以修改。但与常量不同的是这U变量是在运行期初始?/p>
CZQ?/p>
试c:(x)
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example02Lib { public class Class1 { public const String strConst = "Const"Qpublic static readonly String strStaticReadonly = "StaticReadonly"Q?/public const String strConst = "Const Changed"Q?/public static readonly String strStaticReadonly = "StaticReadonly Changed"Q}
客户端代码:(x)
using SystemQusing System.Collections.GenericQusing System.TextQusing Example02LibQ?/p>
namespace Example02 { class Program { static void MainQstring[] argsQ?/p>
{ //修改Example02中Class1的strConst初始值后Q只~译Example02Lib目//然后到资源管理器里把新编译的Example02Lib.dll拯Example02.exe所在的目录Q执行Example02.exe //切不可在IDE里直接调试运行因?x)重新编译整个解x案!Q?/p>
//可以看到strConst的输出没有改变,而strStaticReadonly的输出已l改?/表明Const变量是在~译期初始化q嵌入到客户端程序,而StaticReadonly是在q行时初始化的Console.WriteLineQ?strConst Q?{0}"Q?Class1.strConstQ;Console.WriteLineQ?strStaticReadonly Q?{0}"Q?Class1.strStaticReadonlyQ;
Console.ReadLineQ)(j)Q}l果QstrConst Q?Const strStaticReadonly Q?StaticReadonly
修改后的CZQ?/p>
试c:(x)
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example02Lib { public class Class1 { //public const String strConst = "Const"Q?/public static readonly String strStaticReadonly = "StaticReadonly"Qpublic const String strConst = "Const Changed"Qpublic static readonly String strStaticReadonly = "StaticReadonly Changed"Q}l果
strConst Q?Const strStaticReadonly Q?StaticReadonly Changed
3.extern 是什么意思?
{:(x)
extern 修饰W用于声明由E序集外部实现的成员函数
l常用于pȝAPI函数的调用(通过 DllImport Q。注意,和DllImport一起用时要加?static 修饰W?/p>
也可以用于对于同一E序集不同版本组件的调用Q用 extern 声明别名Q?/p>
不能?abstract 修饰W同时?/p>
CZQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQusing System.Runtime.InteropServicesQ?/p>
namespace Example03 { class Program { //注意DllImport是一个Attribute PropertyQ在System.Runtime.InteropServices命名I间中定?/extern与DllImport一起用时必须再加上一个static修饰W[DllImportQ?User32.dll"Q] public static extern int MessageBoxQint HandleQ?string MessageQ?string CaptionQ?int TypeQ;
static int MainQ)(j)
{ string myStringQConsole.WriteQ?Enter your messageQ?"Q;myString = Console.ReadLineQ)(j)Qreturn MessageBoxQ?Q?myStringQ?"My Message Box"Q?0Q;}l果Q?/p>
4.abstract 是什么意思?
{:(x)
abstract 修饰W可以用于类、方法、属性、事件和索引指示器(indexerQ,表示其ؓ(f)抽象成员
abstract 不可以和 static 、virtual 、override 一起?/p>
声明?abstract 成员可以不包括实C码,但只有类中还有未实现的抽象成员,该类׃可以被实例化Q通常用于强制l承cdd现某一成员
CZQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example04 { #region 基类Q抽象类public abstract class BaseClass { //抽象属性,同时hget和set讉K器表C承类必须该属性实Cؓ(f)可读写public abstract String Attribute { getQsetQ}
//抽象Ҏ(gu)Q传入一个字W串参数无返回值public abstract void FunctionQString valueQ;
//抽象事gQ类型ؓ(f)pȝ预定义的代理QdelegateQ:(x)EventHandler public abstract event EventHandler EventQ?/p>
//抽象索引指示器,只具有get讉K器表C承类必须该索引指示器实Cؓ(f)只读public abstract Char this[int Index] { getQ} #endregion
#region l承cpublic class DeriveClass Q?BaseClass { private String attributeQ?/p>
public override String Attribute { get { return attributeQ} set { attribute = valueQ} public override void FunctionQString valueQ?/p>
{ attribute = valueQif QEvent Q? nullQ?/p>
{ EventQthisQ?new EventArgsQ)(j)Q;} public override event EventHandler EventQpublic override Char this[int Index] { get { return attribute[Index]Q} #endregion
class Program { static void OnFunctionQobject senderQ?EventArgs eQ?/p>
{ for Qint i = 0Q?i < Q(DeriveClassQsenderQ。Attribute.LengthQ?i++Q?/p>
{ Console.WriteLineQ(QDeriveClassQsenderQ[i]Q;} static void MainQstring[] argsQ?/p>
{ DeriveClass tmpObj = new DeriveClassQ)(j)Q?/p>
tmpObj.Attribute = "1234567"QConsole.WriteLineQtmpObj.AttributeQ;
//静态函数OnFunction与tmpObj对象的Event事gq行兌tmpObj.Event += new EventHandlerQOnFunctionQ;
tmpObj.FunctionQ?7654321"Q;
Console.ReadLineQ)(j)Q}l果Q?234567 7 6 5 4 3 2 1
5.internal 修饰Wv什么作用?
{:(x)
internal 修饰W可以用于类型或成员Q用该修饰W声明的cd或成员只能在同一E集内访?/p>
接口的成员不能?internal 修饰W?/p>
CZ
Example05Lib 目?Class1
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example05Lib { public class Class1 { internal String strInternal = nullQpublic String strPublicQ}l果Example05Lib 目?Class2 cd以访问到 Class1 ?strInternal 成员
Example05 目?Program cL法访问到 Class1 ?strInternal 成员
[NextPage]
6.sealed 修饰W是q什么的Q?/p>
{:(x)
sealed 修饰W表C密?/p>
用于cLQ表CcM能再被承,不能?abstract 同时使用Q因两个修饰W在含义上互相排?/p>
用于Ҏ(gu)和属性时Q表CҎ(gu)或属性不能再被承,必须?override 关键字一起用,因ؓ(f)使用 sealed 修饰W的Ҏ(gu)或属性肯定是基类中相应的虚成?/p>
通常用于实现W三方类库时不想被客L(fng)l承Q或用于没有必要再承的cM防止滥用l承造成层次l构体系混ؕ
恰当的利?sealed 修饰W也可以提高一定的q行效率Q因Z用考虑l承cM(x)重写该成?/p>
CZQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example06 { class Program { class A { public virtual void FQ)(j)
{ Console.WriteLineQ?A.F"Q;} public virtual void GQ)(j)
{ Console.WriteLineQ?A.G"Q;} class B Q?A { public sealed override void FQ)(j)
{ Console.WriteLineQ?B.F"Q;} public override void GQ)(j)
{ Console.WriteLineQ?B.G"Q;} class C Q?B { public override void GQ)(j)
{ Console.WriteLineQ?C.G"Q;} static void MainQstring[] argsQ?/p>
{ new AQ)(j)。FQ)(j)Qnew AQ)(j)。GQ)(j)Qnew BQ)(j)。FQ)(j)Qnew BQ)(j)。GQ)(j)Qnew CQ)(j)。FQ)(j)Qnew CQ)(j)。GQ)(j)Q?/p>
Console.ReadLineQ)(j)Q}l果Q类 B 在承类 A 时可以重写两个虚函数Q如图所C:(x)
׃c?B 中对 F Ҏ(gu)q行了密, c?C 在承类 B 时只能重写一个函敎ͼ如图所C:(x)
控制台输出结果,c?C 的方?F 只能是输?cB 中对该方法的实现Q?/p>
A.F A.G B.F B.G B.F C.G
7.override ?overload 的区别?
{:(x)
override 表示重写Q用于承类对基cM虚成员的实现
overload 表示重蝲Q用于同一个类中同名方法不同参敎ͼ包括cd不同或个C同)(j)的实?/p>
CZQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example07 { class Program { class BaseClass { public virtual void FQ)(j)
{ Console.WriteLineQ?BaseClass.F"Q;} class DeriveClass Q?BaseClass { public override void FQ)(j)
{ base.FQ)(j)QConsole.WriteLineQ?DeriveClass.F"Q;} public void AddQint LeftQ?int RightQ?/p>
{ Console.WriteLineQ?Add for IntQ?{0}"Q?Left + RightQ;} public void AddQdouble LeftQ?double RightQ?/p>
{ Console.WriteLineQ?Add for intQ?{0}"Q?Left + RightQ;} static void MainQstring[] argsQ?/p>
{ DeriveClass tmpObj = new DeriveClassQ)(j)QtmpObj.FQ)(j)QtmpObj.AddQ?Q?2Q;tmpObj.AddQ?.1Q?2.2Q;
Console.ReadLineQ)(j)Q}l果QBaseClass.F DeriveClass.F Add for IntQ?3 Add for intQ?3.3
8.什么是索引指示器?
{:(x)
实现索引指示器(indexerQ的cd以象数组那样使用其实例后的对象,但与数组不同的是索引指示器的参数cd不仅限于int
单来_(d)其本质就是一个含参数属?/p>
CZQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example08 { public class Point { private double xQ?yQpublic PointQdouble XQ?double YQ?/p>
{ x = XQy = YQ} //重写ToStringҎ(gu)方便输出public override string ToStringQ)(j)
{ return String.FormatQ?XQ?{0} Q?YQ?{1}"Q?xQ?yQ;} public class Points { Point[] pointsQpublic PointsQPoint[] PointsQ?/p>
{ points = PointsQ} public int PointNumber { get { return points.LengthQ} //实现索引讉K器public Point this[int Index] { get { return points[Index]Q}
//感谢watson huaQhttp://huazhihao.cnblogs.com/Q的指点//索引指示器的实质是含参属性,参数q不只限于int class WeatherOfWeek { public string this[int Index] { get { //注意caseD用return直接q回所以不需要break switch QIndexQ?/p>
{ case 0Q{ return "Today is cloudyQ?Q} case 5Q{ return "Today is thundershowerQ?Q} defaultQ{ return "Today is fineQ?Q} public string this[string Day] { get { string TodayWeather = nullQ?/switch的标准写法switch QDayQ?/p>
{ case "Sunday"Q{ TodayWeather = "Today is cloudyQ?QbreakQ} case "Friday"Q{ TodayWeather = "Today is thundershowerQ?QbreakQ} defaultQ{ TodayWeather = "Today is fineQ?QbreakQ} return TodayWeatherQ} class Program { static void MainQstring[] argsQ?/p>
{ Point[] tmpPoints = new Point[10]Qfor Qint i = 0Q?i < tmpPoints.LengthQ?i++Q?/p>
{ tmpPoints[i] = new PointQiQ?Math.SinQiQ)(j)Q}
Points tmpObj = new PointsQtmpPointsQ;for Qint i = 0Q?i < tmpObj.PointNumberQ?i++Q?/p>
{ Console.WriteLineQtmpObj[i]Q;}
string[] Week = new string[] { "Sunday"Q?"Monday"Q?"Tuesday"Q?"Wednesday"Q?"Thursday"Q?"Friday"Q?"Staurday"}QWeatherOfWeek tmpWeatherOfWeek = new WeatherOfWeekQ)(j)Qfor Qint i = 0Q?i < 6Q?i++Q?/p>
{ Console.WriteLineQtmpWeatherOfWeek[i]Q;} foreach Qstring tmpDay in WeekQ?/p>
{ Console.WriteLineQtmpWeatherOfWeek[tmpDay]Q;}
Console.ReadLineQ)(j)Q}l果QXQ?0 Q?YQ?0 XQ?1 Q?YQ?0.841470984807897 XQ?2 Q?YQ?0.909297426825682 XQ?3 Q?YQ?0.141120008059867 XQ?4 Q?YQ?-0.756802495307928 XQ?5 Q?YQ?-0.958924274663138 XQ?6 Q?YQ?-0.279415498198926 XQ?7 Q?YQ?0.656986598718789 XQ?8 Q?YQ?0.989358246623382 XQ?9 Q?YQ?0.412118485241757 Today is cloudyQ?/p>
Today is fineQ?/p>
Today is thundershowerQ?/p>
Today is cloudyQ?/p>
Today is fineQ?/p>
Today is thundershowerQ?/p>
Today is fineQ?/p>
9.new 修饰W是起什么作用?
{:(x)
new 修饰W与 new 操作W是两个概念
new 修饰W用于声明类或类的成员,表示隐藏了基cM同名的成员。而new 操作W用于实例化一个类?/p>
new 修饰W只能用于承类Q一般用于I补基c设计的不
new 修饰W和 override 修饰W不可同时用在一个成员上Q因两个修饰W在含义上互相排?/p>
CZQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example09 { class BaseClass { //基类设计者声明了一个PI的公共变量,方便q行q算public static double PI = 3.1415Q} class DervieClass Q?BaseClass { //l承cd现该变量的g能满精度,于是可以通过new修饰W显C隐藏基cM的声明public new static double PI = 3.1415926Q} class Program { static void MainQstring[] argsQ?/p>
{ Console.WriteLineQBaseClass.PIQ;Console.WriteLineQDervieClass.PIQ;
Console.ReadLineQ)(j)Q}l果Q?.1415 3.1415926
10.this 关键字的含义Q?/p>
{:(x)
this 是一个保留字Q仅限于构造函数和Ҏ(gu)成员中?/p>
在类的构造函C出现表示Ҏ(gu)在构造的对象本n的引用,在类的方法中出现表示对调用该Ҏ(gu)的对象的引用Q在l构的构造上函数中出现表C对正在构造的l构的引用,在结构的Ҏ(gu)中出现表C对调用该方法的l果的引?/p>
this 保留字不能用于静态成员的实现里,因ؓ(f)q时对象或结构ƈ未实例化
?C# pȝ中,this 实际上是一个常量,所以不能?this++ q样的运?/p>
this 保留字一般用于限定同名的隐藏成员、将对象本n做ؓ(f)参数、声明烦(ch)引访问器、判断传入参数的对象是否为本w?/p>
CZQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example10 { class Class1 { private double cQprivate string valueQ?/p>
public double C { get { return cQ} public Class1Qdouble cQ?/p>
{ //限定同名的隐藏成员this.c = cQ} public Class1QClass1 valueQ?/p>
{ //用对象本w实例化自己没有意义if Qthis Q? valueQ?/p>
{ c = value.CQ} public override string ToStringQ)(j)
{ //对象本w做为参数return string.FormatQ?{0} Celsius = {1} Fahrenheit"Q?cQ?UnitTransClass.C2FQthisQ)(j)Q}
//׃好奇Q在q做了一个效率测试,想看看到底哪U方式访问成员变量更快,l论Q区别不大…?/p>
public string Test1Q)(j)
{ long vTickCount = Environment.TickCountQfor Qint i = 0Q?i < 10000000Q?i++Q?/p>
this.value = i.ToStringQ)(j)Qreturn string.FormatQ?Have this.Q?{0} MSEL"Q?Environment.TickCount - vTickCountQ;} public string Test2Q)(j)
{ long vTickCount = Environment.TickCountQfor Qint i = 0Q?i < 10000000Q?i++Q?/p>
value = i.ToStringQ)(j)Qreturn string.FormatQ?Don't have this.Q?{0} MSEL"Q?Environment.TickCount - vTickCountQ;} class UnitTransClass { public static double C2FQClass1 valueQ?/p>
{ //摄氏到华氏的转换公式return 1.8 * value.C + 32Q} class Program { static void MainQstring[] argsQ?/p>
{ Class1 tmpObj = new Class1Q?7.5Q;
Console.WriteLineQtmpObjQ;
Console.WriteLineQtmpObj.Test1Q)(j)Q;Console.WriteLineQtmpObj.Test2Q)(j)Q;
Console.ReadLineQ)(j)Q}l果Q?7.5 Celsius = 99.5 Fahrenheit Have this.Q?4375 MSEL Don't have this.Q?4406 MSEL
[NextPage]
11.可以使用抽象函数重写基类中的虚函数吗Q?/p>
{:(x)
可以Q但需使用 new 修饰W显式声明,表示隐藏了基cM该函数的实现
CZQ?/p>
class BaseClass { public virtual void FQ)(j)
{ Console.WriteLineQ?BaseClass.F"Q;} abstract class DeriveClass Q?BaseClass { public new abstract void FQ)(j)Q}
12.密封cd以有虚函数吗Q?/p>
{:(x)
可以Q基cM的虚函数隐式的转化为非虚函敎ͼ但密类本n不能再增加新的虚函数
CZQ?/p>
class BaseClass { public virtual void FQ)(j)
{ Console.WriteLineQ?BaseClass.F"Q;} sealed class DeriveClass Q?BaseClass { //基类中的虚函数F被隐式的转化为非虚函?/p>
//密封cM不能再声明新的虚函数G //public virtual void GQ)(j)
//{ // Console.WriteLineQ?DeriveClass.G"Q;//} }
13.如果基类中的虚属性只有一个属性访问器Q那么承类重写该属性后可以有几个属性访问器Q如果基cM?get ?set 两个呢?
{:(x)
如果基类中的虚属性只有一个属性访问器Q那么承类重写该属性后也应只有一个。如果基cM?get ?set 两个属性访问器Q那么承类中可以只有一个也可以同时有两个属性访问器
14.abstract 可以?virtual 一起用吗Q可以和 override 一起用吗Q?/p>
{:(x)
abstract 修饰W不可以?static、virtual ?override 修饰W一起?/p>
15.接口可以包含哪些成员Q?/p>
{:(x)
接口可以包含属性、方法、烦(ch)引指C器和事Ӟ但不能包含常量、域、操作符、构造函数和析构函数Q而且也不能包含Q何静态成?/p>
16.cdl构的区别?
{:(x)c:(x)
cL引用cd在堆上分配,cȝ实例q行赋值只是复制了引用Q都指向同一D实际对象分配的内存
cL构造和析构函数
cd以承和被?/p>
l构Q?/p>
l构是值类型在栈上分配Q虽然栈的访问速度比较堆要快,但栈的资源有限放Q,l构的赋值将分配产生一个新的对象?/p>
l构没有构造函敎ͼ但可以添加。结构没有析构函?/p>
l构不可以承自另一个结构或被承,但和cM样可以承自接口
CZQ?/p>
Ҏ(gu)以上比较Q我们可以得Z些轻量的对象最好用结构,但数据量大或有复杂处理逻辑对象最好用类?/p>
如:(x)GeoemtryQGIS 里的一个概论,?OGC 标准里有定义Q?最好用类Q?Geometry 中点的成员最好用结?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example16 { interface IPoint { double X { getQsetQ} double Y { getQsetQ} double Z { getQsetQ} //l构也可以从接口l承struct PointQ?IPoint { private double xQ?yQ?zQ?/l构也可以增加构造函数public PointQdouble XQ?double YQ?double ZQ?/p>
{ this.x = XQthis.y = YQthis.z = ZQ} public double X { get { return xQ?} set { x = valueQ?} } public double Y { get { return xQ?} set { x = valueQ?} } public double Z { get { return xQ?} set { x = valueQ?} } //在此化了点状Geometry的设计,实际产品中还包含ProjectQ坐标变换)(j){复杂操作class PointGeometry { private Point valueQ?/p>
public PointGeometryQdouble XQ?double YQ?double ZQ?/p>
{ value = new PointQXQ?YQ?ZQ;} public PointGeometryQPoint valueQ?/p>
{ //l构的赋值将分配新的内存this.value = valueQ} public double X { get { return value.XQ?} set { this.value.X = valueQ?} } public double Y { get { return value.YQ?} set { this.value.Y = valueQ?} } public double Z { get { return value.ZQ?} set { this.value.Z = valueQ?} } public static PointGeometry operator +QPointGeometry LeftQ?PointGeometry RigthQ?/p>
{ return new PointGeometryQLeft.X + Rigth.XQ?Left.Y + Rigth.YQ?Left.Z + Rigth.ZQ;} public override string ToStringQ)(j)
{ return string.FormatQ?XQ?{0}Q?YQ?{1}Q?ZQ?{2}"Q?value.XQ?value.YQ?value.ZQ;} class Program { static void MainQstring[] argsQ?/p>
{ Point tmpPoint = new PointQ?Q?2Q?3Q;
PointGeometry tmpPG1 = new PointGeometryQtmpPointQ;PointGeometry tmpPG2 = new PointGeometryQtmpPointQ;tmpPG2.X = 4QtmpPG2.Y = 5QtmpPG2.Z = 6Q?/p>
//׃l构是值类型,tmpPG1 ?tmpPG2 的坐标ƈ不一样Console.WriteLineQtmpPG1Q;Console.WriteLineQtmpPG2Q;
//׃cL引用cdQ对tmpPG1坐标修改后媄(jing)响到了tmpPG3 PointGeometry tmpPG3 = tmpPG1QtmpPG1.X = 7QtmpPG1.Y = 8QtmpPG1.Z = 9QConsole.WriteLineQtmpPG1Q;Console.WriteLineQtmpPG3Q;
Console.ReadLineQ)(j)Q}l果QXQ?1Q?YQ?2Q?ZQ?3 XQ?4Q?YQ?5Q?ZQ?6 XQ?7Q?YQ?8Q?ZQ?9
[NextPage]
17.接口的多l承?x)带来哪些问题?/p>
{:(x)
C# 中的接口与类不同Q可以用多l承Q即一个子接口可以有多个父接口。但如果两个父成员具有同名的成员Q就产生了二义性(q也正是 C# 中类取消了多l承的原因之一Q,q时在实现时最好用显式的声明
CZQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example17 { class Program { //一个完整的接口声明CZinterface IExample { //属性string P { getQsetQ} //Ҏ(gu)string FQint ValueQ;//事gevent EventHandler EQ?/索引指示器string this[int Index] { getQsetQ} interface IA { int Count { getQ?setQ} } interface IB { int CountQ)(j)Q} //IC接口从IA和IB多重l承interface IC Q?IAQ?IB { } class C Q?IC { private int count = 100Q?/昑ּ声明实现IA接口中的Count属性int IA.Count { get { return 100Q?} set { count = valueQ?} } //昑ּ声明实现IB接口中的CountҎ(gu)int IB.CountQ)(j)
{ return count * countQ} static void MainQstring[] argsQ?/p>
{ C tmpObj = new CQ)(j)Q?/p>
//调用时也要显式{换Console.WriteLineQ?Count propertyQ?{0}"Q?Q(IAQtmpObjQ。CountQ;Console.WriteLineQ?Count functionQ?{0}"Q?Q(IBQtmpObjQ。CountQ)(j)Q;
Console.ReadLineQ)(j)Q}l果QCount propertyQ?100 Count functionQ?10000
18.抽象cd接口的区别?
{:(x)
抽象c(abstract classQ可以包含功能定义和实现Q接口(interfaceQ只能包含功能定?/p>
抽象cL从一pd相关对象中抽象出来的概念Q?因此反映的是事物的内部共性;接口是ؓ(f)了满_部调用而定义的一个功能约定, 因此反映的是事物的外部特?/p>
分析对象Q提炼内部共性Ş成抽象类Q用以表C对象本质,即“是什么?/p>
为外部提供调用或功能需要扩充时优先使用接口
19.别名指示W是什么?
{:(x)
通过别名指示W我们可以ؓ(f)某个cd起一个别?/p>
主要用于解决两个命名I间内有同名cd的冲H或避免使用冗余的命名空?/p>
别名指示W只在一个单元文件内起作?/p>
CZQ?/p>
Class1.csQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01 { class Class1 { public override string ToStringQ)(j)
{ return "com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01's Class1"Q} Class2.cs
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02 { class Class1 { public override string ToStringQ)(j)
{ return "com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02's Class1"Q}d元(Program.csQ:(x)
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
//使用别名指示W解军_名类型的冲突using Lib01Class1 = com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01.Class1Qusing Lib02Class2 = com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02.Class1Q?/p>
namespace Example19 { class Program { static void MainQstring[] argsQ?/p>
{ Lib01Class1 tmpObj1 = new Lib01Class1Q)(j)QLib02Class2 tmpObj2 = new Lib02Class2Q)(j)Q?/p>
Console.WriteLineQtmpObj1Q;Console.WriteLineQtmpObj2Q;
Console.ReadLineQ)(j)Q}l果Qcom.nblogs.reonlyrun.CSharp26QExample.Example19.Lib01's Class1 com.nblogs.reonlyrun.CSharp26QExample.Example19.Lib02's Class1
20.如何释放非托资源?
{:(x)
。NET q_在内存管理方面提供了GCQGarbage CollectionQ,负责自动释放托管资源和内存回收的工作Q但它无法对非托资源进行释放,q时我们必须自己提供Ҏ(gu)来释攑֯象内分配的非托管资源Q比如你在对象的实现代码中用了一个COM对象
最单的办法Q可以通过实现protected void FinalizeQ)(j)Q析构函C(x)在编译时变成q个东东Q来释放非托资源,因ؓ(f)GC在释攑֯象时?x)检查该对象是否实现?FinalizeQ)(j) Ҏ(gu)Q如果是则调用它。但Q据说这样会(x)降低效率…?/p>
有一U更好的Q那是通过实现一个接口显式的提供l客戯用端手工释放对象的方法,而不是傻?c){着GC来释放我们的对象Q何冉|率又那么低)(j)
System 命名I间内有一?IDisposable 接口Q拿来做q事非常合适,q得我们自己再声明一个接口了
另外补充一句,q种实现q不一定要使用了非托管资源后才用,如果你设计的cM(x)在运行时有大些的实例Q象 GIS 中的GeometryQ,Z优化E序性能Q你也可以通过实现该接口让客户调用端在认不需要这些对象时手工释放它们
CZQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example20 { class Program { class Class1 Q?IDisposable { //析构函数Q编译后变成 protected void FinalizeQ)(j)QGC?x)在回收对象前?x)调用调用该方法~Class1Q)(j)
{ DisposeQfalseQ;}
//通过实现该接口,客户可以昑ּ地释攑֯象,而不需要等待GC来释放资源,据说那样?x)降低效率void IDisposable.DisposeQ)(j)
{ DisposeQtrueQ;}
//释N托管资源设计成一个虚函数Q提供在l承cM释放基类的资源的能力protected virtual void ReleaseUnmanageResourcesQ)(j)
{ //Do something…?/p>
}
//U有函数用以释放非托资源private void DisposeQbool disposingQ?/p>
{ ReleaseUnmanageResourcesQ)(j)Q?/p>
//为true时表C是客户昑ּ调用了释攑և敎ͼ需通知GC不要再调用对象的FinalizeҎ(gu)//为false时肯定是GC调用了对象的FinalizeҎ(gu)Q所以没有必要再告诉GC你不要调用我的FinalizeҎ(gu)啦if QdisposingQ?/p>
{ GC.SuppressFinalizeQthisQ;} static void MainQstring[] argsQ?/p>
{ //tmpObj1没有手工释放资源Q就{着GC来慢慢的释放它吧Class1 tmpObj1 = new Class1Q)(j)Q?/p>
//tmpObj2调用了DisposeҎ(gu)Q传说比{着GC来释攑֮效率要调一?/个h认ؓ(f)是因逐个对象的查看其元数据,以确认是否实CDisposeҎ(gu)?/当然最重要的是我们可以自己定释放的时间以节省内存Q优化程序运行效率Class1 tmpObj2 = new Class1Q)(j)Q(QIDisposableQtmpObj2Q。DisposeQ)(j)Q}
21.P/Invoke是什么?
{:(x)
在受控代码与非受控代码进行交互时?x)生一个事务(transitionQ?Q这通常发生在用^台调用服务(Platform Invocation ServicesQ,即P/Invoke
如调用系l的 API 或与 COM 对象打交道,通过 System.Runtime.InteropServices 命名I间
虽然使用 Interop 非常方便Q但据估计每ơ调用事务都要执?10 ?40 条指令,v来开销也不,所以我们要量调用事?/p>
如果非用不可Q徏议本着一ơ调用执行多个动作,而不是多ơ调用每ơ只执行量动作的原?/p>
[NextPage]
22.StringBuilder ?String 的区别?
{:(x)
String 虽然是一个引用类型,但在赋值操作时?x)生一个新的对象,?StringBuilder 则不?/p>
所以在大量字符串拼接或频繁Ҏ(gu)一字符串进行操作时最好?StringBuilderQ不要?String
CZQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example22 { class Program { static void MainQstring[] argsQ?/p>
{ const int cycle = 100000Q?/p>
long vTickCount = Environment.TickCountQString str = nullQfor Qint i = 0Q?i < cycleQ?i++Q?/p>
str += i.ToStringQ)(j)QConsole.WriteLineQ?StringQ?{0} MSEL"Q?Environment.TickCount - vTickCountQ;
vTickCount = Environment.TickCountQ?/看到q个变量名我q气,奇怪ؓ(f)什么大安使它呢? Q)(j)
StringBuilder sb = new StringBuilderQ)(j)Qfor Qint i = 0Q?i < cycleQ?i++Q?/p>
sb.AppendQiQ;Console.WriteLineQ?StringBuilderQ?{0} MSEL"Q?Environment.TickCount - vTickCountQ;
Console.ReadLineQ)(j)Q}l果QStringQ?102047 MSEL StringBuilderQ?46 MSEL
23.explicit ?implicit 的含义?
{:(x)
explicit ?implicit 属于转换q算W,如用q两者可以让我们自定义的cd支持怺交换
explicti 表示昑ּ转换Q如?A -> B 必须q行强制cd转换QB = QBQAQ?/p>
implicit 表示隐式转换Q如?B -> A 只需直接赋|A = BQ?/p>
隐式转换可以让我们的代码看上L漂亮、更z易懂,所以最好多使用 implicit q算W。不q!如果对象本n在{换时?x)损׃些信息(如精度)(j)Q那么我们只能?explicit q算W,以便在编译期p警告客户调用?/p>
CZQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example23 { class Program { //本例灉|来源于大话西游经典台词“神仙?妖怪?”——主要是我实在想不出什么好例子了class Immortal { public string nameQpublic ImmortalQstring NameQ?/p>
{ name = NameQ} public static implicit operator MonsterQImmortal valueQ?/p>
{ return new MonsterQvalue.name + "Q神仙变妖怪?偷偷下凡卛_…?Q;} class Monster { public string nameQpublic MonsterQstring NameQ?/p>
{ name = NameQ} public static explicit operator ImmortalQMonster valueQ?/p>
{ return new ImmortalQvalue.name + "Q妖怪想当神仙?再去修炼五百q_(d)"Q;} static void MainQstring[] argsQ?/p>
{ Immortal tmpImmortal = new ImmortalQ?紫霞仙子"Q;//隐式转换Monster tmpObj1 = tmpImmortalQConsole.WriteLineQtmpObj1.nameQ;
Monster tmpMonster = new MonsterQ?孙?zhn)I?Q;//昑ּ转换Immortal tmpObj2 = QImmortalQtmpMonsterQConsole.WriteLineQtmpObj2.nameQ;
Console.ReadLineQ)(j)Q}l果Q霞仙子:(x)仙变妖怪?偷偷下凡卛_…?/p>
孙?zhn)I:(x)妖怪想当神仙?再去修炼五百q_(d)
24.params 有什么用Q?/p>
{:(x)
params 关键字在Ҏ(gu)成员的参数列表中使用Qؓ(f)该方法提供了参数个数可变的能?/p>
它在只能出现一ơƈ且不能在其后再有参数定义Q之前可?/p>
CZQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace ConsoleApplication1 { class App { //W一个参数必L整型Q但后面的参C数是可变的?/p>
//而且׃定的是object数组Q所有的数据cd都可以做为参C入public static void UseParamsQint idQ?params object[] listQ?/p>
{ Console.WriteLineQidQ;for Qint i = 0Q?i < list.LengthQ?i++Q?/p>
{ Console.WriteLineQlist[i]Q;}
static void MainQ)(j)
{ //可变参数部分传入了三个参敎ͼ都是字符串类型UseParamsQ?Q?"a"Q?"b"Q?"c"Q;//可变参数部分传入了四个参敎ͼ分别为字W串、整数、QҎ(gu)和双_ֺ点数数lUseParamsQ?Q?"d"Q?100Q?33.33Q?new double[] { 1.1Q?2.2 }Q;
Console.ReadLineQ)(j)Q}l果Q? a b c 2 d 100 33.33 System.Double[]
25.什么是反射Q?/p>
{:(x)
反射QReflectionQ通过它我们可以在q行时获得各U信息,如程序集、模块、类型、字Dc(din)属性、方法和事g
通过对类型动态实例化后,q可以对其执行操?/p>
一般用于插件式框架E序和设计模式的实现Q当然反是一U手D可以充分发挥其能量来完成你惛_的Q何事情(前面好象见过一位高人用反射调用一个官方类库中未说明的函数……)(j)
CZQ?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
namespace Example25Lib { public class Class1 { private string nameQprivate int ageQ?/p>
//如果昑ּ的声明了无参数构造函敎ͼ客户端只需要用E序集的CreateInstance卛_实例化该c?/在此Ҏ(gu)不实玎ͼ以便在客戯用端体现构造函数的反射实现//public Class1Q)(j)
//{ //} public Class1Qstring NameQ?int AgeQ?/p>
{ name = NameQage = AgeQ} public void ChangeNameQstring NewNameQ?/p>
{ name = NewNameQ} public void ChangeAgeQint NewAgeQ?/p>
{ age = NewAgeQ} public override string ToStringQ)(j)
{ return string.FormatQ?NameQ?{0}Q?AgeQ?{1}"Q?nameQ?ageQ;}反射实例化对象ƈ调用其方法,属性和事g的反调用略?/p>
using SystemQusing System.Collections.GenericQusing System.TextQ?/p>
//注意d该反的命名I间using System.ReflectionQ?/p>
namespace Example25 { class Program { static void MainQstring[] argsQ?/p>
{ //加蝲E序集Assembly tmpAss = Assembly.LoadFileQAppDomain.CurrentDomain.BaseDirectory + "Example25Lib.dll"Q;
//遍历E序集内所有的cdQƈ实例化Type[] tmpTypes = tmpAss.GetTypesQ)(j)Qforeach QType tmpType in tmpTypesQ?/p>
{ //获取W一个类型的构造函C息ConstructorInfo[] tmpConsInfos = tmpType.GetConstructorsQ)(j)Qforeach QConstructorInfo tmpConsInfo in tmpConsInfosQ?/p>
{ //为构造函数生成调用的参数集合ParameterInfo[] tmpParamInfos = tmpConsInfo.GetParametersQ)(j)Qobject[] tmpParams = new object[tmpParamInfos.Length]Qfor Qint i = 0Q?i < tmpParamInfos.LengthQ?i++Q?/p>
{ tmpParams[i] = tmpAss.CreateInstanceQtmpParamInfos[i].ParameterType.FullNameQ;if QtmpParamInfos[i].ParameterType.FullName == "System.String"Q?/p>
{ tmpParams[i] = "Clark"Q}
//实例化对象object tmpObj = tmpConsInfo.InvokeQtmpParamsQ;Console.WriteLineQtmpObjQ;
//获取所有方法ƈ执行foreach QMethodInfo tmpMethod in tmpType.GetMethodsQ)(j)Q?/p>
{ //为方法的调用创徏参数集合tmpParamInfos = tmpMethod.GetParametersQ)(j)QtmpParams = new object[tmpParamInfos.Length]Qfor Qint i = 0Q?i < tmpParamInfos.LengthQ?i++Q?/p>
{ tmpParams[i] = tmpAss.CreateInstanceQtmpParamInfos[i].ParameterType.FullNameQ;if QtmpParamInfos[i].ParameterType.FullName == "System.String"Q?/p>
{ tmpParams[i] = "Clark Zheng"Q} if QtmpParamInfos[i].ParameterType.FullName == "System.Int32"Q?/p>
{ tmpParams[i] = 27Q} tmpMethod.InvokeQtmpObjQ?tmpParamsQ;}
//调用完方法后再次打印对象Q比较结果Console.WriteLineQtmpObjQ;}