1.(1)面向对象的语言具有__继承性_、_封装性_、_多态性 。
(2)能用foreach遍历访问的对象需要实现 _IEnumerable_接口或声明_GetEnumerator_方法的类型。
2.c#中的三元运算符是__? :__
3.当整数a赋值给一个object对象时,整数a将会被__装箱___
4.类成员有__3__种可访问形式
5.public static const int A=1;这段代码有错误么?是什么?
答:const(编译时的常量)成员都是static所以应该去掉static [readonly(运行时的常量)成员不是static如果想变静态需显示声明]
6.float f = -123.567F ; int I = (int)f ; i的值现在是_-123_
7.利用operator声明且仅声明了==,有什么错误么? 要同时修改Equale和GetHash() ?
答:重载了"==" 就必须重载 "!="
8.委托声明的关键字是___ delegate ___
9.用sealed修饰的类有什么特点?
答:(sealed密封类)不可被继承
10.在Asp.net中所有的自定义用户控件都必须继承自_System.Web.UI.UserControl_
11.在.Net中所有可序列化的类都被标记为__[serializable]__
12.在.Net托管代码中我们不用担心内存漏洞,这是因为有了_ gC _ (Garbage Collection (内存)垃圾回收)
13.下面的代码中有什么错误吗?
using System;
class A
{
public virtual void F(){ Console.WriteLine("A.F"); }
}
abstract class B:A
{
public abstract override void F();
}
答:abstract override 不可以一起修饰
14.当类T只声明了私有实例构造函数时,则在T的程序文本外部,_不可以_(可以 or 不可以)从T派生出新的类,_不可以_(可以 or 不可以)直接创建T的任何实例。
15.下面这段代码有错误么?
switch (i){
case():
CaseZero();
break;
case 1:
CaseOne();
break;
case 2:
dufault;
CaseTwo();
break;
}
答:case(): 错误
缺少default;
16.在.Net中,类System.Web.UI.Page 可以被继承么?
答:可以
17. 以下叙述正确的是: B C
A. 接口中可以有虚方法。B. 一个类可以实现多个接口。
C. 接口不能被实例化。 D. 接口中可以包含已实现的方法。
18. 从数据库读取记录,你可能用到的方法有:B C D
A. ExecuteNonQuery(用于执行操作) B. ExecuteScalar
C. Fill D. ExecuteReader
19. 对于一个实现了IDisposable接口的类,以下哪些项可以执行与释放或重置非托管资源相关的应用程序定义的任务?(多选) (ABC)
A.Close B.Dispose C.Finalize D.using E.Quit
20.以下关于ref和out的描述哪些项是正确的?(多选) (ACD)
A.使用ref参数,传递到ref参数的参数必须最先初始化。
B.使用out参数,传递到out参数的参数必须最先初始化。
C.使用ref参数,必须将参数作为ref参数显式传递到方法。
D.使用out参数,必须将参数作为out参数显式传递到方法。
21.在对SQL Server 数据库操作时应选用(A)。
a)SQL Server .NET Framework 数据提供程序;
b)OLE DB .NET Framework 数据提供程序;
c)ODBC .NET Framework 数据提供程序;
d)Oracle .NET Framework数据提供程序;
22.下列选项中,(C)是引用类型。
a)enum类型 b)struct类型 c)string类型 d)int类型
23.关于ASP.NET中的代码隐藏文件的描述正确的是(C)
a)Web窗体页的程序的逻辑由代码组成,这些代码的创建用于与窗体交互。编程逻辑唯一与用户界面不同的文件中。该文件称作为“代码隐藏”文件,如果用C#创建,该文件将具有“.ascx.cs”扩展名。
b)项目中所有Web窗体页的代码隐藏文件都被编译成.EXE文件。
c)项目中所有的Web窗体页的代码隐藏文件都被编译成项目动态链接库(.dll)文件。
d)以上都不正确。
24.以下描述错误的是(A)
a)在C++中支持抽象类而在C#中不支持抽象类。
b)C++中可在头文件中声明类的成员而在CPP文件中定义类的成员,在C#中没有头文件并且在同一处声明和定义类的成员。
c)在C#中可使用 new 修饰符显式隐藏从基类继承的成员。
d)在C#中要在派生类中重新定义基类的虚函数必须在前面加Override。
25.C#的数据类型有(B)
a)值类型和调用类型; b)值类型和引用类型;
c)引用类型和关系类型;d)关系类型和调用类型;
26.下列描述错误的是(D)
a)类不可以多重继承而接口可以;
b)抽象类自身可以定义成员而接口不可以;
c)抽象类和接口都不能被实例化;
d)一个类可以有多个基类和多个基接口;
27.在DOM中,装载一个XML文档的方法(D)
a)save方法 b)load方法 c)loadXML方法 d)send方法
28.下列关于构造函数的描述正确的是(C)
a)构造函数可以声明返回类型。
b)构造函数不可以用private修饰
c)构造函数必须与类名相同
d)构造函数不能带参数
29.以下是一些C#中的枚举型的定义,其中错误的用法有()
a)public enum var1{ Mike = 100, Nike = 102, Jike }
b)public enum var1{ Mike = 100, Nike, Jike }
c)public enum var1{ Mike=-1 , Nike, Jike }
d)public enum var1{ Mike , Nike , Jike }
30.int[][] myArray3=new int[3][]{new int[3]{5,6,2},new int[5]{6,9,7,8,3},new int[2]{3,2}};
myArray3[2][2]的值是(D)。
a)9 b)2 c)6 d)越界
31.接口是一种引用类型,在接口中可以声明(A),但不可以声明公有的域或私有的成员变量。
a)方法、属性、索引器和事件; b)方法、属性信息、属性;
c)索引器和字段; d)事件和字段;
32.ASP.NET框架中,服务器控件是为配合Web表单工作而专门设计的。服务器控件有两种类型,它们是(A )
a)HTML控件和Web控件 b)HTML控件和XML控件
c)XML控件和Web控件 d)HTML控件和IIS控件
33.ASP.NET中,在Web窗体页上注册一个用户控件,指定该控件的名称为”Mike”,正确的注册指令为( D)
a)<%@Register TagPrefix = “Mike” TagName = “Space 2 ” Src = “myX.ascx”%>
b)<%@Register TagPrefix = “Space 2 ” TagName = “Mike” Src = “myX.ascx”%>
c)<%@Register TagPrefix = “SpaceX” TagName = “Space 2 ” Src = “Mike”%>
d)以上皆非
34.在ADO.NET中,对于Command对象的ExecuteNonQuery()方法和ExecuteReader()方法,下面叙述错误的是(C)。
a)insert、update、delete等操作的Sql语句主要用ExecuteNonQuery()方法来执行;
b)ExecuteNonQuery()方法返回执行Sql语句所影响的行数。
c)Select操作的Sql语句只能由ExecuteReader()方法来执行;
d)ExecuteReader()方法返回一个DataReder对象;
35.下列ASP.NET语句(b)正确地创建了一个与SQL Server 2000数据库的连接。
a)SqlConnection con1 = new Connection(“Data Source = localhost; Integrated Security = SSPI; Initial Catalog = myDB”);
b)SqlConnection con1 = new SqlConnection(“Data Source = localhost; Integrated Security = SSPI; Initial Catalog = myDB”);
c)SqlConnection con1 = new SqlConnection(Data Source = localhost; Integrated Security = SSPI; Initial Catalog = myDB);
d)SqlConnection con1 = new OleDbConnection(“Data Source = localhost; Integrated Security = SSPI; Initial Catalog = myDB”);
36.Winform中,关于ToolBar控件的属性和事件的描述不正确的是(D)。
a)Buttons属性表示ToolBar控件的所有工具栏按钮
b)ButtonSize属性表示ToolBar控件上的工具栏按钮的大小,如高度和宽度
c)DropDownArrows属性表明工具栏按钮(该按钮有一列值需要以下拉方式显示)旁边是否显示下箭头键
d)ButtonClick事件在用户单击工具栏任何地方时都会触发
37.在ADO.NET中执行一个存储过程时,如果要设置输出参数则必须同时设置参数的方向和(B ),必要时还要设置参数尺寸。
a)大小; b)上限; c)初始值; d)类型;
38.如果将窗体的FormBoderStyle设置为None,则( B)。
a)窗体没有边框并不能调整大小; b)窗体没有边框但能调整大小;
c)窗体有边框但不能调整大小; d)窗体是透明的;
39.如果要将窗体设置为透明的,则( B)
a)要将FormBoderStyle属性设置为None;
b)要将Opacity属性设置为小于100%得值;
c)要将locked 属性设置为True;
d)要将 Enabled属性设置为True;
40.下列关于C#中索引器理解正确的是(B/C )
a)索引器的参数必须是两个或两个以上
b)索引器的参数类型必须是整数型
c)索引器没有名字
d)以上皆非
41.下面描述错误的是( C/D)。
a)窗体也是控件; b)窗体也是类; c)控件是从窗体继承来的; d)窗体的父类是控件类;
42.要对注册表进行操作则必须包含( D)。
a)System.ComponentModel命名空间; b)System.Collections命名空间;
c)System.Threading命名空间; d)Microsoft.Win32命名空间;
43.要创建多文档应用程序,需要将窗体的(D )属性设为true。
a)DrawGrid; b)ShowInTaskbar;
c)Enabled; d)IsMdiContainer;
44.如果设treeView1=new TreeView(),则treeView1.Nodes.Add("根节点")返回的是一个 ()类型的值。
a)TreeNode;
b)int;
c)string;
d)TreeView;
45.下面关于XML的描述错误的是(D)。
a)XML提供一种描述结构化数据的方法;
b)XML 是一种简单、与平台无关并被广泛采用的标准;
c)XML文档可承载各种信息;
d)XML只是为了生成结构化文档;
46.装箱、拆箱操作发生在: ( C )
A.类与对象之间 B.对象与对象之间
C.引用类型与值类型之间 D.引用类型与引用类型之间
47.用户类若想支持Foreach语句需要实现的接口是: ( A )
A.IEnumerable B.IEnumerator
C.ICollection D.ICollectData
48..Net Framework通过什么与COM组件进行交互操作?( C )
A.Side By Side B.Web Service
C.Interop D.PInvoke
49..Net依靠以下哪一项技术解决COM存在的Dll Hell问题的?( A )
A.Side By Side B.Interop
C.PInvoke D.COM+
50.装箱与拆箱操作是否是互逆的操作?( B )
A.是 B.否
51.以下哪个是可以变长的数组?( D )
A.Array B.string[]
C.string[N] D.ArrayList
52.用户自定义异常类需要从以下哪个类继承:( A )
A.Exception B.CustomException
C.ApplicationException D.BaseException
53.以下代码段中能否编译通过?请给出理由。
try
{
}
catch(FileNotFoundException e1)
{
}
catch(Exception e2)
{
}
catch(IOException e3)
{
}
catch
{
}
54.对于一个实现了IDisposable接口的类,以下哪些项可以执行与释放或重置非托管资源相关的应用程序定义的任务?(多选) ( ABC )
A.Close B.DisposeC.Finalize
D.using E.Quit
55.Net依赖以下哪项技术实现跨语言互用性?( C )
A.CLR(公共语言运行库编译)
B.CTS 公共类型系统CTS(Common Type System)
C.CLS(公共语言运行库提供内置的语言互用性支持。但是,这种支持不能保证您编写的代码能被使用另一种编程语言的开发人员使用。为了确保使用任何编程语言的开发人员都可以完全使用您开发的托管代码,已经定义了一组语言功能和使用这些功能的规则,名为公共语言规范 (CLS)。遵循这些规则和仅公开 CLS 功能的组件被认为是符合 CLS 的。)
D.CTT(.ctt)
56.请问: String类与StringBuilder类有什么区别?为什么在.Net类库中要同时存在这2个类?(简答)
如果要操作一个不断增长的字符串,尽量不用String类,改用StringBuilder类。两个类的工作原理不同:String类是一种传统的修改字符串的方式,它确实可以完成把一个字符串添加到另一个字符串上的工作没错,但是在.NET框架下,这个操作实在是划不来。因为系统先是把两个字符串写入内存,接着删除原来的String对象,然后创建一个String对象,并读取内存中的数据赋给该对象。这一来二去的,耗了不少时间。而使用System.Text命名空间下面的StringBuilder类就不是这样了,它提供的Append方法,能够在已有对象的原地进行字符串的修改,简单而且直接。当然,一般情况下觉察不到这二者效率的差异,但如果你要对某个字符串进行大量的添加操作,那么StringBuilder类所耗费的时间和String类简直不是一个数量级的。
57.以下哪个类是int的基类?( )
A.Int32 B.Object C.ValueType D.Int16
58.以下哪些可以作为接口成员?(多选) ( ABDE )
A.方法 B.属性 C.字段 D.事件 E.索引器
F.构造函数 G.析构函数
59.以下关于ref和out的描述哪些项是正确的?(多选) ( ACD )
A.使用ref参数,传递到ref参数的参数必须最先初始化。
B.使用out参数,传递到out参数的参数必须最先初始化。
C.使用ref参数,必须将参数作为ref参数显式传递到方法。
D.使用out参数,必须将参数作为out参数显式传递到方法。
60.“访问范围限定于此程序或那些由它所属的类派生的类型”是对以下哪个成员可访问性含义的正确描述?( B )
A.public B.protected
C.internal D.protected internal
61.class Class1
{
private static int count = 0;
static Class1()
{
count++;
}
public Class1()
{
count++;
}
}
Class1 o1 = new Class1();
Class1 o2 = new Class1();
请问,o1.Count的值是多少?( C )
A.1 B.2 C.3 D.4
62.abstract class BaseClass
{
public virtual void MethodA()
{
}
public virtual void MethodB()
{
}
}
class Class1: BaseClass
{
public void MethodA(string arg)
{
}
public override void MethodB()
{
}
}
class Class2: Class1
{
new public void MethodB()
{
}
}
class MainClass
{
public static void Main (string[] args)
{
Class2 o = new Class2();
Console.WriteLine(o.MethodA());
}
}
请问,o.MethodA调用的是: ( A )
A.BaseClass.MethodA
B.Class2.MethodA
C.Class1.MethodA
D.都不是
63.请叙述属性与索引器的区别。
属性 索引器
通过名称标识。 通过签名标识。
通过简单名称或成员访问来访问。 通过元素访问来访问。
可以为静态成员或实例成员。 必须为实例成员。
属性的 get 访问器没有参数。 索引器的 get 访问器具有与索引器相同的形参表。
属性的 set 访问器包含隐式 value 参数。 除了 value 参数外,索引器的 set 访问器还具有与索引器相同的形参表。
64.请叙述const与readonly的区别。
每一个class至多只可以定义一个static构造函数,并且不允许增加访问级别关键字,参数列必须为空。
为了不违背编码规则,通常把static数据成员声明为private,然后通过statci property提供读写访问。
const 关键字用于修改字段或局部变量的声明。它指定字段或局部变量的值不能被修改。常数声明引入给定类型的一个或多个常数。
const数据成员的声明式必须包含初值,且初值必须是一个常量表达式。因为它是在编译时就需要完全评估。
const成员可以使用另一个const成员来初始化,前提是两者之间没有循环依赖。
readonly在运行期评估赋值,使我们得以在确保“只读访问”的前提下,把object的初始化动作推迟到运行期进行。
readonly 关键字与 const 关键字不同: const 字段只能在该字段的声明中初始化。readonly 字段可以在声明或构造函数中初始化。因此,根据所使用的构造函数,readonly 字段可能具有不同的值。另外,const 字段是编译时常数,而 readonly 字段可用于运行时常数。
readonly 只能在声明时或者构造函数里面初始化,并且不能在 static 修饰的构造函数里面。
65.您需要创建一个ASP.NET应用程序,公司考虑使用Windows身份认证。
所有的用户都存在于AllWin这个域中。您想要使用下列认证规则来配置这个应用程序:
a、 匿名用户不允许访问这个应用程序。
b、 所有雇员除了Tess和King都允许访问这个应用程序。
请问您应该使用以下哪一个代码段来配置这个应用程序?( A )
A. <authorization>
<deny users=”allwin/tess, allwin/king”>
<allow users=”*”>
<deny users=”?”>
</authorization>
B. <authorization>
<allow users=”*”>
<deny users=”allwin/tess, allwin/king”>
<deny users=”?”>
</authorization>
C. <authorization>
<deny users=”allwin/tess, allwin/king”>
<deny users=”?”>
<allow users=”*”>
</authorization>
D. <authorization>
<allow users=”allwin/tess, allwin/king”>
<allow users=”*”>
</authorization>
E.<authorization>
<allow users=”*”>
<deny users=”allwin/tess, allwin/king”>
</authorization>
66.您要创建一个显示公司员工列表的应用程序。您使用一个DataGrid控件显示员工的列表。您打算修改这个控件以便在这个Grid的Footer显示员工合计数。请问您应该怎么做?( C? )
A.重写OnPreRender事件,当Grid的Footer行被创建时显示合计数。
B.重写OnItemCreated事件,当Grid的Footer行被创建时显示合计数。
C.重写OnItemDataBound事件,当Grid的Footer行被创建时显示合计数。
D. 重写OnLayout事件,当Grid的Footer行被创建时显示合计数。
67.您要创建ASP.NET应用程序用于运行AllWin公司内部的Web站点,这个应用程序包含了50个页面。您想要配置这个应用程序以便当发生一个HTTP代码错误时它可以显示一个自定义的错误页面给用户。您想要花最小的代价完成这些目标,您应该怎么做?(多选)( CD )
A.在这个应用程序的Global.asax文件中创建一个Application_Error过程去处理ASP.NET代码错误。
B.在这个应用程序的Web.config文件中创建一个applicationError节去处理ASP.NET代码错误。
C.在这个应用程序的Global.asax文件中创建一个CustomErrors事件去处理HTTP错误。
D.在这个应用程序的Web.config文件中创建一个CustomErrors节去处理HTTP错误。
E.在这个应用程序的每一页中添加一个Page指示符去处理ASP.NET 代码错误。
F. 在这个应用程序的每一页中添加一个Page指示符去处理ASP.NET HTTP错误。
68.您的公司有一个DB Server,名为AllWin,其上装了MS SQLSERVER 2000。现在需要您写一个数据库连接字符串,用以连接AllWin上SQL SERVER中的一个名为PubBase实例的Test库。请问,应该选择下面哪一个字符串?( B )
A. “Server=AllWin;Data Source=PubBase;Initial Catalog=Test;Integrated Security=SSPI”
B. “Server= AllWin;Data Source=PubBase;Database=Test;Integrated Security= SSPI”
C. “Data Source= AllWin /PubBase;Initial Category=PubBase;Integrated Security= SSPI”
D. “Data Source= AllWin / PubBase;Database=Test;Integrated Security= SSPI”
25.您为AllWin公司创建了一个ASP.NET应用程序。这个应用程序调用一个 Xml Web Service。这个 Xml Web Service 将返回一个包含了公司雇员列表的DataSet对象。请问您该如何在这个程序中使用这个 Xml Web Service?( ? )
A.在“引用”对话框的.Net标签中选择 System.Web.Services.dll。
B.在“Web引用”对话框中输入这个 XML Web service 的地址。
C.在您的 Global.asax.cs 中添加一条 using 语句并指定这个 XML Web service 的地址。
D.在您的 Global.asax.cs 中写一个事件处理器导入这个 Xml Web Service 相应的 .wsdl 和 .disco 文件。
69.您要创建一个ASP.NET应用程序在DataGrid控件中显示一个经过排序的列表。产品数据被存放于一个名为PubBase的Microsoft SQL Server 数据库。每个产品的主键是ProductID,Numeric型并且每个产品有一个字母描述字段,名为ProductName。您使用一个SqlDataAdapter对象和一个SqlCommand对象通过调用一个存储过程从数据库中获取产品数据。您将SqlCommand对象的CommandType属性设置为CommandType.StoredProcedure,并将它的CommandText属性设置为procProductList。您成功的获取了一个DataTable对象,其中是已经按ProductID降序排列的产品列表。您打算显示以相反的字母顺序排列的ProductName,请问该怎么做? ( B )
A. 将SqlCommand对象的CommandType属性修改为CommandType.Text,将CommandText属性修改为”SELECT * FROM procProductList ORDER BY ProductName DESC”。然后将这个DataTable对象绑定到DataGrid控件。
B. 创建一个基于这个DataTable对象的新的DataView并将这个DataView的Sort属性设置为“ProductName DESC”。然后将这个DataView对象绑定到DataGrid控件。
C. 将DataGrid控件的AllowSorting属性设置为True,并将DataGridColumn的SortExpression属性设置为 “ProductName DESC”.以显示ProductName。然后将这个DataTable对象绑定到DataGrid控件。
D. 将DataTable对象的DisplayExpression属性设置为 “ORDER BY ProductName DESC”.。然后将这个DataTable对象绑定到DataGrid控件。
70.C#代码实现,确保windows程序只有一个实例(instance)
///<summary>
///应用程序的主入口点。
///</summary>
[STAThread]
staticvoid Main ()
{
//防止程序多次运行
if(!OneInstance.IsFirst("GetPayInfo"))
{
MessageBox.Show ("警告:程序正在运行中! 请不要重复打开程序!可在右下角系统栏找到!","程序错误提示:",MessageBoxButtons.OK,MessageBoxIcon.Stop);
return;
}
Application.Run(new Form1());
}
// ******************* 防止程序多次执行 **************************
publicabstractclass OneInstance
{
///<summary>
///判断程序是否正在运行
///</summary>
///<param name="appId">程序名称</param>
///<returns>如果程序是第一次运行返回True,否则返回False</returns>
publicstaticbool IsFirst(string appId)
{
bool ret=false;
if(OpenMutex(0x 1F 0001,0,appId)==IntPtr.Zero)
{
CreateMutex(IntPtr.Zero,0,appId);
ret=true;
}
return ret;
}
[DllImport("Kernel32.dll",CharSet=CharSet.Auto)]
privatestaticextern IntPtr OpenMutex(
uint dwDesiredAccess, // access
int bInheritHandle, // inheritance option
string lpName // object name
);
[DllImport("Kernel32.dll",CharSet=CharSet.Auto)]
privatestaticextern IntPtr CreateMutex(
IntPtr lpMutexAttributes, // SD
int bInitialOwner, // initial owner
string lpName // object name
);
}
71. 简述 private、 protected、 public、 internal 修饰符的访问权限。
private : 私有成员, 在类的内部才可以访问。protected : 保护成员,该类内部和继承类中可以访问。public : 公共成员,完全公开,没有访问限制。internal: 在同一命名空间内可以访问。
72. 写出一条Sql语句:取出表A中第31到第40记录(SQLServer, 以自动增长的ID作为主键, 注意:ID可能不是连续的。)
select top 10 * from A where id not in (select top 30 id from A)
解2: select top 10 * from A where id > (select max(id) from (select top 30 id from A )as A)
73.列举ASP.NET 页面之间传递值的几种方式。
1.使用QueryString, 如....?id=1; response. Redirect()....
2.使用Session变量
3.使用Server.Transfer
74.请说明在.net中常用的几种页面间传递参数的方法,并说出他们的优缺点。
session(viewstate) 简单,但易丢失
application 全局
cookie 简单,但可能不支持,可能被伪造
input ttype="hidden" 简单,可能被伪造
url参数简单,显示于地址栏,长度有限
数据库稳定,安全,但性能相对弱
75.override与重载的区别
Override用来重写父类的方法,重载使用相同名的方法或操作符拥有不同类型的参数
76..net的错误处理机制是什么
.net错误处理机制采用try->catch->finally结构,发生错误时,层层上抛,直到找到匹配的Catch为止。
77.C#中接口和类的异同
接口和类都是类,不同的事,接口只包含方法或属性的声明,不包含具体实现方法的代码,接口可以实现多继承,而类只能是单继承,继承接口的类必须实现接口中声明的方法或属性。接口主要定义一种规范,统一调用方法,在大型项目中接口正发挥日益重要的作用。
78.DataReader和DataSet的异同
DataReader和DataSet最大的区别在于,DataReader使用时始终占用SqlConnection,在线操作数据库..任何对SqlConnection的操作都会引发DataReader的异常..因为DataReader每次只在内存中加载一条数据,所以占用的内存是很小的..因为DataReader的特殊性和高性能.所以DataReader是只进的..你读了第一条后就不能再去读取第一条了..
DataSet则是将数据一次性加载在内存中.抛弃数据库连接..读取完毕即放弃数据库连接..因为DataSet将数据全部加载在内存中.所以比较消耗内存...但是确比DataReader要灵活..可以动态的添加行,列,数据.对数据库进行回传更新操作...
79.在c#中using和new这两个关键字有什么意义,请写出你所知道的意义?
Using 引入一个名子空间,或在使用了一个对像后自动调用其IDespose,New 实例化一个对像,或修饰一个方法,表此方法完全重写此方法
80.在下面的例子里
using System;
class A
{
public A(){
PrintFields();
}
public virtual void PrintFields(){}
}
class B:A
{
int x=1;
int y;
public B(){
y=-1;
}
public override void PrintFields(){
Console.WriteLine("x={0},y={1}",x,y);
}
当使用new B()创建B的实例时,产生什么输出?X=1,Y=0
3.下面的例子中
using System;
class A
{
public static int X;
static A(){
X=B.Y+1;
}
}
class B
{
public static int Y=A.X+1;
static B(){}
static void Main(){
Console.WriteLine("X={0},Y={1}",A.X,B.Y);
}
}
产生的输出结果是什么?x=1,y=2
4.谈谈类和结构的区别?
最大区别一个是引用类型,一个是值类型 默认成员访问为public是另外一个区别
1. 在.net(C# or vb.net)中如何获得当前窗体或控件的句柄,特别是控件本身的句柄(请列举)。
this(C#) Me(vb.net).
2在.net(C# or vb.net)中如何用户自定义消息,并在窗体中处理这些消息。
在form中重载DefWndProc函数来处理消息:
protected override void DefWndProc ( ref System.WinForms.Message m )
{
switch(m.msg)
{
case WM_Lbutton :
///string与MFC中的CString的Format函数的使用方法有所不同
string message = string.Format("收到消息!参数为:{0},{1}",m.wParam,m.lParam);
MessageBox.Show(message);///显示一个消息框
break;
case USER:
处理的代码
default:
base.DefWndProc(ref m);///调用基类函数处理非自定义消息。
break;
}
}
3. 在.net(C# or vb.net)如何启动另一个程序。process
4. 在.net(C# or vb.net)中如何取消一个窗体的关闭
private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel=true;
}
5. 在.net(C# or vb.net)中,Appplication.Exit 还是 Form.Close有什么不同?
答案:一个是退出整个应用程序,一个是关闭其中一个form
6. 在C#中有一个double型的变量,比如10321.5,比如122235401.21644,作为货币的值如何按各个不同国家的习惯来输出。比如美国用$10,321.50和$122,235,401.22而在英国则为£10 321.50和£122 235 401.22
答案:
System.Globalization.CultureInfo MyCulture = new System.Globalization.CultureInfo("en-US");
//System.Globalization.CultureInfo MyCulture = new System.Globalization.CultureInfo("en-GB");为英国货币类型
decimal y = 9999999999999999999999999999m;
string str = String.Format(MyCulture,"My amount = {0:c}",y);
7. 某一密码仅使用K、L、M、N、O共5个字母,密码中的单词从左向右排列,密码单词必须遵循如下规则:
(1) 密码单词的最小长度是两个字母,可以相同,也可以不同
(2) K不可能是单词的第一个字母
(3) 如果L出现,则出现次数不止一次
(4) M不能使最后一个也不能是倒数第二个字母
(5) K出现,则N就一定出现
(6) O如果是最后一个字母,则L一定出现
问题一:下列哪一个字母可以放在LO中的O后面,形成一个3个字母的密码单词?
A) K B)L C) M D) N
答案:B
问题二:如果能得到的字母是K、L、M,那么能够形成的两个字母长的密码单词的总数是多少?
A)1个 B)3个 C)6个 D)9个
答案:A
问题三:下列哪一个是单词密码?
A) KLLN B) LOML C) MLLO D)NMKO
答案:C
8. 62-63=1 等式不成立,请移动一个数字(不可以移动减号和等于号),使得等式成立,如何移动?
答案:62移动成2的6次方
new有几种用法
第一种:new Class();
第二种:覆盖方法
public new XXXX(){}
第三种:new 约束指定泛型类声明中的任何类型参数都必须有公共的无参数构造函数。
2.如何把一个array复制到arrayList里
foreach( object o in array )arrayList.Add(o);
3.datagrid.datasouse可以连接什么数据源 [dataset,datatable,dataview]
dataset,datatable,dataview , IList
4.概述反射和序列化
反射:程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性
序列化:序列化是将对象转换为容易传输的格式的过程。例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象。在另一端,反序列化将从该流重新构造对象。
5.概述o/r mapping 的原理
利用反射,配置 将类于数据库表映射
7.用sealed修饰的类有什么特点
sealed 修饰符用于防止从所修饰的类派生出其它类。如果一个密封类被指定为其它类的基类,则会发生编译时错误。
密封类不能同时为抽象类。
sealed 修饰符主要用于防止非有意的派生,但是它还能促使某些运行时优化。具体说来,由于密封类永远不会有任何派生类,所以对密封类的实例的虚拟函数成员的调用可以转换为非虚拟调用来处理。
11.详述.NET里class和struct的异同!
class:放在 ? struct放在?
struct值传递
类与结构有很多相似之处:结构可以实现接口,并且可以具有与类相同的成员类型。然而,结构在几个重要方面不同于类:结构为值类型而不是引用类型,并且结构不支持继承。结构的值存储在“在堆栈上”或“内联”。细心的程序员有时可以通过聪明地使用结构来增强性能。
12.概述.NET里对 remoting 和 webservice 两项技术的理解和实际中的应用。
远程逻辑调用,remoing接口只能用在.net中
13.什么是code-behind技术 aspx and cs
14.概述三层结构体系 web/business/dataaccess
15.asp.net如何实现MVC模式,举例说明! web/business/dataaccess
2.什么是ASP.net中的用户控件
答:用户控件就是.ascx扩展名的东西喽,可以拖到不同的页面中调用,以节省代码.比如登陆可能在多个页面上有,就可以做成用户控件,但是有一个问题就是用户控件拖到不同级别的目录下后里面的图片等的相对路径会变得不准确,需要自已写方法调整.
3.什么叫应用程序域?什么是受管制的代码?什么是强类型系统?什么是装箱和拆箱?什么是重载?CTS、CLS和CLR分别作何解释?
答:装箱就是把值类型转成引用类型,从MS IL角度看好像是boxing,没记错的话是把值从堆栈转到堆中.拆箱相反,重载就是指一个方法名同,参数个数不同,返回值可以相同的方法.CLR是通用语言运行时,其它的不清楚.
4.列举一下你所了解的XML技术及其应用
答:XML可是好东西,保存配置,站与站之间的交流,WEB SERVICE都要用它.
5.值类型和引用类型的区别?写出C#的样例代码。
答:结构是值类型,类是引用类型,所以传结构就是值类型的应用啦,传对象或类就是引用类型的,这个不用多写了吧.
6.ADO.net中常用的对象有哪些?分别描述一下。
答:connection command sqladapter dataset datatable dataview等等.写不完了.
7.如何理解委托?
答:据说相当于函数指针,定义了委托就可以在不调用原方法名称的情况下调用那个方法.
msdn2005中是这样解释的:
委托具有以下特点:
委托类似于 C++ 函数指针,但它是类型安全的。
委托允许将方法作为参数进行传递。
委托可用于定义回调方法。
委托可以链接在一起;例如,可以对一个事件调用多个方法。
方法不需要与委托签名精确匹配。有关更多信息,请参见协变和逆变。
C# 2.0 版引入了匿名方法的概念,此类方法允许将代码块作为参数传递,以代替单独定义的方法。
8.C#中的接口和类有什么异同。
答:接口是负责功能的定义,项目中通过接口来规范类,操作类以及抽象类的概念!
而类是负责功能的具体实现!
在类中也有抽象类的定义,抽象类与接口的区别在于:
抽象类是一个不完全的类,类里面有抽象的方法,属性,也可以有具体的方法和属性,需要进一步的专业化。
但接口是一个行为的规范,里面的所有东西都是抽象的!
一个类只可以继承一个基类也就是父类,但可以实现多个接口
9.。net中读写数据库需要用到哪些类?他们的作用
答:这个类自已可以写的啊,你是指基类吗?那configuration,sqlconnection,sqlcommand等都要用到.
10.UDP连接和TCP连接的异同。
答:前者只管传,不管数据到不到,无须建立连接.后者保证传输的数据准确,须要连结.
11.ASP.net的身份验证方式有哪些?分别是什么原理?
答:form认证,windows集成认证等,原理不清楚.
13.什么是code-Behind技术。
答:代码分离,这是个明智的东西,像ASP这样混成一堆很不爽.或者可以理解成HTML代码写在前台,C#代码写在后台.当然前台也有脚本,类的调用等,其实写在一起也是可以的.
15..net中读写XML的类都归属于哪些命名空间?
答:System.Xml
16.解释一下UDDI、WSDL的意义及其作用。
答:
17.什么是SOAP,有哪些应用。
答:SOAP(Simple Object Access Protocol )简单对象访问协议是在分散或分布式的环境中交换信息并执行远程过程调用的协议,是一个基于XML的协议。使用SOAP,不用考虑任何特定的传输协议(最常用的还是HTTP协议),可以允许任何类型的对象或代码,在任何平台上,以任何一直语言相互通信。这种相互通信采用的是XML格式的消息,具体请看:http://playist.blogchina.com/2521621.html
20.常用的调用webservice方法有哪些?
答:
可以从浏览器、ASP页或其他WEB服务调用可以使用HTTP-GET HTTP-POST访问WEB服务也可以从ASP页或其他WEB服务向其他WEB服务发出SOAP请求HTTP-GET HTTP-POST SOAP 使用WEB服务代理
6. 私有程序集与共享程序集有什么区别?
一个私有程序集通常为单个应用程序所使用,并且存储于这个应用程序所在的目录之中,或此目录下面的一个子目录中。共享程序集通常存储在全局程序集缓存(Global Assembly Cache)之中,这是一个由.NET运行时所维护的程序集仓库。共享程序集通常是对许多应用程序都有用的代码库,比如.NET Framework类。
7. 请解释进程与线程的区别?进程与程序的区别?
一般,一个应用程序对应于一个或多个进程,可以把进程看作是该应用程序在*作系统中的标识;而一个进程通常由多个线程组成,而线程是*作系统为该应用程序分配处理时间的最小单元。
8. CLR与IL分别是什么含义?
CLR:公共语言运行时,类似于Java中的JVM,Java虚拟机;在.Net环境下,各种编程语言使用一种共同的基础资源环境,这就是CLR,CLR将直接与*作系统进行通信,而编程语言如C#.NET将尽量避免直接与*作系统直接通信,加强了程序代码的执行安全性,可以这样看:CLR就是具体的编程语言如:C#.NET与*作系统之间的翻译,同时它为具体的编程语言提供了许多资源:
IL,中间语言,也称MSIL,微软中间语言,或CIL,通用中间语言;所有.NET源代码(不管用哪种语言编写)在进行编译时都被编译成IL。在应用程序运行时被即时(Just-In-Time,JIT)编译器处理成为机器码,被解释及执行。
10 .请解释ASP。NET中以什么方式进行数据验证
Aps.net 中有非空验证,比较验证,取值范围验证,正则表达式验证及客户自定义验证五大控件,另还有一个集中验证信息处理控件
11. WEB控件可以激发服务端事件,请谈谈服务端事件是怎么发生并解释其原理?自动传回是什么?为什么要使用自动传回。
在web控件发生事件时,客户端采用提交的形式将数据交回服务端,服务端先调用Page_Load事件,然后根据传回的状态信息自动调用服务端事件自动传回是当我们在点击客户端控件时,采用提交表单的形式将数据直接传回到务端
只有通过自动传回才能实现服务端事件的机制,如果没有自动回传机制就只能调用客户端事件,而不能调用服务端事件
12. WEB控件及HTML服务端控件能否调用客户端方法?如果能,请解释如何调用?
可以调用
例如:<asp:TextBox id="TextBox1" onclick="clientfunction();" runat="server">
</asp:TextBox>
<INPUT id="Button2" value="Button" name="Button2"
runat="server" onclick="clientfunction();">
13. 请解释web.config文件中的重要节点
appSettings包含自定义应用程序设置。
system.web 系统配置
compilation动态调试编译设置
customErrors自定义错误信息设置
authentication身份验证,此节设置应用程序的身份验证策略。
authorization授权, 此节设置应用程序的授权策略.
14. 请解释ASP。NET中的web页面与其隐藏类之间的关系?
一个ASP.NET页面一般都对应一个隐藏类,一般都在ASP.NET页面的声明中指定了隐藏类例如一个页面Tst1.aspx的页面声明如下
<%@ Page language="c#" Codebehind="Tst1.aspx.cs" AutoEventWireup="false" Inherits="T1.Tst1" %>
Codebehind="Tst1.aspx.cs" 表明经编译此页面时使用哪一个代码文件
Inherits="T1.Tst1" 表用运行时使用哪一个隐藏类
15. 什么是viewstate,能否禁用?是否所用控件都可以禁用?
Viewstate是保存状态的一种机制,EnableViewState属性设置为false即可禁用
16. 当发现不能读取页面上的输入的数据时很有可能是什么原因造成的?怎么解决
很有可能是在Page_Load中数据处理时没有进行Page的IsPostBack属性判断
17. 请解释什么是上下文对象,在什么情况下要使用上下文对象
上下文对象是指HttpContext类的Current 属性,当我们在一个普通类中要访问内置对象(Response,Request,Session,Server,Appliction等)时就要以使用此对象
18. 请解释转发与跳转的区别?
转发就是服务端的跳转A页面提交数据到B页面,B页面进行处理然后从服务端跳转到其它页面
跳转就是指客户端的跳转
19.请简述一下用Socket进行同步通讯编程的详细步骤
1、在应用程序和远程设备中使用协议和网络地址初始化套接字
2、在应用程序中通过指定端口和地址建立监听
3、远程设备发出连接请求
4、应用程序接受连接产生通信scoket
5、应用程序和远程设备开始通讯(在通讯中应用程序将挂起直到通讯结束)
6、通讯结束,关闭应用程序和远程设备的Socket回收资源
1、在C#中,string str = null 与 string str = “” 请尽量使用文字或图象说明其中的区别。
string str = null 是不给他分配内存空间,而string str = “”给它分配长度为空字符窜的内存空间.
2.请详述在dotnet中类(class)与结构(struct)的异同:(10分)
Class可以被实例化,属于引用类型,是分配在内存的堆上的,Struct属于值类型,是分配在内存的栈上的.
3、根据委托(delegate)的知识,请完成以下用户控件中代码片段的填写:(10)
namespace test
{
public delegate void OnDBOperate();
public class UserControlBase : System.Windows.Forms.UserControl
{
public event OnDBOperate OnNew;
privatevoidtoolBar_ButtonClick(objectsender,System.Windows.Forms.ToolBarButtonClickEventArgs e)
{
if(e.Button.Equals(BtnNew))
{
//请在以下补齐代码用来调用OnDBOperate委托签名的OnNew事件。
}
}
}
}
____________________________________________________________________
if( OnNew != null )
OnNew( this, e );
4、分析以下代码,完成填空(10分)
string strTmp = "abcdefg某某某";
int i= System.Text.Encoding.Default.GetBytes(strTmp).Length;
int j= strTmp.Length;
以上代码执行完后,i= j= i还真的不知道, j=10
5、SQLSERVER服务器中,给定表 table1 中有两个字段 ID、LastUpdateDate,ID表示更新的事务号, LastUpdateDate表示更新时的服务器时间,请使用一句SQL语句获得最后更新的事务号。(10)
select top ID from table1 order by LastUpdateData desc
8、简要谈一下您对微软.NET 构架下remoting和webservice两项技术的理解以及实际中的应用。(10)
remoting是.net 中用来跨越machine, process, appdomain 进行方法调用的技术,对于三成结构的程序,就可以使用remoting技术来构建.它是分布应用的基础技术.相当于以前的DCOM Web Service是一种构建应用程序的普通模型,并能在所有支持internet网通讯的操作系统上实施。Web Service令基于组件的开发和web的结合达到最佳,基于组件的对象模型
9.什么叫做SQL注入,如何防止?请举例说明。
利用sql关键字对网站进行攻击。过滤关键字'等
所谓SQL注入(SQL Injection),就是利用程序员对用户输入数据的合法性检测不严或不检测的特点,故意从客户端提交特殊的代码,从而收集程序及服务器的信息,从而获取想得到的资料。
http://localhost/lawjia/show.asp?ID=444 and user>0,这时,服务器运行Select * from 表名 where 字段=444 and user>0这样的查询,当然,这个语句是运行不下去的,肯定出错,错误信息如下:
·错误类型:
Microsoft OLE DB Provider for ODBC Drivers (0x80040E07)
[Microsoft][ODBC SQL Server Driver][SQL Server]将 nvarchar 值 'sonybb' 转换为数据类型为 int 的列时发生语法错误。
a 产生一个int数组,长度为100,并向其中随机插入1-100,并且不能重复。
int[] intArr=new int[100];
ArrayList myList=new ArrayList();
Random rnd=new Random();
while(myList.Count<100)
{
int num=rnd.Next(1,101);
if(!myList.Contains(num))
myList.Add(num);
}
for(int i=0;i<100;i++)
intArr[i]=(int)myList[i];
20.请叙述类与结构的区别。
1)、结构是值类型;
2)、结构不支持继承;
3)、结构不能定义默认的构造函数;
4)、结构不能定义析构函数;
5)、结构不能使用初始值设置域值。
C#使用_多线程处理
1. 创建一个线程
using System;
using System.Threading;
namespace ConsoleApplication1
{
class App
{
public void run()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i.ToString());
}
}
static void Main(string[] args)
{
Console.WriteLine("App Start");
App app = new App();
ThreadStart ts = new ThreadStart(app.run);
Thread t = new Thread(ts);
t.Start();
Console.WriteLine("App end");
}
}
}
2. 为线程设置名字
Thread有Name属性,可以设置它的名字。
3. Sleep函数
Sleep函数被调用时,该线程所占用的系统资源,一般来说是CPU,会被释放掉,从而其他线程可以得到一些资源以进行响应的操作。
4. 设置线程优先级
可以通过设置Priority属性来控制线程的优先级。但是我们对线程的运行次序永远无法做到真正控制,因为这个控制权主要在于操作系统。我们只能在整体上使一个线程获得更多或更少的运行机会而已。而并不是说在任何一个特定的时刻,一个高优先级的线程就一定会先于一个低优先级的线程被运行。
5. 线程的后台与前台运行
所谓运行在后台是说该线程与产生它的父进程紧密联系在一起,它隐藏在父进程的后面,所以一旦父进程终止运行,该线程本身也会被立即终止,不管线程是否已经真正结束。相反,一个运行在前端的线程却是独立于产生它的父进程,一旦该进程开始运行,即使父进程已经终止运行,这个线程却会照样独立运行,直到运行结束或者被其他程序强制终止。C#中的一个线程的默认设置为前台运行。
6. 终止一个进程
6.1我们可以使用Abort方法来强行终止一个线程
6.2 一个更好的方法是通过设置一个标志来告诉线程自动终止,程序如下
using System;
using System.Threading;
namespace ConsoleApplication1
{
class App
{
private bool bContinue = true;
public void run()
{
Thread t = Thread.CurrentThread;
for (int i = 0; i < 10; i++)
{
Console.WriteLine(t.Name + ": " + i.ToString());
Thread.Sleep(1);
if (!bContinue)
{
return;
}
}
}
static void Main(string[] args)
{
Console.WriteLine("App Start");
App app = new App();
ThreadStart ts1 = new ThreadStart(app.run);
Thread t1 = new Thread(ts1);
t1.Name = "T1";
t1.Start();
Thread.Sleep(5);
app.bContinue = false;
Console.WriteLine("App end");
}
}
}
7. 线程的暂停与再运行
使用Suspend方法,我们可以暂停一个线程,如果要重新激活它,则使用Resume方法。
8. 线程Join函数
在激活一个线程后,我们可以等待该线程运行结束,或者设置一个最长的等待时间。只有等线程自动结束或者已经运行超过了前面预先设定的时间后,才去接着运行后面的程序。
using System;
using System.Threading;
namespace ConsoleApplication1
{
class App
{
public void run()
{
Thread t = Thread.CurrentThread;
for (int i = 0; i < 10; i++)
{
Console.WriteLine(t.Name + ": " + i.ToString());
Thread.Sleep(10);
}
}
static void Main(string[] args)
{
Console.WriteLine("App Start");
App app = new App();
ThreadStart ts1 = new ThreadStart(app.run);
ThreadStart ts2 = new ThreadStart(app.run);
Thread t1 = new Thread(ts1);
Thread t2 = new Thread(ts2);
t1.Name = "T1";
t2.Name = "T2";
t1.Start();
t1.Join(40);
t2.Start();
t2.Join();
Console.WriteLine("App end");
}
}
}
在这个程序中,T1运行40微秒后,T2才有机会运行,而主程序则是一直等待T2结束后,才去运行最后一个打印语句。
9. 线程加锁
线程加锁可以放置同一时刻,多个线程对资源的争夺。使它们能够正确的访问资源。
using System;
using System.Threading;
namespace ConsoleApplication1
{
class App
{
public int i = 0;
public int j = 0;
public int Limit = 100;
public void Increase()
{
while (i < Limit)
{
i++;
Thread.Sleep(1);
j++;
}
}
public void Check()
{
while (i < Limit)
{
if (i != j)
{
Console.WriteLine("i != j, i = {0}, j = {1}", i, j);
Thread t = Thread.CurrentThread;
t.Abort();
}
Thread.Sleep(1);
}
}
static void Main(string[] args)
{
App app = new App();
Thread t1 = new Thread(new ThreadStart(app.Increase));
Thread t2 = new Thread(new ThreadStart(app.Increase));
Thread t3 = new Thread(new ThreadStart(app.Check));
t1.Start();
t2.Start();
t3.Start();
}
}
}
在这个例子中,我们看到,每当i被加1时,j也总是被同时加1,所以看来i与j应该永远相等,可事实上,我们得到如下结果:
i != j, i = 2, j = 0
原因在于,i与j的累加和i与j的检测发生在两个不同的线程中。可能发生的情况是,在一愕嘎线程中i刚被累加过,但是j还没有;或者是前两个线程互相覆盖了累加的结果(假设在某一点上,线程A与线程B同时读取i的值为5,此时,j的值也是5,但两个线程都还没有去读取它,然后线程A将i加上1,即i的值变为6,可是同样的,线程B又在5的基础上将i加1,再次将i设置为6,所以i最终被设置为6。但是两个线程对j的读取却是错开进行的,也就是说当线程A将j的值改变为6后,线程B才去读取,从而发现j的值为6,并在此基础上将其加1,最终将j的值设定为7)。这样在测试程序中,便发现i与j的值不等了。这便是线程之间的资源共享问题。
C#为解决资源共享问题引进了Monitor的概念,也就是说,在某个线程要运行一段程序前先得获取一个监控器,从而通知其他线程不可能同时再运行这同一段程序。比如,上面的例子中的Increase函数可以修改如下:
public void Increase()
{
while (i < Limit)
{
try
{
Monitor.Enter(this);
i++;
Thread.Sleep(1);
j++;
}
finally
{
Monitor.Exit(this);
}
}
}
这样便解决了两个累加线程之间相互覆盖的问题。可是这样i与j还是可能不等,我们必须给检测函数也加一个监控器。这样无论何时,ij总是相等的了。
再实际应用中,我们总是将Monitor.Enter与Monitor.Exit同时运用,C#又提供了lock语句来实现对一段程序的加锁,因而程序可以简化为
public void Increase()
{
while (i < Limit)
{
lock (this)
{
i++;
Thread.Sleep(1);
j++;
}
}
}
同样要lock住Check函数。
注意:虽然我们对这两个函数加了锁,但是我们还是不能保证任何适合ij的值在任何程序中都是相等的。比如我们添加一个函数,然后添加一个线程。ij就可能不等了。所以,最彻底的解决办法是,对i和j的访问加锁。最终的程序如下:
using System;
using System.Threading;
namespace ConsoleApplication1
{
class App
{
public int i = 0;
public int j = 0;
public int Limit = 100;
public void Increase()
{
while (i < Limit)
{
lock (this)
{
i++;
Thread.Sleep(1);
j++;
}
}
}
public int I
{
get
{
lock (this)
{
return i;
}
}
set
{
lock (this)
{
i = value;
}
}
}
public int J
{
get
{
lock (this)
{
return j;
}
}
set
{
lock (this)
{
j = value;
}
}
}
public void Check()
{
while (I < Limit)
{
if (I != J)
{
Console.WriteLine("I != J, I = {0}, J = {1}", I, J);
Thread t = Thread.CurrentThread;
t.Abort();
}
Thread.Sleep(1);
}
}
static void Main(string[] args)
{
App app = new App();
Thread t1 = new Thread(new ThreadStart(app.Increase));
Thread t2 = new Thread(new ThreadStart(app.Increase));
Thread t3 = new Thread(new ThreadStart(app.Check));
t1.Start();
t2.Start();
t3.Start();
}
}
}
在C#中编写多线程应用程序,简单!
以前在使用VB来实现多线程的时候,发现有一定的难度。虽然也有这样那样的方法,但都不尽人意,但在C#中,要编写多线程应用程序却相当的简单。这篇文章将作简要的介绍,以起到抛砖引玉的作用!
.NET将关于多线程的功能定义在System.Threading名字空间中。因此,要使用多线程,必须先声明引用此名字空间(using System.Threading;)。
即使你没有编写多线程应用程序的经验,也可能听说过“启动线程”“杀死线程”这些词,其实除了这两个外,涉及多线程方面的还有诸如“暂停线程”“优先级”“挂起线程”“恢复线程”等等。下面将一个一个的解释。
a.启动线程
顾名思义,“启动线程”就是新建并启动一个线程的意思,如下代码可实现:
Thread thread1 = new Thread(new ThreadStart( Count));
其中的 Count 是将要被新线程执行的函数。
b.杀死线程
“杀死线程”就是将一线程斩草除根,为了不白费力气,在杀死一个线程前最好先判断它是否还活着(通过 IsAlive 属性),然后就可以调用 Abort 方法来杀死此线程。
c.暂停线程
它的意思就是让一个正在运行的线程休眠一段时间。如 thread.Sleep(1000); 就是让线程休眠1秒钟。
d.优先级
这个用不着解释了。Thread类中有一个ThreadPriority属性,它用来设置优先级,但不能保证操作系统会接受该优先级。一个线程的优先级可分为5种:Normal, AboveNormal, BelowNormal, Highest, Lowest。具体实现例子如下:
thread.Priority = ThreadPriority.Highest;
e.挂起线程
Thread类的Suspend方法用来挂起线程,知道调用Resume,此线程才可以继续执行。如果线程已经挂起,那就不会起作用。
if (thread.ThreadState = ThreadState.Running)
{
thread.Suspend();
}
f.恢复线程
用来恢复已经挂起的线程,以让它继续执行,如果线程没挂起,也不会起作用。
if (thread.ThreadState = ThreadState.Suspended)
{
thread.Resume();
}
下面将列出一个例子,以说明简单的线程处理功能。此例子来自于帮助文档。
using System;
using System.Threading;
// Simple threading scenario: Start a static method running
// on a second thread.
public class ThreadExample {
// The ThreadProc method is called when the thread starts.
// It loops ten times, writing to the console and yielding
// the rest of its time slice each time, and then ends.
public static void ThreadProc() {
for (int i = 0; i < 10; i++) {
Console.WriteLine("ThreadProc: {0}", i);
// Yield the rest of the time slice.
Thread.Sleep(0);
}
}
public static void Main() {
Console.WriteLine("Main thread: Start a second thread.");
// The constructor for the Thread class requires a ThreadStart
// delegate that represents the method to be executed on the
// thread. C# simplifies the creation of this delegate.
Thread t = new Thread(new ThreadStart(ThreadProc));
// Start ThreadProc. On a uniprocessor, the thread does not get
// any processor time until the main thread yields. Uncomment
// the Thread.Sleep that follows t.Start() to see the difference.
t.Start();
//Thread.Sleep(0);
for (int i = 0; i < 4; i++) {
Console.WriteLine("Main thread: Do some work.");
Thread.Sleep(0);
}
Console.WriteLine("Main thread: Call Join(), to wait until ThreadProc ends.");
t.Join();
Console.WriteLine("Main thread: ThreadProc.Join has returned. Press Enter to end program.");
Console.ReadLine();
}
}
此代码产生的输出类似如下内容:
Main thread: Start a second thread.
Main thread: Do some work.
ThreadProc: 0
Main thread: Do some work.
ThreadProc: 1
Main thread: Do some work.
ThreadProc: 2
Main thread: Do some work.
ThreadProc: 3
Main thread: Call Join(), to wait until ThreadProc ends.
ThreadProc: 4
ThreadProc: 5
ThreadProc: 6
ThreadProc: 7
ThreadProc: 8
ThreadProc: 9
Main thread: ThreadProc.Join has returned. Press Enter to end program.
socket编程
http://www.aspcool.com/lanmu/browse1.asp?ID=904&bbsuser=csharp
Socket编程基础
本章以Berkeley Socket为主,主要介绍网络编程时常用的调用和程序使用它们的方法及基本结构。网络编程有两种主要的编程接口,一种是Berkeley UNIX(BSD UNIX)的socket编程接口,另一种是AT&T的TLI接口(用于UNIXSYSV)。
1 、TCP/IP 基础知识
这里先假定读者对ISO的OSI七层模型已有了一定的了解,下面我们来看看TCP/IP模型。ISO的OSI对服务、接口和协议的概念区别十分明了,但它却没有真正的用户群。TCP/IP模型对服务、接口和协议的概念区别不象OSI模型那样明晰,但很实用。TCP/IP模型分为四层,对应于OSI七层模型如下图所示:图6-1 TCP/IP参考模型与OSI模型的近似对应关系在TCP/IP模型中,互联网层是基于无连接互联网络层的分组交换网络。在这一层中主机可以把报文(Packet)发往任何网络,报文独立地传向目标。互联网层定义了报文的格式和协议,这就是IP协议族(Internet Protocol)。互联网层的功能是将报文发送到目的地,主要的设计问题是报文路由和避免阻塞。互联网层上面是传输层,该层的主要功能和OSI模型的该层一样,主要使源和目的主机之间可以进行会话。该层定义了两个端到端的协议,一个是面向连接的传输控制协议TCP,另一个是无连接的用户数据报协议UDP。TCP/IP协议模型中没有会话层和表示层。传输层之上是应用层,它包含所有的高层协议,如远程虚拟终端协议TELNET、文件传输协议FTP、简单邮件传输协议SMTP等。这些高层协议中常见的如TELNET协议,用来允许用户远程登录到另一台UNIX机器;FTP协议用来传输文件,常见的有WU-FTP(Washington University的FTP服务器端程序,是一个免费程序);SMTP协议用来传送email,常见的服务器端程序有netscape等公司制作的程序,也有免费使用的sendmail程序;还有域名系统服务DNS协议,新闻组传送协议NNTP,用于WWW的超文本传输协议HTTP等。主机到网络这一层,在TCP/IP模型中没有详细定义,这里不作介绍。
2、 Socket一般描述
由于越来越多的计算机厂商,特别是工作站制造商如Sun等公司采用了Berkeley UNIX,socket接口被广泛采用,以至于现在,socket接口被广泛认可并成为了事实上的工业标准。目前的SYSV、BSD、OSF都将socket接口作为系统的一部分。当时设计如何支持TCP/IP协议时,有两种加入函数的方法,一种是直接加入支持TCP/IP协议的调用,另一种是加入支持一般网络协议的函数,而用参数来指定支持TCP/IP协议。Berkeley采用了后者,这样可以支持多协议族,TCP/IP是协议族之一(PF_INET)。
2.1 socket 描述符
前面已经提到过,在UNIX中,进程要对文件进行操作,一般使用open调用打开一个文件进行访问,每个进程都有一个文件描述符表,该表中存放打开的文件描述符。用户使用open等调用得到的文件描述符其实是文件描述符在该表中的索引号,该表项的内容是一个指向文件表的指针。应用程序只要使用该描述符就可以对指定文件进行操作。同样,socket接口增加了网络通信操作的抽象定义,与文件操作一样,每个打开的socket都对应一个整数,我们称它为socket描述符,该整数也是socket描述符在文件描述符表中的索引值。但socket描述符在描述符表中的表项并不指向文件表,而是指向一个与该socket有关的数据结构。BSD UNIX中新增加了一个socket调用,应用程序可以调用它来新建一个socket描述符,注意进程用open只能产生文件描述符,而不能产生socket描述符。socket调用只能完成建立通信的部分工作,一旦建立了一个socket,应用程序可以使用其他特定的调用来为它添加其他详细信息,以完成建立通信的过程。
2.2 从概念上理解socket的使用网络编程中最常见的是客户/服务器模式。
以该模式编程时,服务端有一个进程(或多个进程)在指定的端口等待客户来连接,服务程序等待客户的连接信息,一旦连接上之后,就可以按设计的数据交换方法和格式进行数据传输。客户端在需要的时刻发出向服务端的连接请求。
这里为了便于理解,提到了这些调用及其大致的功能。使用socket调用后,仅产生了一个可以使用的socket描述符,这时还不能进行通信,还要使用其他的调用,以使得socket所指的结构中使用的信息被填写完。在使用TCP协议时,一般服务端进程先使用socket调用得到一个描述符,然后使用bind调用将一个名字与socket描述符连接起来,对于Internet域就是将Internet地址联编到socket。之后,服务端使用listen调用指出等待服务请求队列的长度。然后就可以使用accept调用等待客户端发起连接(一般是阻塞等待连接,后面章节会讲到非阻塞的方式),一旦有客户端发出连接,accept返回客户的地址信息,并返回一个新的socket描述符,该描述符与原先的socket有相同的特性,这时服务端就可以使用这个新的socket进行读写操作了。一般服务端可能在accept返回后创建一个新的进程进行与客户的通信,父进程则再到accept调用处等待另一个连接。客户端进程一般先使用socket调用得到一个socket描述符,然后使用connect向指定的服务器上的指定端口发起连接,一旦连接成功返回,就说明已经建立了与服务器的连接,这时就可以通过socket描述符进行读写操作了。下面是在客户和服务端使用TCP时,客户进程和服务进程使用系统调用的该程。
使用TCP的客户和服务端使用系统调用的图示使用无连接的UDP协议时,服务端进程创建一个socket,之后调用recvfrom接收客户端的数据报,然后调用sendto将要返回客户端的消息发送给客户进程。客户端也要先创建一个socket,再使用sendto向服务端进程发出请求,使用recvfrom得到返回的消息。
界面表现(WPF),企业级通讯(WCF),工作流引擎(WWF)
WCF是构建安全可靠的事务性服务的统一框架。它是一种构建分布式面向服务系统的非常丰富的技术基础,它统一了消息风格和RPC[Remote Procedure Call]风格,并且通过二进制和基于开放标准的通信达到了平台最优化。对于我来说,最关键的事情是让更多人了解它,但我认为让开发人员激动的是编成模型的优雅和简单。在很多文章中,面向服务已经获得了这种极高的评价。我想,WCF能做的就是对绝大多数开发人员甚至架构师来说让这种想法成为现实。
wcf从第一天使用起,就把我高兴坏了,电话好几个朋友赶紧来使用;
wwf可以在你的应用中使用工作流,以前曾想过要开发一个工作流,无奈工作量巨大,wwf真是雪中送碳的东东啊
如果想了解SOAP到底是什么,就要自己动手建立自己的SOAP标准对象。本文可以帮助你起步。
SOAP,简单对象传输协议,是一种在网络上传输对象的协议,它用XML将对象编码,用HTTP协议在网络上传输,是WebService的一种标准协议。
to SOAP,简单对象传输协议,是一种在网络上传输对象的协议,它用XML将对象编码,用HTTP协议在网络上传输,是WebService的一种标准协议。
SOAP,简单对象传输协议,不一定非要用http来进行传输。只不过见得最多的是使用http来传输而已。
"SOAP是在非集中、分布环境中交换信息的轻量级协议。它是基于XML的协议,包括三个部分: 封套(envelope)定义了消息内容和处理的框架、一套编码规则用来表达应用定义数据类型的实例以及表达远程过程调用和响应的协定。"
——SOAP 1.1规范
第一节 SOAP简介
SOAP(Simple Object Access Protocal,简单对象访问协议) 技术有助于实现大量异构程序和平台之间的互操作性,从而使存在的应用能够被广泛的用户所访问。SOAP是把成熟的基于HTTP的WEB技术与XML的灵活性和可扩展性组合在了一起。
SOAP的一个主要目标是使存在的应用能被更广泛的用户所使用。为了实现这个目的,没有任何SOAP API或SOAP 对象请求代理(SOAP ORB),SOAP是假设你将使用尽可能多的存在的技术。几个主要的CORBA厂商已经承诺在他们的ORB产品中支持SOAP协议。微软也承诺在将来的COM版本中支持SOAP。DevelopMentor已经开发了参考实现,它使得在任何平台上的任何Java或Perl程序员都可以使用SOAP。而且IBM和Sun也陆续支持了SOAP协议,和MS合作共同开发SOAP规范和应用。目前SOAP已经成为了W3C和IETF的参考标准之一。
SOAP的指导理念是“它是第一个没有发明任何新技术的技术”。它采用了已经广泛使用的两个协议:HTTP和XML。HTTP用于实现SOAP的RPC风格的传输,而XML是它的编码模式。采用几行代码和一个XML解析器,HTTP服务器(如MS的IIS或Apache)立刻成为了SOAP的ORBs。 因为目前超过一半的Web服务器采用IIS或Apache, SOAP将会从这两个产品的广泛而可靠的使用中获取利益。这并不意味着所有的SOAP请求必须通过Web服务器来路由,传统的Web 服务器只是分派SOAP请求的一种方式。因此Web服务如IIS或Apache对建立SOAP性能的应用是充分的,但决不是必要的。
SOAP把XML的使用代码化为请求和响应参数编码模式,并用HTTP作传输。这似乎有点抽象。具体地讲,一个SOAP方法可以简单地看作遵循SOAP编码规则的HTTP请求和响应。一个SOAP终端则可以看作一个基于HTTP的URL,它用来识别方法调用的目标。象CORBA/IIOP一样,SOAP不需要具体的对象被绑定到一个给定的终端,而是由具体实现程序来决定怎样把对象终端标识符映射到服务器端的对象。
SOAP请求是一个HTTP POST请求。SOAP请求的content-type必须用text/xml。而且它必须包含一个请求-URI。服务器怎样解释这个请求-URI是与实现相关的,但是许多实现中可能用它来映射到一个类或者一个对象。一个SOAP请求也必须用SOAPMethodName HTTP头来指明将被调用的方法。简单地讲,SOAPMethodName头是被URI指定范围的应用相关的方法名,它是用#符作为分隔符将方法名与URI分割开:
SOAPMethodName: urn:strings-com:IString#reverse
这个头表明方法名是reverse,范围URI是urn:strings-com:Istring。 在SOAP中,规定方法名范围的名域URI在功能上等同于在DCOM 或 IIOP中规定方法名范围的接口ID。
简单的说,一个SOAP请求的HTTP体是一个XML文档,它包含方法中[in]和[in,out]参数的值。这些值被编码成为一个显著的调用元素的子元素,这个调用元素具有SOAPMethodName HTTP头的方法名和名域URI。调用元素必须出现在标准的SOAP <Envelope>和<Body>元素内(后面会更多讨论这两个元素)。下面是一个最简单的SOAP方法请求:
POST /string_server/Object17 HTTP/1.1
Host: 209.110.197.2
Content-Type: text/xml
Content-Length: 152
SOAPMethodName: urn:strings-com:IString#reverse
<Envelope>
<Body>
<m:reverse xmlns:m=''urn:strings-com:IString''>
<theString>Hello, World</theString>
</m:reverse>
</Body>
</Envelope>
SOAPMethodName头必须与<Body>下的第一个子元素相匹配,否则调用将被拒绝。这允许防火墙管理员在不解析XML的情况下有效地过滤对一个具体方法的调用。
SOAP响应的格式类似于请求格式。响应体包含方法的[out]和 [in,out]参数,这个方法被编码为一个显著的响应元素的子元素。这个元素的名字与请求的调用元素的名字相同,但以Response后缀来连接。下面是对前面的SOAP请求的SOAP响应:
200 OK Content-Type: text/xml
Content-Length: 162
<Envelope>
<Body>
<m:reverseResponse xmlns:m=''urn:strings-com:IString''>
<result>dlroW ,olleH</result>
</m:reverseResponse>
</Body>
</Envelope>
这里响应元素被命名为reverseResponse,它是方法名紧跟Response后缀。要注意的是这里是没有SOAPMethodName HTTP头的。这个头只在请求消息中需要,在响应消息中并不需要。
Top
10 楼qiri07(俺家金毛de地位比俺高)回复于 2006-10-22 10:36:05 得分 0 第二节 SOAP体的核心
SOAP的XML特性是为把数据类型的实例序列化成XML的编码模式。为了达到这个目的,SOAP不要求使用传统的RPC风格的代理。而是一个SOAP方法调用包含至少两个数据类型:请求和响应。考虑这下面个COM IDL代码:
[ uuid(DEADF00D-BEAD-BEAD-BEAD-BAABAABAABAA) ]
interface IBank : IUnknown {
HRESULT withdraw([in] long account,
[out] float *newBalance,
[in, out] float *amount
[out, retval] VARIANT_BOOL *overdrawn);
}
在任何RPC协议下,account和amount参数的值将出现在请求消息中,newBalance、overdrawn参数的值,还有amount参数的更新值将出现在响应消息中。
SOAP把方法请求和方法响应提升到了一流状态。在SOAP中,请求和响应实际上类型的实例。为了理解一个方法比如IBank::withdraw怎样映射一个SOAP请求和响应类型,考虑下列的数据类型:
struct withdraw {
long account;
float amount;
};
这时所有的请求参数被打包成为单一的结构类型。同样下面的数据表示打包所有响应参数到单一的数据类型。
struct withdrawResponse {
float newBalance;
float amount;
VARIANT_BOOL overdrawn;
};
再给出下面的简单的Visual Basic程序,它使用了以前定义的Ibank接口:
Dim bank as IBank
Dim amount as Single
Dim newBal as Single
Dim overdrawn as Boolean
amount = 100
Set bank = GetObject("soap:http://bofsoap.com/am")
overdrawn = bank.withdraw(3512, amount, newBal)
这里,在发送请求消息之前,参数被序列化成为一个请求对象。同样被响应消息接收到的响应对象被反序列化为参数。一个类似的转变同样发生在调用的服务器端。
当通过SOAP调用方法时,请求对象和响应对象被序列化成一种已知的格式。每个SOAP体是一个XML文档,它具有一个显著的称为<Envelope>的根元素。标记名<Envelope>由SOAP URI (urn:schemas-xmlsoap-org:soap.v1)来划定范围,所有SOAP专用的元素和属性都是由这个URI来划定范围的。SOAP Envelope包含一个可选的<Header>元素,紧跟一个必须的<Body>元素。<Body>元素也有一个显著的根元素,它或者是一个请求对象或者是一个响应对象。下面是一个IBank::withdraw请求的编码:
<soap:Envelope xmlns:soap=''urn:schemas-xmlsoap-org:soap.v1''>
<soap:Body>
<IBank:withdraw xmlns:IBank=''urn:uuid:DEADF00D-BEAD-BEAD-BEAD-BAABAABAABAA''>
<account>3512</account>
<amount>100</amount>
</IBank:withdraw>
</soap:Body>
</soap:Envelope>
下列响应消息被编码为:
<soap:Envelope xmlns:soap=''urn:schemas-xmlsoap-org:soap.v1''>
<soap:Body>
<IBank:withdrawResponse xmlns:IBank=''urn:uuid:DEADF00D-BEAD-BEAD-BEAD-BAABAABAABAA''>
<newBalance>0</newBalance>
<amount>5</amount>
<overdrawn>true</overdrawn>
</IBank:withdrawResponse>
</soap:Body>
</soap:Envelope>
注意[in, out]参数出现在两个消息中。在检查了请求和响应对象的格式后,你可能已经注意到序列化格式通常是:
<t:typename xmlns:t=''namespaceuri''>
<fieldname1>field1value</fieldname1>
<fieldname2>field2value</fieldname2>
......
</t:typename>
在请求的情况下,类型是隐式的C风格的结构,它由对应方法中的[in]和[in, out]参数组成。对响应来说,类型也是隐式的C风格的结构,它由对应方法中的[out]和[in, out]参数组成。这种每个域对应一个子元素的风格有时被称为元素正规格式(ENF)。一般情况下,SOAP只用XML特性来传达描述包含在元素内容中信息的注释。
象DCOM和IIOP一样,SOAP支持协议头扩展。SOAP用可选的<Header>元素来传载被协议扩展所使用的信息。如果客户端的SOAP软件包含要发送头信息,原始的请求将可能如图9所示。在这种情况下命名causality的头将与请求一起序列化。收到请求后,服务器端软件能查看头的名域URI,并处理它识别出的头扩展。这个头扩展被http://comstuff.com URI识别,并期待一个如下的对象:
struct causality {
UUID id;
};
在这种情况下的请求,如果头元素的URI不能被识别,头元素可以被安全地忽略。
但你不能安全的忽略所有的SOAP体中的头元素。如果一个特定的SOAP头对正确处理消息是很关键的,这个头元素能被用SOAP属性mustUnderstand=’true’标记为必须的。这个属性告诉接收者头元素必须被识别并被处理以确保正确的使用。为了强迫前面causality头成为一个必须的头,消息将被写成如下形式:
<soap:Envelope xmlns:soap=''urn:schemas-xmlsoap-org:soap.v1''>
<soap:Header>
<causality soap:mustUnderstand=''true''xmlns="http://comstuff.com">
<id>362099cc-aa46-bae2-5110-99aac9823bff</id>
</causality>
</soap:Header>
</soap:Envelope>
SOAP软件遇到不能识别必须的头元素情况时,必须拒绝这个消息并出示一个错误。如果服务器在一个SOAP请求中发现一个不能识别的必须的头元素,它必须返回一个错误响应并且不发送任何调用到目标对象。如果客户端在一个SOAP请求中发现一个不能识别出的必须的头元素,它必须向调用者返回一个运行时错误。在COM情况下,这将映射为一个明显的HRESULT。
Top
11 楼qiri07(俺家金毛de地位比俺高)回复于 2006-10-22 10:36:23 得分 0 第三节 SOAP数据类型
在SOAP消息中,每个元素可能是一个SOAP结构元素、根元素、存取元素或一个独立的元素。在SOAP中,soap:Envelope、soap:Body和soap:Header是唯一的组成元素。它们的基本关系由下列XML Schema所描述:
<schema targetNamespace=''urn:schemas-xmlsoap-org:soap.v1''>
<element name=''Envelope''>
<type>
<element name=''Header'' type=''Header'' minOccurs=''0'' />
<element name=''Body'' type=''Body''minOccurs=''1'' />
</type>
</element>
</schema>
在SOAP元素的四种类型中,除了结构元素外都被用作表达类型的实例或对一个类型实例的引用。
根元素是显著的元素,它是soap:Body 或是 soap:Header的直接的子元素。其中soap: Body只有一个根元素,它表达调用、响应或错误对象。这个根元素必须是soap:Body的第一个子元素,它的标记名和域名URI必须与HTTP SOAPMethodName头或在错误消息情况下的soap:Fault相对应。而soap:Header元素有多个根元素,与消息相联系的每个头扩展对应一个。这些根元素必须是soap:Header的直接子元素,它们的标记名和名域URI表示当前存在扩展数据的类型。
存取元素被用作表达类型的域、属性或数据成员。一个给定类型的域在它的SOAP表达将只有一个存取元素。存取元素的标记名对应于类型的域名。考虑下列Java 类定义:
package com.bofsoap.IBank;
public class adjustment {
public int account ;
public float amount ;
}
在一个SOAP消息中被序列化的实例如下所示:
<t:adjustment xmlns:t=''urn:develop-com:java:com.bofsoap.IBank''>
<account>3514</account>
<amount>100.0</amount>
</t:adjustment>
在这个例子中,存取元素account和amount被称着简单存取元素。对引用简单类型的存取元素,元素值被简单地编码为直接在存取元素下的字符数据,如上所示。对引用组合类型的存取元素(就是那些自身用子存取元素来构造的存取元素),有两个技术来对存取元素进行编码。最简单的方法是把被结构化的值直接嵌入在存取元素下。考虑下面的Java类定义:
package com.bofsoap.IBank;
public class transfer {
public adjustment from;
public adjustment to;
}
如果用嵌入值编码存取元素,在SOAP中一个序列化的transfer对象如下所示:
<t:transfer xmlns:t=''urn:develop-com:java:com.bofsoap.IBank''>
<from>
<account>3514</account>
<amount>-100.0</amount>
</from>
<to>
<account>3518</account>
<amount>100.0</amount>
</to>
</t:transfer>
在这种情况下,adjustment对象的值被直接编码在它们的存取元素下。在考虑组合存取元素时,需要说明几个问题。先考虑上面的transfer类。类的from和to的域是对象引用,它可能为空。SOAP用XML Schemas的null属性来表示空值或引用。下面例子表示一个序列化的transfer对象,它的from域是空的:
<t:transfer xmlns:t=''urn:develop-com:java:com.bofsoap.IBank''
xmlns:xsd=''http://www.w3.org/1999/XMLSchema/instance''>
<from xsd:null=''true'' />
<to>
<account>3518</account>
<amount>100.0</amount>
</to>
</t:transfer>
在不存在的情况下, xsd:null属性的隐含值是false。给定元素的能否为空的属性是由XML Schema定义来控制的。例如下列XML Schema将只允许from存取元素为空:
<type name=''transfer'' >
<element name=''from'' type=''adjustment'' nullable=''true'' />
<element name=''to'' type=''adjustment'' nullable=''false''/>
</type>
在一个元素的Schema声明中如果没有nullable属性,就意味着在一个XML文档中的元素是不能为空的。Null存取元素的精确格式当前还在修订中�要了解用更多信息参考最新版本的SOAP规范。
与存取元素相关的另一个问题是由于类型关系引起的可代换性。由于前面的adjustment类不是一个final类型的类,transfer对象的from和to域实际引用继承类型的实例是可能的。为了支持这种类型兼容的替换,SOAP使用一个名域限定的类型属性的XML Schema约定。这种类型属性的值是一个对元素具体的类型的限制的名字。考虑下面的adjustment扩展类:
package com.bofsoap.IBank;
public class auditedadjustment extends adjustment {
public int auditlevel;
}
给出下面Java语言:
transfer xfer = new transfer();
xfer.from = new auditedadjustment();
xfer.from.account = 3514;
xfer.from.amount = -100;
xfer.from.auditlevel = 3;
xfer.to = new adjustment();
xfer.to.account = 3518;
xfer.from.amount = 100;
在SOAP中transfer对象的序列化形式如下所示:
<t:transfer xmlns:xsd=''http://www.w3.org/1999/XMLSchema''
xmlns:t=''urn:develop-com:java:com.bofsoap.IBank''>
<from xsd:type=''t:auditedadjustment'' >
<account>3514</account>
<amount>-100.0</amount>
<auditlevel>3</auditlevel >
</from>
<to>
<account>3518</account>
<amount>100.0</amount>
</to>
</t:transfer>
在这里xsd:type属性引用一个名域限定的类型名,它能被反序列化程序用于实例化对象的正确类型。因为to存取元素引用到一个被预料的类型的实例(而不是一个可代替的继承类型),xsd:type属性是不需要的。
刚才的transfer类设法回避了一个关键问题。如果正被序列化的transfer对象用下面这种方式初始化将会发生什么情况:
transfer xfer = new transfer();
xfer.from = new adjustment();
xfer.from.account = 3514; xfer.from.amount = -100;
xfer.to = xfer.from;
基于以前的议论,在SOAP 中transfer对象的序列化形式如下所示:
<t:transfer xmlns:t=''urn:develop-com:java:com.bofsoap.IBank''>
<from>
<account>3514</account>
<amount>-100.0</amount>
</from>
<to>
<account>3514</account>
<amount>-100.0</amount>
</to>
</t:transfer>
这个表达有两个问题。首先最容易理解的问题是同样的信息被发送了两次,这导致了一个比实际所需要消息的更大的消息。一个更微妙的但是更重要的问题是由于反序列化程序不能分辨两个带有同样值的adjustment对象与在两个地方被引用的一个单一的adjustment对象的区别,两个存取元素间的身份关系就被丢失。如果这个消息接收者已经在结果对象上执行了下面的测试,(xfer.to == xfer.from)将不会返回true。
void processTransfer(transfer xfer) {
if (xfer.to == xfer.from)
handleDoubleAdjustment(xfer.to);
else
handleAdjustments(xfer.to, xfer.from);
}
为了支持必须保持身份关系的类型的序列化,SOAP支持多引用存取元素。目前我们接触到的存取元素是单引用存取元素,也就是说,元素值是嵌入在存取元素下面的,而且其它存取元素被允许引用那个值(这很类似于在NDR中的[unique]的概念)。多引用存取元素总是被编码为只包含已知的soap:href属性的空元素。soap:href属性总是包含一个代码片段标识符,它对应于存取元素引用到的实例。如果to和from存取元素已经被编码为多引用存取元素,序列化的transfer对象如下所示:
<t:transfer xmlns:t=''urn:develop-com:java:com.bofsoap.IBank''>
<from soap:href=''#id1'' />
<to soap:href=''#id1'' />
</t:transfer>
这个编码假设与adjustment类兼容的一个类型的实例已经在envelope中的其它地方被序列化,而且这个实例已经被用soap:id属性标记,如下所示:
<t:adjustment soap:id=''id1''xmlns:t=''urn:develop-com:java:com.bofsoap.IBank''>
<account>3514</account>
<amount>-100.0</amount>
</t:adjustment>
简介
SOAP - 简单对象处理协议(Simple Object Access Protocol) - 是当前XML开发的热点。它是微软新一代Visual Studio的主要角色,是".NET"策略的基础。如果想用VB 6编制一个有保障的SOAP服务,可以查阅微软的SOAP工具包(VB)。但如果想了解SOAP到底是什么,就要自己动手建立自己的SOAP标准对象。本文可以帮助你起步。
本文中,我们创建一个简单的SOAP服务端和一个客户端。服务端用ASP编写,名为soap.asp。这个文件应存放在个人Web服务器的根目录下,如:/Inetpub/wwwroot。这个服务器将接受和处理客户端提出的SOAP请求。客户端是一个简单的VB可执行文件,由Sub Main()启动。
步骤
众所周知,SOAP是一个“呼叫-响应”机制,按客户/服务方式运行。客户端(应用程序)向服务端(位于互联网上的某个Web服务器)发出函数调用请求并传递参数;服务端则返回响应。呼叫与响应的内容和数据都是按XML文件格式进行传送的。因此,要建立一个简单的SOAP应用系统,就要建立一个客户端和一个服务端,即一个呼叫-响应体系。
下面是一个简单例子:
我们建立一个服务端来计算销售交易的税款。按照传统的VB术语,即建立一个函数,定义如下:
Public Function GetSalesTax(ByVal pSalesTotal As Double) as Double
GetSalesTax = pSalesTotal * 0.04
End Function
一个粗糙的函数,但可作为示例(本例只能用于税率为4%的地方)。
这个函数定义了一个函数名(GetSalesTax),一个参数(pSalesTotal – 销售金额)和一个返回值(函数返回值)。按照面向对象原则,可以认为pSalesTotal是一个"IN"参数,GetSalesTax返回值是一个"OUT"参数。因此我们的SOAP服务端就要侦听客户发出的调用GetSalesTax的请求和传递的"IN"参数(销售金额),然后返回带有"OUT"参数的回应,向客户返回所需税款。
客户端
下面是用VB建立一个呼叫服务的客户端程序:
dblSalesTax = GetSalesTax(100)
得到返回值$4。
如果GetSalesTax函数是一个外部对象,比如在MTS服务器上,就要调用服务器上的DLL模块:
Dim objTax As New CTaxCalc
dblSalesTax = objTax.GetSalesTax(100)
在SOAP系统中,远程调用的方式略有不同,呼叫是通过XML文件传送到服务器的。XML文件里有调用的函数名和相应的参数:
<GetSalesTax>
<SalesTotal>100</SalesTotal>
<GetSalesTax>
为确保服务器能够识别和解释客户请求,呼叫指令被包装到一个称之为SOAP信封的大文件里。这个信封使用的是SOAP封装标准的通用命名空间:
<SOAP:Envelope xmlns:SOAP="urn:schemas-xmlsoap-org:soap.v1">
<SOAP:Header></SOAP:Header>
<SOAP:Body>
<GetSalesTax>
<SalesTotal>100</SalesTotal>
<GetSalesTax>
</SOAP:Body>
</SOAP:Envelope>
最后,加入函数调用的命名空间,起到函数声明的作用:
<SOAP:Envelope xmlns:SOAP="urn:schemas-xmlsoap-org:soap.v1">
<SOAP:Header></SOAP:Header>
<SOAP:Body>
<m:GetSalesTax xmlns:m="urn:myserver/soap:TaxCalc">
<SalesTotal>100</SalesTotal>
</m:GetSalesTax>
</SOAP:Body>
</SOAP:Envelope>
现在,已经准备好客户请求文件,可以送往服务端了。发送请求很简单,可以跟浏览器表单一样,用HTTP post方式。浏览器可以向服务端发送复杂的表单,.NET可以向服务器发送VB代码,但我使用XMLHTTP(IE 5以上版本才能用)。
假设strEnvelope含有XML文件格式的请求,发送格式如下:
Dim objHTTP As New MSXML.XMLHTTPRequest
Dim strEnvelope As String
'设定发往本地服务器
objHTTP.open "post", "http://localhost/soap/soap.asp"
'设定标准SOAP/ XML文件头格式
objHTTP.setRequestHeader "Content-Type", "text/xml"
'设置呼叫函数请求
objHTTP.setRequestHeader "SOAPMethodName", _
"urn:myserver/soap:TaxCalc#GetSalesTax"
'呼叫SOAP
objHTTP.send strEnvelope
'取得返回值
strReturn = objHTTP.responseBody
至此,客户端完成了向服务端发送请求的过程。现在回到服务端,看看服务端如何侦听客户请求并作出响应。
服务端
服务端要能够接收客户发出的HTTP请求,在本地服务器(http://localhost/soap.asp)接收到客户请求时作出回应。因此服务端要能够解析客户端发出的XML格式(SOAP封装)的请求,取出调用的函数名和参数。
服务端文件是soap.asp,它接收客户请求的做法是:
Set objReq = Server.CreateObject("Microsoft.XMLDOM")
objReq.Load Request
然后用XSL样式从封装的XML文件中取出参数:
strQuery = "SOAP:Envelope/SOAP:Body/m:GetSalesTax/SalesTotal"
varSalesTotal = objReq.SelectSingleNode(strQuery).Text
根据参数计算税款:
varSalesTax = varSalesTotal * 0.04
在将结果返回给客户之前,要按SOAP标准做格式化封装。过程与客户端类似,只是把"IN"参数改换为"OUT"参数,并将函数标记名标为回应:
<SOAP:Envelope xmlns:SOAP="urn:schemas-xmlsoap-org:soap.v1">
<SOAP:Header></SOAP:Header>
<SOAP:Body>
<m:GetSalesTaxResponse xmlns:m="urn:myserver/soap:TaxCalc">
<SalesTax>4</SalesTax>
</m:GetSalesTaxResponse>
</SOAP:Body>
</SOAP:Envelope>
可以用字符串方式构造这个回应文件,也可以创建一个DOM对象,增加一个节点。
文件返回给客户后,客户经过解码就能得到结果:
Dim objReturn As New MSXML.DomDocument
objReturn.LoadXML strReturn
strQuery = _
"SOAP:Envelope/SOAP:Body/m:GetSalesTaxResponse/SalesTax"
dblTax = objReturn.SelectSingleNode(strQuery).Text
这样就完成了一个简单的SOAP服务应用。虽然Visual Studio 7掩盖了内在的SOAP协议,但我希望本文有助于理解SOAP的操作过程。
下面是客户端VB代码:
VB Client Code
Sub Main()
Dim objHTTP As New MSXML.XMLHTTPRequest
Dim strEnvelope As String
Dim strReturn As String
Dim objReturn As New MSXML.DOMDocument
Dim dblTax As Double
Dim strQuery As String
'创建SOAP封装
strEnvelope = _
"<soap:envelope xmlns:soap=""urn:schemas-xmlsoap-org:soap.v1"">" = _
"<soap:header></soap:header>" = _
"<soap:body>" = _
"<m:getsalestax xmlns:m=""urn:myserver/soap:TaxCalculator"">" = _
"<salestotal>100</salestotal>" = _
"</m:getsalestax>" = _
"</soap:body>" = _
"</soap:envelope>"
'设定发往本地服务器
objHTTP.open "post", "http://localhost/soap.asp", False
'设定标准SOAP/ XML格式
objHTTP.setRequestHeader "Content-Type", "text/xml"
'设置调用函数头
objHTTP.setRequestHeader "SOAPMethodName", _
"urn:myserver/soap:TaxCalculator#GetSalesTax"
'SOAP呼叫
objHTTP.send strEnvelope
'取出返回信封
strReturn = objHTTP.responseText
'加载到DOM
objReturn.loadXML strReturn
'查询返回值
strQuery = _
"SOAP:Envelope/SOAP:Body/m:GetSalesTaxResponse/SalesTax"
dblTax = objReturn.selectSingleNode(strQuery).Text
Debug.Print dblTax
End Sub
下面是服务端ASP代码(文件名soap.asp,存放在本地服务器根目录下):
<%
Set objReq = Server.CreateObject("Microsoft.XMLDOM")
'加载请求到XML DOM
objReq.Load Request
'按照输入参数查询
strQuery = "SOAP:Envelope/SOAP:Body/m:GetSalesTax/SalesTotal"
varSalesTotal = objReq.SelectSingleNode(strQuery).Text
'计算
varSalesTax = varSalesTotal * 0.04
'准备返回信封
strTmp = _
"<soap:envelope xmlns:soap=""urn:schemas-xmlsoap-org:soap.v1"">" = _
"<soap:header></soap:header>" = _
"<soap:body>" = _
"<m:getsalestaxresponse xmlns:m=""urn:myserver/soap:TaxCalc"">" = _
"<salestax>" = varSalesTax = "</salestax>" = _
"</m:getsalestaxresponse>" = _
"</soap:body>" = _
"</soap:envelope>"
'回写结果文件
Response.Write strTmp
%>
事务处理概念和 MS DTC 概述 (简述)
编写应用程序是很难的。随着时间的推移,我们不断发掘允许建立大型应用程序的概念和技术。模块化,或者说将应用程序构建成独立的模块,使我们得以由简单的部分构建复杂的系统,并使软件得到重复使用。面向对象概念和 Microsoft® 组件对象模块 (COM) 可提供编写模块化应用程序的技术。
当某个应用程序构建为组件时,各个单独部分可共同驻留在单台计算机上,也可通过远程过程调用在网络上相互作用。因此,组件同时提供了模块化和自然分布。
将应用程序构建成独立的组件会产生组件的管理问题。单个程序失败后,将作为一个单位重新启动。但在使用模块化系统时,一个组件的失败不应当破坏其它组件。这就必须有隔离故障和限制故障传播的方法。事务提供了模块化执行方式,因此简化了故障处理,并使故障处理自动实现。它们同时为执行者和用户提供了简单的概念化执行框架。
用户认为事务是单个的要么发生或要么不发生的更改事件。执行者认为事务是一种允许他们编写能参与分布式计算的模块的编程形式。假定您要将资金从一个银行帐户转到另一个帐户。执行者和用户需要确保两个帐户都更改或都不更改。在分布式系统中很难完成这项工作 - 计算机可能失败,并且会丢失信息。事务提供了一种方法,能够将一组操作集合成具有原子性的执行单位。
原子性要么全有要么全无的属性并不新鲜:它在生活中随处可见。例如,如果输入一个合同,escrow(由第三者保存附带条件委付盖印的契约)官员将协调这项事务:escrow 官员会收集此合同每一方的签字。当 escrow 官员宣布所有人都已签字后,此合同就完成了。主持结婚典礼的牧师先问新娘和新郎“愿意此人成为您的配偶吗?” 如果他们都回答“愿意”,牧师就会宣布他们结婚。电影导演在某一场景会先问“布景准备好了吗?” 如果都回答已准备好,导演就会喊“开拍!” 在帆船上预备掉转航向的舵手会先问船员,“准备好转向了吗?” 如果都回答已准备好,舵手就会喊“转舵!”,改变船的方向。
这些情景说明了事务的基本原理:几个独立的实体必须达成一致。如果任何一方不同意,交易就会失败。一旦同意后,事务就会发生。Microsoft Distributed Transaction Coordinator (MS® DTC) 为 COM 结构的其它组件执行这项事务协调任务。
在 MS DTC 术语中,执行者被称为事务管理器。在执行事务保护资源的事务中,其参与者(如关系数据库)被称为资源管理器。
应用程序通过调用事务管理器的 BeginTransaction 方法开始事务。这样可创建一个代表事务的事务对象。然后应用程序会调用资源管理器来完成事务工作。
应用程序对每个资源管理器的第一次调用确定应用程序的当前事务。例如,如果应用程序在使用关系数据库,它会调用 ODBC 接口,此接口将事务对象与 ODBC 连接关联起来。在此之后,所有通过此连接的数据库调用都会代表该事务执行,直到该事务结束为止。
当某个资源管理器首先代表某个事务工作时,会通过调用事务管理器“登记”到该事务中。随着事务的发展,事务管理器会跟踪每个登记到该事务中的资源管理器。
通常,应用程序用 Commit 事务方法来完成事务。如果应用程序无法完成,则调用 Abort 事务方法,该方法可以撤消事务的操作。如果应用程序失败,MS DTC 就会放弃此事务。
当应用程序成功地完成事务的工作后,它会调用 MS DTC 来“提交”事务。然后 MS DTC 会仔细检查“两阶段” “提交协议”,使所有已登记的资源管理器都提交。两阶段提交协议可确保所有的资源管理器提交此事务,或全都放弃此事务。在第一阶段,MS DTC 询问每个资源管理器是否“准备”提交。如果所有参与者都回答“是”,那么在第二阶段 MS DTC 将向所有参与者广播提交信息。如果事务的任何部分失败,或资源管理器响应准备请求失败,或资源管理器响应“否”,那么 MS DTC 将通知所有资源管理器该事务已被放弃。
事务管理器是大多数数据库系统的关键部分。事务管理器还是某些操作系统的可选部分。Microsoft 相信事务对分布式应用至关重要 - 事务提供了模块化执行,从而使 COM 模块化编程更加完备。Microsoft 提供 Microsoft Windows® 95 和 Microsoft Windows NT® 操作系统的事务管理软件。
将事务概念与 COM 必需的创新相结合。传统的事务系统要求具有很高的安装和管理技巧。而集成 MS DTC 与 Microsoft 操作系统的难点是自动实现安装、管理和使用。许多概念和技术必须针对新的客户/服务器、面向对象和可视化管理环境重新创造。
在第一版中,MS DTC 使用了一个资源管理器:Microsoft SQL Server®。它还使用了几个事务处理监视器,包括 Encina®、Top End 和 TUXEDO®。MS DTC 实现 OLE 事务接口。所有 OLE 事务接口都是公用的,因此任何资源管理器都可以变成 OLE 事务资源管理器。将来,Microsoft 和其它软件公司将增加其它事务资源管理器,比如分布式对象系统、事务文件系统、事务队列系统及业务流程管理系统。
有效的敏捷编程
很多企业一直都在寻找更小型、更快速和更廉价的软件。目前,一种新的软件开发方法—“敏捷编程”(Agile Programming)受到了很多企业的持续关注。如果将敏捷编程用在合适的环境和合适的项目中,这种开发方法就会非常有效。
“极限编程”(XP)是最早的敏捷编程方法,由肯特·贝克(Kent Beck)、沃德·库宁汉(Ward Cunningham)和容·杰弗里斯(Ron Jeffries)等在上世纪90年代末首先提出。到2001年,这个涵盖性术语的含义发生了一定的改变,它主要描述一些具有共同特征的编程方法,运用这些方法,都能将项目细分成小型化的、可管理的模块,都采用了多次反复开发的方式,而非传统的单向开发模式。
敏捷编程在新兴企业环境中或是针对一些保密项目特别有效。因为在这些保密项目中,在开发初期时,最终产品的定义总是不明确的,而这也是必要的。从企业的角度来说,敏捷编程很具有吸引力,因为它更贴切地反映了真实的市场开发过程。
敏捷编程与传统的“自上向下”(Top-Down)设计方法不同的地方在于,一旦整体基础架构建成,整个项目被分成小的模块,这些小的模块可以在很短的时间,通常不到7天或甚至只要一天,就可以组合为具有完整功能、经过检验的和潜在可用的产品。
由于敏捷编程方法在风险管理方面有很大价值,因此用户早期的反馈就显得非常重要。敏捷项目在初期就要很好地控制风险,避免浪费太多的资源。最糟糕的情况是,企业投入了大量资源进行项目开发,最后却发现产品的性能是用户不想要或不需要的。敏捷编程方法意味着开发团队只需专注于对用户最具价值的模块和性能。对于这样的开发方法,营销部门非常欢迎。因为在项目早期阶段,一些重要的产品功能就已开发出来,而且随时可以发布。
开发人员在构建单独的个人工作模块之前,必须周密考虑到工作的界面和协议、供应商名录等情况。因此,站在全局性的高度,让大家参与规划项目的整体架构就很有好处,尽管这并非是一个必要的工作环节。所以,对于企业来说,让开发人员理解项目的整体设计,以及设计的背后原因,就是比较理想的操作方法。
因为敏捷方法高度依赖于创建单独的工作模块,所以需要在整个开发过程中,进行大量内置的和反复的检验。质量保证(Quality-Assurance)和可用性(User-Acceptance)测试就是流程的一部分。由于最后阶段为了赶时间,往往很容易把测试部分略去,因此在敏捷编程过程中,这些工作就已经构建在代码编写的流程中,而不是像通常那样放在项目最后阶段进行。
如果运用恰当,敏捷编程会是一种特别有效的技术。它将项目分割成不同的模块,共同组成互相关联的、可管理的大型产品,每个模块性能都可以单独开发,同时在过程中保持了整体的产品功能。当然,和任何方法一样,敏捷编程工具成功应用的关键,还必须具备良好的编程操作和优秀的项目管理技巧。
2006-12-30 高薪招聘兼职系统架构师 |
我为什么选择 iBatis 而不是 Hibernate(对于正在选型的人的建议)
关键字: iBATIS Hibernate
[注意]清在回复之前认真地看一下我的帖子,结合你的实际项目经验考虑一下,看看你是否能比较好地解决我所提出的Hibernate 的缺点。最好不要提一些大家都知道的泛泛的观点,这样会很浪费读者的时间并且分散大家的注意力。
非常感谢有几位对 hibernate 有深入了解的朋友给出了我这里提出的问题的 hibernate 解决方案。我提出这几个问题的初衷不是说 hibernate 无法实现这些功能。而是说他的实现比较不美,呵呵。比如说把一些 sql 嵌入到 java 代码中,我觉得这是非常不好的习惯。
v0.3 - 2007-1-1 21:1:1
我在最初的选型的时候是打算选择 Hibernate 的,在研究的过程中发现了 iBatis,经过
分析比较之后我选择了 iBatis。现在我已经使用 iBatis 完成了一个中小型的项目。这个
项目在性能、可维护性、可扩展性方面都非常令我满意。
在这个过程中我也不断的与使用过或者正在使用 Hibernate 的人进行过探讨。而且我本身
也在不断的跟进 Hibernate 的发展。
最终,我的结论是 iBatis 的选择非常正确,而且越用越喜欢它了。
当然了,我对 Hibernate 的理解还是非常有限的,所以这里的关于 Hibernate 的一些观
点的错误之处希望能够得到 Hibernate 高手的指正。
1. iBatis 易于掌握。拿来文档看半天到两天就可以掌握了。
Hibernate 可能需要 3 倍以上的时间来掌握。
2. iBatis 更容易进行 sql 的 优化。
这个应该大家都有共识了。另外 Hibernate 生成的 sql 也实在是太难看了。鉴
于有的朋友提到了 sql 不太重要。我想在这里强调一下我的经验,一般系统性能
的瓶颈都在数据库上。所以这一点是 iBatis 非常重要的一个优势。
3. iBatis 可以进行细粒度的优化
3.1 比如说我有一个表,这个表有几个或者几十个字段,我需要更新其中
的一个字段,iBatis 很简单,执行一个sql
UPDATE TABLE_A SET column_1=#column_1# WHERE id=#id#
但是用 Hibernate 的话就比较麻烦了,缺省的情况下 hibernate 会更新所有字段。
当然我记得 hibernate 有一个选项可以控制只保存修改过的字段,但是我不太确
定这个功能的负面效果。
3.2 我需要列出一个表的部分内容,用 iBatis 的时候,这里面的好处是可以少从数据
库读很多数据,节省流量
SELECT ID, NAME FROM TABLE_WITH_A_LOT_OF_COLUMN WHERE ...
3.2.1 一般情况下
Hibernate 会把所有的字段都选出来。比如说有一个上面表有8个字段,
其中有一两个比较大的字段,varchar(255)/text。上面的场景中我为什么要把他
们也选出来呢?
3.2.2 用 hibernate 的话,你又不能把这两个不需要的字段设置为 lazy load,因
为还有很多地方需要一次把整个 domain object 加载出来。这个时候就能显现出
ibatis 的好处了
3.2.3 Hibernate 还有一个方案,就是生成 javabean/map/object[](感谢
leelun/cjmm),但是这样的话就可能会产生大量的多余 class。map/object[] 的方式
应该不错,我比较喜欢这种方式。
3.3 如果我需要更新一条记录(一个对象),如果使用 hibernate,需要现把对
象 select 出来,然后再做 update。这对数据库来说就是两条 sql。而 iBatis
只需要一条 update 的 sql 就可以了。减少一次与数据库的交互,对于性能的
提升是非常重要。
4. 开发方面
4.1 开发效率上,我觉得两者应该差不多
4.2 可维护性方面,我觉得 iBatis 更好一些。因为 iBatis 的 sql 都保存到
单独的文件中。而 Hibernate 在有些情况下可能会在 java 代码中保存
sql/hql。
5. 运行效率
5.1 在不考虑 cache 的情况下,iBatis 应该会比hibernate 快一些或者很多
(根据实际情况会有所不同)。
当然 iBatis 也有比较大的缺点
1. 不同数据库类型的支持不好,如果你要开发的系统是要在对中数据间移植,那可能用 hibernate 比较好。
2. 缺省的 cache 支持不好,但是 hibernate 的 cache 支持其实也不是很好,而且很复杂。尤其是对于大并发量的应用。所以我更倾向于自己管理 cache。
非常感谢这么多朋友对这个话题很感兴趣。但是我感觉大家并没有对我第三部分提到的问题进行更深入的思考。我晚些时候会提交一些 ibatis 的代码。欢迎大家一起来讨论。
最后更新: 2007-07-06 17:14
15:18 | 永久链接 | 浏览 (35036) | 评论 (101) | java | 进入论坛 |
永久链接
http://jiming.javaeye.com/blog/41720
评论 共 101 条
karna 2006-12-30 15:34
jiming 写道
我在最初的选型的时候是打算选择 Hibernate 的,在研究的过程中发现了 iBatis,经过
分析比较之后我选择了 iBatis。现在我已经使用 iBatis 完成了一个中小型的项目。这个
项目在性能、可维护性、可扩展性方面都非常令我满意。
在这个过程中我也不断的与使用过或者正在使用 Hibernate 的人进行过探讨。而且我本身
也在不断的跟进 Hibernate 的发展。
最终,我的结论是 iBatis 的选择非常正确,而且越用越喜欢它了。
当然了,我对 Hibernate 的理解还是非常有限的,所以这里的关于 Hibernate 的一些观
点的错误之处希望能够得到 Hibernate 高手的指正。
1. iBatis 易于掌握。拿来文档看半天到两天就可以掌握了。
Hibernate 可能需要 3 倍以上的时间来掌握。
其中有一些与我探讨的人跟我说 Hibernate 也很容易学,但是在与他们的更深一步的
讨论中,我发现他们只是使用了 Hibernate 最简单的一部分功能,更深一些的功能,
和我提出来的一些疑虑他们并没有考虑到。
2. iBatis 更容易进行 sql 的 优化。
这个应该大家都有共识了。另外 Hibernate 生成的 sql 也实在是太难看了。
3. iBatis 可以进行细粒度的优化
3.1 比如说我有一个表,我需要更新其中的一个字段,iBatis 很简单,执行一个sql
UPDATE TABLE_A SET column_1=#column_1# WHERE id=#id#
但是用 Hibernate 的话就比较麻烦了
3.2 我需要列出一个表的部分内容,用 iBatis 的时候,这里面的好处是可以少从数据
库读很多数据,节省流量
SELECT ID, NAME FROM TABLE_WITH_A_LOT_OF_COLUMN WHERE ...
当然 iBatis 也有比较大的缺点
1. 不同数据库类型的支持不好
2. 缺省的 cache 支持不好
看了偶即使闲极无聊也没必要去看一下iBatis了
lighter 2006-12-30 15:44
就我的理解,iBATIS不是一个ORM工具,哪有什么可比的地方。
只是一个sql mapping tool。
感觉这一种VS没有什么意思
Allen 2006-12-30 15:49
以上列出来的几条并不是两者绝对存在的差异,其实运用得好Hibernate,上面的问题都不是问题……
而如果使用者水平有限的话,上述iBatis的“优点”也就没有意义了。
这个帖子的标题要是写作 —— “作为JDBC高手的我为什么选择 iBatis 而不是 Hibernate”就更加贴切了。
daquan198163 2006-12-30 16:07
jiming 写道
1. iBatis 易于掌握。拿来文档看半天到两天就可以掌握了。
Hibernate 可能需要 3 倍以上的时间来掌握。
其中有一些与我探讨的人跟我说 Hibernate 也很容易学,但是在与他们的更深一步的
讨论中,我发现他们只是使用了 Hibernate 最简单的一部分功能,更深一些的功能,
和我提出来的一些疑虑他们并没有考虑到。
2. iBatis 更容易进行 sql 的 优化。
这个应该大家都有共识了。另外 Hibernate 生成的 sql 也实在是太难看了。
3. iBatis 可以进行细粒度的优化
3.1 比如说我有一个表,我需要更新其中的一个字段,iBatis 很简单,执行一个sql
UPDATE TABLE_A SET column_1=#column_1# WHERE id=#id#
但是用 Hibernate 的话就比较麻烦了
3.2 我需要列出一个表的部分内容,用 iBatis 的时候,这里面的好处是可以少从数据
库读很多数据,节省流量
SELECT ID, NAME FROM TABLE_WITH_A_LOT_OF_COLUMN WHERE ...
当然 iBatis 也有比较大的缺点
1. 不同数据库类型的支持不好
2. 缺省的 cache 支持不好
虽然不是hibernate高手,也想来替它平反一下:
1:Hibernate 可能需要 3 倍以上的时间来掌握,但它也能成倍提高效率;
2:sql难看就难看呗,基本不需要看了
3.1:hibernate load出一个对象,修改,然后保存就可以。hibernate还可以通过p6spy优化性能
3.2:hibernate的延迟加载会更节省流量
另外, iBatis 的第一个缺点——不同数据库类型的支持不好,足以让它输给hibernate了吧
ahuaxuan 2006-12-30 17:51
我觉得楼主应该没有用过hibernate吧,lz并没有写hibernate的优点,而是一个劲的写缺点,ibatis和hibernate其实应该可以看成是互不的两个东西,hibernate学习成本高于ibatis,但是hibernate的效率也高于ibatis,用hibernate也更oo,我看到lz说ibatis更容易优化芸芸,其实一般的系统要优化的sql语句有多少??一般的项目用hibernate是没有问题的,遗留系统或者性能要求非常苛刻的系统应该用ibatis,这两者没有绝对的好和坏的问题,只有不同的系统不同的项目用哪个更合适的问题,如果lz说hibernate不适合你们的系统,那请讲讲理由好吗
jamesby 2006-12-30 18:10
最近正在看Hibernate,感觉要深入了解确实需要较多时间,特别是用好它的缓存策略!
虽然ibatis不是ORM,但是它们的共同点都是解决数据持久层的问题,在一起比较也不为过吧.
robbin 2006-12-30 18:12
http://robbin.javaeye.com/blog/24529
ceder 2006-12-30 21:01
我选择用iBatis,主要是可以照搬JPetStore的架构
而且,写的SQL语句,基本可以直接拿到数据库上去执行测试。
flyspider 2006-12-30 22:55
ahuaxuan 写道
我觉得楼主应该没有用过hibernate吧,lz并没有写hibernate的优点,而是一个劲的写缺点,ibatis和hibernate其实应该可以看成是互不的两个东西,hibernate学习成本高于ibatis,但是hibernate的效率也高于ibatis,用hibernate也更oo,我看到lz说ibatis更容易优化芸芸,其实一般的系统要优化的sql语句有多少??一般的项目用hibernate是没有问题的,遗留系统或者性能要求非常苛刻的系统应该用ibatis,这两者没有绝对的好和坏的问题,只有不同的系统不同的项目用哪个更合适的问题,如果lz说hibernate不适合你们的系统,那请讲讲理由好吗
hibernate的效率高于iBATIS?请讲讲理由。
迭代器 与枚举器 在前习lambda时候看到有介绍到迭代器。。
找到microsoft的例子添加了一个枚举器下去。。不太清楚什么场景下用这个比较多 //Copyright (C) Microsoft Corporation. All rights reserved.
using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
namespace Yield
{
class Yield
{
public class NumberList:IEnumerable
{
// Create an array of integers.
public static int[] ints = { 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377 };
// Define a property that returns only the even numbers.
public static IEnumerable<int> GetEven()
{
// Use yield to return the even numbers in the list.
foreach (int i in ints)
if (i % 2 == 0)
yield return i;
}
// Define a property that returns only the even numbers.
public static IEnumerable<int> GetOdd()
{
// Use yield to return only the odd numbers.
foreach (int i in ints)
if (i % 2 == 1)
yield return i;
}
IEnumerable Members#region IEnumerable Members
public IEnumerator GetEnumerator()
{
return ints.GetEnumerator();
}
#endregion
}
static void Main(string[] args)
{
// Display the even numbers.
Console.WriteLine("Even numbers");
foreach (int i in NumberList.GetEven())
Console.WriteLine(i);
// Display the odd numbers.
Console.WriteLine("Odd numbers");
foreach (int i in NumberList.GetOdd())
Console.WriteLine(i);
Console.WriteLine("枚举器");
NumberList list = new NumberList();
foreach (int i in list)
{
Console.WriteLine(i);
}
Console.ReadLine();
}
}
}
Ajax在ASP.NET的应用原理 --转载
Asynchronous JavaScript and XML(Ajax)最近掀起的高潮,要完全归功于Google在Google Suggest和Google Maps中的使用。对ASP.NET而言,Ajax不需要回传就能进行服务器端处理,从而使客户机(浏览器)具有丰富的服务器端能力。换句话说,它为异步指派和处理请求与服务器响应提供了一个框架。Ajax利用了一些不是很新颖的已有技术,但是对这些技术(加到一起就是Ajax)的爱好最近突然升温。
请尝试Michael Schwarz的AJAX .NET包装器,通过它ASP.NET开发人员可以快速方便的部署很容易利用AJAX功能的页面。需要注意的是,这个包装器处于初期开发阶段,因此还没有完全成熟。
然而,AJAX这样的技术很可能破坏分层体系结构(N-Tier)。我的看法是,AJAX增加了表示逻辑层(甚至更糟,业务层)渗透到表示层的可能性。像我这样严肃的架构师对这种想法可能畏步不前。我感到AJAX的使用即便稍微越过了层次边界,这种代价也是值得深思的。当然,这要视具体的项目和环境而定。
起步
它是如何工作的——概述
AJAX依靠代理(broker)指派和处理往返服务器的请求。对此,.NET包装器依靠客户端XmlHttpRequest对象。多数浏览器都支持XmlHttpRequest对象,这就是选择它的原因。因为包装器的目的是隐藏XmlHttpRequest的实现,我们就不再详细讨论它了。
包装器本身通过将.NET函数标记为AJAX方法来工作。标记之后,AJAX就创建对应的JavaScript函数,这些函数(和任何JavaScript函数一样)作为代理可以在客户端使用XmlHttpRequest调用。这些代理再映射回服务器端函数。
复杂吗?并不复杂。我们来看一个例子。假设有一个.NET函数:
public int Add(int firstNumber, int secondNumber)
{
return firstNumber + secondNumber;
}
Ajax .NET包装器将自动创建名为“Add”、带有两个参数的JavaScript函数。使用JavaScript(在客户机上)调用该函数时,请求将传递给服务器并把结果返回给客户机。
初始设置
我们首先介绍“安装”项目中使用的.dll的步骤。如果您很清楚如何添加.dll文件引用,可以跳过这一节。
首先,如果还没有的话,请下载最新的AJAX版本。解压下载的文件并把Ajax.dll放到项目的引用文件夹中。在Visual Studio.NET中有机Solution Explorer的“References(引用)”节点并选择Add Reference(添加引用)。在打开的对话框中,单击Browse(浏览)并找到ref/Ajax.dll文件。依次单击Open(打开)和Ok(确认)。这样就可以用AJAX .NET包装器编程了。
建立HttpHandler
为了保证正常工作,第一步是在web.config中设置包装器的HttpHandler。不需要详细解释HttpHandlers是什么及其如何工作,只要知道它们用于处理ASP.NET请求就足够了。比如,所有*.aspx页面请求都由System.Web.UI.PageHandlerFactory类处理。类似的,我们让所有对Ajax/*.ashx的请求由Ajax.PageHandlerFactory处理:
<configuration>
<system.web>
<httpHandlers>
<add verb="POST,GET" path="Ajax/*.ashx"
type="Ajax.PageHandlerFactory, Ajax" />
</httpHandlers>
<system.web>
</configuration>
简言之,上面的代码告诉ASP.NET,和指定路径(Ajax/*.ashx)匹配的任何请求都由Ajax.PageHandlerFactory而不是默认处理程序工厂来处理。不需要创建Ajax子目录,使用这个神秘的目录只是为了让其他HttpHandlers能够在自己建立的子目录中使用.ashx扩展。
建立页面
现在我们可以开始编码了。创建一个新页面或者打开已有的页面,在file后的代码中,为Page_Load事件添加以下代码:
public class Index : System.Web.UI.Page{
private void Page_Load(object sender, EventArgs e){
Ajax.Utility.RegisterTypeForAjax(typeof(Index));
//
}
//
}
调用RegisterTypeForAjax将在页面上引发后面的JavaScript(或者在页面中手工加入以下两行代码):
<script language="javascript" src="Ajax/common.ashx"></script>
<script language="javascript"
src="Ajax/Namespace.PageClass,AssemblyName.ashx"></script>
其中最后一行的含义是:
Namespace.PageClass——当前页面的名称空间和类(通常是@Page指令中Inherits属性的值)
AssemblyName——当前页面所属程序集的名称(通常就是项目名)
下面是AjaxPlay项目中sample.aspx页面的结果例子:
<%@ Page Inherits="AjaxPlay.Sample" Codebehind="sample.aspx.cs" %>
<html>
<head>
<script language="javascript" src="Ajax/common.ashx"></script>
<script language="javascript"
src="Ajax/AjaxPlay.Sample,AjaxPlay.ashx"></script>
</head>
<body>
<form id="Form1" method="post" runat="server">
</form>
</body>
</html>
可以在浏览器中手工导航到src路径(查看源代码,复制粘贴路径)检查是否一切正常。如果两个路径都输出一些(似乎)毫无意义的文本,就万事大吉了。如果什么也没输出或者出现ASP.NET错误,则表明有些地方出现问题。
即便不知道HttpHandlers如何工作,上面的例子也很容易理解。通过web.config,我们已经保证所有对Ajax/*.ashx的请求都由自定义的处理程序处理。显然,这里的两个脚本标签将由自定义的处理程序处理。
创建服务器端函数
现在来创建可从客户端调用中异步访问的服务器端函数。因为目前还不支持所有的返回类型(不用担心,将在目前的基础上开发新的版本),我们继续使用简单的ServerSideAdd函数吧。在file后的代码中,向页面添加下列代码:
[Ajax.AjaxMethod()]
public int ServerSideAdd(int firstNumber, int secondNumber)
{
return firstNumber + secondNumber;
}
要注意,这些函数具有Ajax.AjaxMethod属性集。该属性告诉包装器这些方法创建javaScript代理,以便在客户端调用。
客户端调用
最后一步是用JavaScript调用该函数。AJAX包装器负责创建带有两个参数的JavaScript函数Sample.ServerSideAdd。对这种最简单的函数,只需要调用该方法并传递两个数字:
<%@ Page Inherits="AjaxPlay.Sample" Codebehind="sample.aspx.cs" %>
<html>
<head>
<script language="javascript" src="Ajax/common.ashx"></script>
<script language="javascript"
src="Ajax/AjaxPlay.Sample,AjaxPlay.ashx"></script>
</head>
<body>
<form id="Form1" method="post" runat="server">
<script language="javascript">
var response = Sample.ServerSideAdd(100,99);
alert(response.value);
</script>
</form>
</body>
</html>
当然,我们不希望仅仅用这种强大的能力来警告用户。这就是所有客户端代理(如JavaScript Sample.ServerSideAd函数)还接受其他特性的原因。这种特性就是为了处理响应而调用的回调函数:
Sample.ServerSideAdd(100,99, ServerSideAdd_CallBack);
function ServerSideAdd_CallBack(response){
if (response.error != null){
alert(response.error);
return;
}
alert(response.value);
}
从上述代码中可以看到我们指定了另外一个参数。ServerSideAdd_CallBack(同样参见上述代码)是用于处理服务器响应的客户端函数。这个回调函数接收一个响应对象,该对象公开了三个主要性质
Value——服务器端函数实际返回的值(无论是字符串、自定义对象还是数据集)。
Error——错误消息,如果有的话。
Request——xml http请求的原始响应。
Context——上下文对象。
首先我们检查error只看看是否出现了错误。通过在服务器端函数中抛出异常,可以很容易处理error特性。在这个简化的例子中,然后用这个值警告用户。Request特性可用于获得更多信息。
处理类型
返回复杂类型
Ajax包装器不仅能处理ServerSideAdd函数所返回的整数。它目前还支持integers、strings、double、booleans、DateTime、DataSets和DataTables,以及自定义类和数组等基本类型。其他所有类型都返回它们的ToString值。
返回的DataSets和真正的.NET DataSet差不多。假设一个服务器端函数返回DataSet,我们可以通过下面的代码在客户端显示其中的内容:
<script language="JavaScript">
//Asynchronous call to the mythical "GetDataSet" server-side function
function getDataSet(){
AjaxFunctions.GetDataSet(GetDataSet_callback);
}
function GetDataSet_callback(response){
var ds = response.value;
if(ds != null && typeof(ds) == "object" && ds.Tables != null){
var s = new Array();
s[s.length] = "<table border=1>";
for(var i=0; i<ds.Tables[0].Rows.length; i++){
s[s.length] = "<tr>";
s[s.length] = "<td>" + ds.Tables[0].Rows[i].FirstName + "</td>";
s[s.length] = "<td>" + ds.Tables[0].Rows[i].Birthday + "</td>";
s[s.length] = "</tr>";
}
s[s.length] = "</table>";
tableDisplay.innerHTML = s.join("");
}
else {
alert("Error. [3001] " + response.request.responseText);
}
}
</script>
Ajax还可以返回自定义类,唯一的要求是必须用Serializable属性标记。假设有如下的类:
[Serializable()]
public class User{
private int _userId;
private string _firstName;
private string _lastName;
public int userId{
get { return _userId; }
}
public string FirstName{
get { return _firstName; }
}
public string LastName{
get { return _lastName; }
}
public User(int _userId, string _firstName, string _lastName){
this._userId = _userId;
this._firstName = _firstName;
this._lastName = _lastName;
}
public User(){}
[AjaxMethod()]
public static User GetUser(int userId){
//Replace this with a DB hit or something :)
return new User(userId,"Michael", "Schwarz");
}
}
我们可以通过调用RegisterTypeForAjax注册GetUser代理:
private void Page_Load(object sender, EventArgs e){
Utility.RegisterTypeForAjax(typeof(User));
}
这样就可以在客户端异步调用GetUser:
<script language="javascript">
function getUser(userId){
User.GetUser(GetUser_callback);
}
function GetUser_callback(response){
if (response != null && response.value != null){
var user = response.value;
if (typeof(user) == "object"){
alert(user.FirstName + " " + user.LastName);
}
}
}
getUser(1);
</script>
响应中返回的值实际上是一个对象,公开了和服务器端对象相同的属性(FirstName、LastName和UserId)。
自定义转换器
我们已经看到,Ajax .NET包装器能够处理很多不同的.NET类型。但是除了大量.NET类和内建类型以外,包装器对不能正确返回的其他类型仅仅调用ToString()。为了避免这种情况,Ajax .NET包装器允许开发人员创建对象转换器,用于在服务器和客户机之间平滑传递复杂对象。
其他事项
在其他类中注册函数
上面的例子中,我们的服务器端函数都放在执行页面背后的代码中。但是,没有理由不能把这些函数放在单独的类文件中。要记住,包装器的工作方式是在指定类中发现所有带Ajax.AjaxMethod的方法。需要的类通过第二个脚本标签指定。使用Ajax.Utility.RegisterTypeForAjax,我们可以指定需要的任何类。比如,将我们的服务器端函数作为单独的类是合情合理的:
Public Class AjaxFunctions
<Ajax.AjaxMethod()> _
Public Function Validate(username As String, password As String) As Boolean
'do something
'Return something
End Function
End Class
通过指定类的类型而不是页面就可以让Ajax包装器创建代理:
private void Page_Load(object sender, EventArgs e){
Ajax.Utility.RegisterTypeForAjax(typeof(AjaxFunctions));
//
}
要记住,客户端代理的名称是<ClassName>.<ServerSideFunctionName>。因此,如果ServerSideAdd函数放在上面虚构的AjaxFunctions类中,客户端调用就应该是: AjaxFunctions.ServerSideAdd(1,2)。
代理到底是如何工作的
Ajax工具生成的第二个脚本标签(也可以手工插入)传递了页面的名称空间、类名和程序集。根据这些信息,Ajax.PageHandlerFactory就能够使用反射得到具有特定属性的任何函数的详细信息。显然,处理函数查找具有AjaxMethod属性的函数并得到它们的签名(返回类型、名称和参数),从能够创建必要的客户端代理。具体而言,包装器创建一个和类同名的JavaScript对象,该对象提供代理。换句话说,给定一个带有Ajax ServerSideAdd方法的服务器端类AjaxFunctions,我们就会得到公开ServerSideAdd函数的AjaxFunction JavaScript对象。如果将浏览器指向第二个脚本标签的路径就会看到这种动作。
返回Unicode字符
Ajax .NET包装器能够从服务器向客户机返回Unicode字符。为此,数据在返回之前必须在服务器上用html编码。比如:
[Ajax.AjaxMethod]
public string Test1(string name, string email, string comment){
string html = "";
html += "Hello " + name + "<br>";
html += "Thank you for your comment <b>";
html += System.Web.HttpUtility.HtmlEncode(comment);
html += "</b>.";
return html;
}
SessionState
服务器端函数中很可能需要访问会话信息。为此,只需要通过传递给Ajax.AjaxMethod属性的一个参数告诉Ajax启用这种功能。
在考察包装器会话能力的同时,我们来看看其他几个特性。这个例子中我们有一个文档管理系统,用户编辑的时候会对文档加锁。其他用户可以请求在文档可用的时候得到通知。如果没有AJAX,我们就只能等待该用户再次返回来检查请求的文档是否可用。显然不够理想。使用支持会话状态的Ajax就非常简单了。
首先来编写服务器端函数,目标是循环遍历用户希望编辑的documentId(保存在会话中)并返回所有已释放的文档。
[Ajax.AjaxMethod(HttpSessionStateRequirement.Read)]
public ArrayList DocumentReleased(){
if (HttpContext.Current.Session["DocumentsWaiting"] == null){
return null;
}
ArrayList readyDocuments = new ArrayList();
int[] documents = (int[])HttpContext.Current.Session["DocumentsWaiting"];
for (int i = 0; i < documents.Length; ++i){
Document document = Document.GetDocumentById(documents[i]);
if (document != null && document.Status == DocumentStatus.Ready){
readyDocuments.Add(document);
}
}
return readyDocuments;
}
}
要注意,我们指定了HttpSessionStateRequirement.Read值(还可以用Write和ReadWrite)。
现在编写使用该方法的JavaScript:
<script language="javascript">
function DocumentsReady_CallBack(response){
if (response.error != null){
alert(response.error);
return;
}
if (response.value != null && response.value.length > 0){
var div = document.getElementById("status");
div.innerHTML = "The following documents are ready!<br />";
for (var i = 0; i < response.value.length; ++i){
div.innerHTML += "<a href=/"edit.aspx?documentId=" + response.value[i].DocumentId + "/">" + response.value[i].Name + "</a><br />";
}
}
setTimeout('page.DocumentReleased(DocumentsReady_CallBack)', 10000);
}
</script>
<body onload="setTimeout('Document.DocumentReleased(DocumentsReady_CallBack)', 10000);">
我们的服务器端函数在页面加载时调用一次,然后每隔10秒钟调用一次。回调函数检查响应看看是否有返回值,有的话则在div标签中显示该用户可使用的新文档。
结束语
AJAX技术已经催生了原来只有桌面开发才具备的健壮而丰富的Web界面。Ajax .NET包装器让您很容易就能利用这种新的强大技术。请注意,Ajax .NET包装器和文档仍在开发之中。
列名
|
类型
|
默认值
|
说明
|
NodeID
|
Int
|
|
本节点索引
注:此节点须唯一值
|
NodeName
|
String
|
|
本节点名称
|
ParentID
|
Int
|
ParentID=0
|
父节点索引
|
|
|
|
|
属性名
|
类型
|
默认值
|
说明
|
PageName
|
string
|
PageName=""
|
需要在调用此组件的
Web窗体的aspx页面顶端
Inherits的值 必填
|
TreeTitleText
|
string
|
TreeTitleText="默认标题
"
|
树型的标题名称
|
TreeTopID
|
int
|
TreeTopID=0
|
自定义顶层节点索引
|
TreeTopName
|
string
|
TreeTopName="默认类别
"
|
自定义顶层节点名称
|
AddPopedom
|
int
|
AddPopedom=0
|
添加节点权限
注:1为有权限;0为无权限
|
EditPopedom
|
int
|
EditPopedom=0
|
修改节点权限
注:1为有权限;0为无权限
|
DelPopedom
|
int
|
DelPopedom=0
|
删除节点权限
注:1为有权限;0为无权限
|
ManagePage
|
string
|
ManagePage
="./WelcomePage.aspx"
|
自定义处理页
注:增删改操作的页面
|
|
|
|
|
属性名
|
类型
|
默认值
|
说明
|
NodeID
|
string
|
NodeID=
|
本节点索引
注:此节点须唯一值
|
ParentID
|
string
|
ParentID=0
|
父节点索引
注:[顶层]新增节点时的
ParentID
为空
|
Do
|
string
|
Do=add
|
表示此操作为:
添加节点
|
Do=edit
|
表示此操作为:
修改节点
| ||
Do=del
|
表示此操作为:
删除节点
| ||
|
|
|
|