// Check to see if the contents are in the Data Cache
            if(Cache[categoryKey] != null){

            // If the data is already cached, then used the cached copy
            products.DataSource = (IList)Cache[categoryKey];

            }else{

            // If the data is not cached, 
            // then create a new products object and request the data
            Product product = new Product();
            IList productsByCategory = product.GetProductsByCategory(categoryKey);
               
            // Store the results of the call in the Cache 
            // and set the time out to 12 hours
            Cache.Add(categoryKey, productsByCategory, null, DateTime.Now.AddHours(12),        Cache.NoSlidingExpiration , CacheItemPriority.High, null);
               
            products.DataSource = productsByCategory;
            }

            // Bind the data to the control
            products.DataBind();
            
返回页首
小结
.NET Pet Shop 的体系结构已经进行了改进,能够在部署选择方面提供更灵活的解决方案,应用程序能够更加容易地进行自定义,适应业务模型中的变化。 尽管有所有这些变化,Pet Shop 的性能与 2.0 版实现大致相同,说明了 Microsoft .NET 框架在 J2EE 之外提供了另一种可行的企业级解决方案。 通过 .NET Pet Shop 和 J2EE Pet Store,架构师和开发人员可以使用功能完全一样、说明了各自平台上最佳编程实践的参考应用程序,一对一地比较 .NET 和 J2EE。 即将进行的 Middleware 基准测试将测试新的 .NET Pet Shop 3.0 实现,将比较它与两个新 J2EE 实现——一个基于 CMP 2.0,另一个基于纯粹的 JSP/Servlet 体系结构(不使用 EJB)的性能。 测试将由 Middleware 公司进行,在运行着各种 J2EE 应用程序服务器的服务器端发布。 将组成一个 J2EE 专家评委会监督这一基准测试,以确保对被测试产品的公平性以及所有实现都符合规范和最佳准则。 此外,已经邀请了主要的 J2EE 应用程序服务器供应商,加上 Microsoft 来参与这一基准测试,他们将对规范进行评价,提供供测试的实现,并现场进行微调和优化,参与测试过程。 Microsoft 选择全面参与第二轮测试。 可以到这里 了解 Middleware Application Server 测试第一轮的细节,测试基于两个基准测试应用程序:早期的 .NET Pet Shop 2.0 和一个基于 BMP 的 J2EE 实现,或者下载完整的报告。

返回页首
附录 A: 从版本 2 到版本 3 的更改
项目 说明 原因 
1
 创建数据库访问层 (DAL)。
 可以将业务逻辑从数据库访问代码中完全分离出来,因此可以无需更改业务逻辑代码,即可更换数据库供应商。
 
2
 为所有瘦数据类(模型)创建公共的项目。
 模型项目只包含保存数据的瘦类,可以用在应用程序的每一层,作为传输数据的容器。所有模型类都通过 [Serializable] 标记支持序列化,以添加对群集和任何未来物理部署中变化的增强支持。
 
3
 在 Components 项目中删除对 System.Web 的引用。
 这是一种好的设计实践,因为可以使中间层组件用于不同类型的 UI。
 
4
 将一些自定义服务器控件改为 Web 用户控件;然而 Pager 控件仍然保留为自定义服务器控件。
 标头和标题控件含有许多 HTML/UI 内容,因此如果实现为 Web 用户控件更易维护。 Pager 控件处理的是查询字符串的操作,视图状态中数据的使用,因此作为自定义服务器控件更合适。
 
5
 为 Oracle 创建了 DAL 实现。
 为了支持 Oracle 数据库,创建了特定的 Oracle DAL,可以使用 Oracle 特定的驱动程序
 
6
 DAL 层现在实现了工厂模式 [GoF] 以加载相应的供应商特定的 DAL。
 为了隐藏后端所使用的数据库,使用工厂方法将接口返回到要用到的 DAL 层。
 
7
 模型类中的所有公共字段都转换为属性。
 这样字段的存储机制就可以隐藏起来,如果要看什么函数修改了某些数据,这提供了一个很好的在代码中添加断点的地方。
 
8
 创建数据库访问层 (DAL)。
 可以将业务逻辑从数据库访问代码中完全分离出来,因此可以无需更改业务逻辑代码,即可更换数据库供应商。
 
9
 将所有静态方法改为实例方法。
 根据评论反馈进行的修改
 
10
 将 assemblyinfo.cs 中的版本号改为与部署匹配的具体版本。
 在 DALFactory 对象中加载程序集时,允许指定证据。
 
11
 在 Visual Studio .NET 解决方案中添加构建前和构建后的步骤。
 这样,在项目编译后,就可以对具体程序集运行 gacutil 和 regsvcs 实用工具。
 
12
 将订购业务组件改名,根据其执行的功能而非实现机制。
 对其他开发人员而言理解组件的功能更直观。
 
13
 创建配置工具辅助应用程序的设置。
 需要以管理员组成员的身份创建应用程序事件日志源。 确保完成最简单的方式是提供一个简单的工具,可以在部署后运行。
 
14
 将代码修改为允许帐号和产品数据库以及定单数据库使用不同的 DAL。
 对用户请求的响应应该能够使用混合数据库部署模型。
 
15
 页面中添加页面输出缓存;但是基准测试时要删除。
 有用户请求说明如何重写 VaryByCustom 函数使标题和标头控件等页面能够在 default.aspx 这样的页面中缓存。
 
16
 将处理流程控件类添加到 Web 应用程序中。
 根据评论反馈做的修改,为帐号或购物车这样的 Web 区域提供单一位置来控制定位和状态管理。
 

 



 

关于该系统的大致介绍可以从上面的连接获得,都是中文的。
下面来分析一下PetShop3.0的用户注册部分(我今早上刚研究的,哈,趁热端出来)
PetShop3.0是业务实体和业务逻辑分开的,并且在表示层上也有逻辑处理。业务实体部分从前到后都有用到。实际上,在传递数据的时候就是传递的一个实体,而不是像我们一般用的一个变量一个变量的传,在用户注册中也是这样。
注册页面是CreateAccount.aspx,这里有一个usercontrol:AddressUI,用来收集用户的一般信息,其他的个人网站设定和用户名密码什么的都是分开来取的,通过提取AddressUI.Address来获得一个AddressInfo对象,然后用这些信息创建一个AccountInfo对象,最后调用ProcessFlow.AccountController的CreateAccount方法来完成注册。CreateAccount接收的参数自然是一个AddressInfo类型的对象,返回类型为bool。根据返回值来判断注册是否成功。实际上,它这里假定如果不成功,那就只有一种情况,就是用户名已经被注册了。
接下来的事情就是一层套一层的引用了。把业务实体AccountInfo一层的往下传,最后到达SQLServerDAL层,这里的Insert方法执行最后的操作。
PetSop.Web.ProcessFlow.AccountController : 
public bool CreateAccount(AccountInfo newAccountInfo){

try {
// Creata a new business logic tier
Account account = new Account();

// Call the insert method
account.Insert(newAccountInfo);

// Store the data in session state and store the authenticated cookie
HttpContext.Current.Session[ACCOUNT_KEY] = newAccountInfo;
FormsAuthentication.SetAuthCookie(newAccountInfo.UserId, false);

//Finally forward to the welcome page
HttpContext.Current.Response.Redirect(URL_ACCOUNTCREATE, true);

}
//注意在这里捕获异常,说明用户名已存在。详细描述见下面
catch {
return false;
}

return true;
}

PetShop.BLL.Account : 
public void Insert(AccountInfo account) {

// Validate input
if (account.UserId.Trim() == string.Empty)
return;

// Get an instance of the account DAL using the DALFactory
IAccount dal = PetShop.DALFactory.Account.Create();

// Call the DAL to insert the account
dal.Insert(account);
}

最后进入实际的数据操作层

PetShop.SQLServerDAL.Account : 
public void Insert(AccountInfo acc) {
SqlParameter[] signOnParms = GetSignOnParameters();
SqlParameter[] accountParms = GetAccountParameters();
SqlParameter[] profileParms = GetProfileParameters();

signOnParms[0].Value = acc.UserId;
signOnParms[1].Value = acc.Password;

SetAccountParameters(accountParms, acc);
SetProfileParameters(profileParms, acc);

using (SqlConnection conn = new SqlConnection(SQLHelper.CONN_STRING_NON_DTC)) {
conn.Open();
using (SqlTransaction trans = conn.BeginTransaction()) {
try {
SQLHelper.ExecuteNonQuery(trans, CommandType.Text, SQL_INSERT_SIGNON, signOnParms);
SQLHelper.ExecuteNonQuery(trans, CommandType.Text, SQL_INSERT_ACCOUNT, accountParms);
SQLHelper.ExecuteNonQuery(trans, CommandType.Text, SQL_INSERT_PROFILE, profileParms);
trans.Commit();

}
//违反约束,抛出异常
catch {
trans.Rollback();
throw;
}
}
}
}

那么它是怎么判断用户名是否已经被注册了呢?原来,在保存用户名和密码的表里有一个主键约束,这样自然就插不进重复的用户名。一旦有相同的用户名进入,就会违反约束,抛出异常,当然在之前还要回滚事务,抛出的异常在表示层CreateAccount方法中被捕获,方法返回false,最后反映到页面上。
我在这里就有一个疑问,这样做不是把异常作为一种控制流程的手段了吗?
《effective java》第39条“只针对不正常的条件才使用异常”。根据这一条,万一不能注册是因为其他不可预料的原因而发生的呢?这也会返回给要注册的用户“Duplicate user ID! Please try again.”信息。而且我以前看过一篇文章,说.net的异常抛出会消耗大量的资源,建议不要把异常做为一种实现的方法。其实这里完全可以用T-SQL编程的手段来预先判断用户名是否存在,然后再采取下一步措施。

从数据绑定的知识可以得到,这个id其实是ProductInfo的一个属性。
if(itemsByProduct.Count > 0)
在Items.aspx中进行的数据操作和Category.aspx的基本一样。
Items.aspx.cs里最后有这么一句代码:
productName.Text = ((ItemInfo)itemsByProduct[0]).ProductName;
因为itemsByProduct是一个ArrayList,所以itemsByProduct[0]实际上返回的就是一个ItemInfo。

从上面的分析可以看出,业务逻辑和业务实体分开来的好处。

这里涉及到一个关于Session的问题,由于我们的StoreList保存在了Session中,所以每次操作都要先从Session里把StoreList取出来,但是在操作完后,并没有再把StoreList保存回Session,这主要是因为我们提取出来的并不是Session里保存的值,而只是得到了对Session里保存的值的引用,所以之后的操作其实都是在对Session里保存的值进行,就没有必要最后再保存了。

 

 


if (itemId != null){
// Clean the input string
itemId = WebComponents.CleanString.InputText(itemId, 50);
myCart.Add(itemId);
cartController.StoreCart(myCart);

}
}

先看这一句 myCart = cartController.GetCart(true);
GetCart方法用来生成一个Cart,它是先在Session里检查,如Session里没有保存Cart,就生成一个新的,否则就把保存在Session里的Cart取出来,然后使用Add方法把新的宠物加到Cart里。
public Cart GetCart(bool create){

// Fetch the cart object from session state
Cart myCart = (Cart)HttpContext.Current.Session[CART_KEY];

if ( myCart == null ){
if (create){
myCart = new Cart();
}else{ 
HttpContext.Current.Server.Transfer(URL_NOCART);
return null;
}
}

return myCart;
}

下面是Add方法的实现
public void Add(string ItemId) {
// _items是在Cart里保存的宠物集合,通过遍历来判断这是否是一个新类别
foreach (CartItemInfo cartItem in _items) {
if (ItemId == cartItem.ItemId) {
cartItem.Quantity++;
cartItem.InStock = (GetInStock(ItemId) - cartItem.Quantity) >= 0 ? true : false;
_total = _total+(cartItem.Price*cartItem.Quantity);
return;
}
}

//是一个新类别,则把一个CartItemInfo加入Cart,可以在这里看到CartItemInfo的组成
Item item = new Item();

ItemInfo data = item.GetItem(ItemId);
CartItemInfo newItem = new CartItemInfo(ItemId,data.Name, (data.Quantity >= 1), 1, (decimal)data.Price); 
_items.Add(newItem);
_total = _total+(data.Price);
}

这就完成了添加。然后是数量的更改。
// Check for update button
if (e.CommandName == CMD_UPDATE){
TextBox txt;
int qty;
int index;

// Go through each item on the page
for (int i = 0, j = cart.Items.Count; i < j; i++){

// lookup the control
txt = (TextBox)cart.Items[i].FindControl(ID_TXT);

try{
qty = int.Parse(txt.Text);
index = cart.CurrentPageIndex * cart.PageSize + i;

// If the new qty is zero, remove the item from the cart
if (qty <= 0)
myCart.RemoveAt(index); 
// Update the item with the new quantity
else
myCart[index].Quantity = qty;
}
catch {}
}

}else
// otherwise the command is to remove the an item
myCart.Remove((string)e.CommandArgument);

因为宠物的添加是先于数量的更改进行的,所以到了这里Session里肯定有保存Cart。数量的更改直接同过索引器来完成,更改后直接就会保存。Remove和RemoveAt都实现了从Cart删除指定的CartItemInfo。

ShoppingCart.aspx页还有其他的东西,比如一个继承自SimplePager的自定义控件,,还有判断是否显示用户喜好的宠物列表的判断。
Refresh方法用来重新计算total的值,哈,不过我不清楚微软究竟想拿这个值显示什么?在我下的这个版本里,根本就不是subtotla的总和,而是price的总和,但问题在于,当你把一种宠物从Cart里移除的时候,它竟然会total=total-subtotal,因此常常会出现负数……

购物车从开始到最后销毁,都是在和Session打交道,没有任何与数据库的交互。我不是很了解面向对象技术,但我觉得oo在这里得到了很好的体现。

 


点proceed to checkout后,就进入Checkout.aspx,确认后进入OrderBilling.aspx,在这里可以修改你的信息,完成后点continue,会出现个人信息的只读页面,最终确认后就进入OrderProcess.aspx,在这里是定单的详细情况,并且是只读的,到这里,定单被添加到数据库,购物完成。
Checkout.aspx把数据从Session中取出来,然后显示到页面,没什么好说的。 
OrderBilling.aspx,这个页面一开始显示的信息可写,我们看OnLoad事件中,是用ProcessFlow.AccountController.GetAccountInfo获得用户的信息CreditCardInfo,然后显示在一些可写的控件中,比如用户控件:StaticAddress。当点击确认后,使用StoreCreditCard把刚才获得的用户信息保存到Sessin,准备呆会用。
OrderProcess.aspx是最终的定单生成页面。主要就是一个方法:
ProcessFlow.CartController.PurchaseCart 来看它的实现
public OrderInfo PurchaseCart(){

// Fetch the cart from session
Cart myCart = (Cart)HttpContext.Current.Session[CART_KEY];

// Make some checks on the cart
if ( ( myCart == null ) || ( myCart.Count==0 ) ) {

HttpContext.Current.Server.Transfer(URL_NOCART);
//HttpContext.Current.Response.Redirect(URL_NOCART, false);
return null;

}else{

// Build up the order
OrderInfo newOrder = new OrderInfo();
newOrder.UserId = ((AccountInfo)HttpContext.Current.Session[ACCOUNT_KEY]).UserId;
newOrder.CreditCard = (CreditCardInfo)HttpContext.Current.Session[CREDITCARD_KEY];
newOrder.BillingAddress = (AddressInfo)HttpContext.Current.Session[BILLING_KEY];
newOrder.ShippingAddress = (AddressInfo)HttpContext.Current.Session[SHIPPING_KEY];

newOrder.LineItems = (LineItemInfo[])myCart.GetOrderLineItems().ToArray(typeof(LineItemInfo));

newOrder.OrderTotal = myCart.Total; 
newOrder.Date = DateTime.Now;

// Send the order to the middle tier
OrderInsert order = new OrderInsert(); 
//向数据库插入数据
newOrder.OrderId = order.Insert(newOrder);

// clear the session objects used
HttpContext.Current.Session[CART_KEY] = null;
HttpContext.Current.Session[CREDITCARD_KEY] = null;
HttpContext.Current.Session[BILLING_KEY] = null;
HttpContext.Current.Session[SHIPPING_KEY] = null;

return newOrder;
}
}

Order主要是由保存在Session里的值形成,其中包括购物车。
在Order有一个LineItemInfo,它是由Cart.GetOrderLineItems方法依据cart里CartItemInfo的值返回的,是定单的物品部分,和CartItemInfo相比,主要是多了一个Line属性,这个Line是用来表示物品在定单内的序号。
下面是Insert的实现。
public int Insert(OrderInfo order) {

// Get an instance of the Order DAL using the DALFactory
IOrder dal = PetShop.DALFactory.Order.Create();

// Call the insert method in the DAL to insert the header
int orderId = dal.Insert(order);

// Get an instance of the Inventory business component
Inventory inventory = new Inventory();
//向数据库插入库存信息
inventory.TakeStock( order.LineItems);

// As part of the sample application we have created a user 
// you can tested distributed transactions with
// If the order has been created with the user 'Acid', 
// then throw an exception which will rollback the entire transaction
if (order.UserId == ACID_USER_ID)
throw new ApplicationException(ACID_ERROR_MSG);

// Set the orderId so that it can be returned to the caller
return orderId;
}

大体就这么多了,表示层的东西到这里基本上就算全看完了,嘿嘿


图 10:带有 CRUD 行为的自定义业务实体组件的作用(单击缩略图以查看大图像) 

定义带有 CRUD 行为的自定义实体类的优点如下: 

封装。自定义实体可以封装由基础数据访问逻辑组件定义的操作。 
与调用程序的接口。调用程序必须只处理一个接口来保持业务实体数据。不必直接访问数据访问逻辑组件。 
专用字段。您可以隐藏不希望向调用程序公开的信息。 
定义带有 CRUD 行为的自定义实体类的缺点如下: 

处理业务实体集合。自定义实体中的方法属于单个业务实体实例。要支持业务实体集合,可以定义静态方法以读取或返回一个数组或一个实体组件集合。 
开发时间长。传统的面向对象方法通常比使用现有对象(如 DataSet)需要更多的设计和开发工作。 

 

 

SimplePager是继承自Repeater的一个自定义控件,在整个站中应用很广,下面通过Category.aspx这个页面具体的研究研究:)
先来看Category.aspx.cs,很奇怪吧,怎么没有和Load事件相关的方法呢?只有一个控制分页的PageChanged,而这个分页方法也有问题,每次都是获得缓存里的数据,然后改变的只CurrentPageIndex属性而已,并且该方法接受的参数竟然是DataGridPageChangedEventArgs,真晕,这个控件是一个Repeater嘛……
下面就让我们打开SimplePager.cs来看个究竟吧~~(两天的感冒刚好,目前精神百倍中……),其实看这个的时候最好是用.NET Reflector来查看,这样比较容易好辨认(我现在的电脑就把打开dll文件的默认程序设成了.NET Reflector)。
先来看第一个问题,在具体的页面中没有Load事件,那肯定是在控件的内部做了手脚

override protected void OnLoad(EventArgs e) {
if (Visible) {
string page = Context.Request[KEY_PAGE];
int index = (page != null) ? int.Parse(page) : 0;
SetPage(index);
}
}
当Load事件发生时就调用SetPage方法
public void SetPage(int index) {
//对DataGridPageChangedEventArgs有疑惑的可以看文章的最后
OnPageIndexChanged(new DataGridPageChangedEventArgs(null, index));
}
然后是OnPageIndexChanged
virtual protected void OnPageIndexChanged(DataGridPageChangedEventArgs e) {
if (PageIndexChanged != null)
PageIndexChanged(this, e);
}
PageIndexChanged是一个事件,使用了DataGridPageChangedEventHandler作为它的委托
public event DataGridPageChangedEventHandler PageIndexChanged;

这样就不难理解了,PageChanged就是在页面加载时的“Page_Load”方法,并且还是页数改变时相应的处理方法。换句话说,每次进入该页,程序都当成页数改变事件来处理。
从上面我们可以看到一个问题,即在这三个连续的方法中页数的传递都是通过
string page = Context.Request[KEY_PAGE];这里获得的值来进行的,好象CurrentPageIndex属性根本就没有用……
CurrentPageIndex的作用主要体现在分页方法这里
override protected void OnDataBinding(EventArgs e) {

//Work out which items we want to render to the page
int start = CurrentPageIndex * pageSize;
int size = Math.Min(pageSize, ItemCount - start);

IList page = new ArrayList();

//Add the relevant items from the datasource
for (int i = 0; i < size; i++)
page.Add(dataSource[start + i]);

//set the base objects datasource
base.DataSource = page; 
base.OnDataBinding(e);

}

OnDataBinding是当服务器控件绑定到数据源时发生的DataBinding事件的处理方法。在这里有机会对数据源进行处理。而这里的确就是对数据源进行了处理,取到当前页的数据并把他们作为新的数据源赋给控件。
for (int i = 0; i < size; i++)
page.Add(dataSource[start + i]);
因为dataSource是实现了IList接口的数据,所以可以这样用索引器来操作数据,再往里面看,这些数据是什么呢?好象以前讨论过这个,数据源实际是一个ArrayList,里面放的是ProductInfo实体。
看到这里,或许你和我一样,会有这样的疑惑,既然在Load阶段就接收到了一个页数,为什么在这里还要另外的用一个,直接用一个不可以吗?我在这里的看法是,这样可以使程序的可读性更高,并且使控件的用户接口更加友好。
或者,那个变量根本就传不到OnDataBinding方法里面?这个我没有调试过,感兴趣的可以试试。
除了这里,Render方法也用CurrentPageIndex来实现上一页和下一页连接的显示。
顺便说一下ViewStatePager,这个是SimplePager的子类,主要的区别是该控件识别页数时使用了ViewState来完成,这样不至于和同一页面上的其他需要分页的控件(比如SimplePager)冲突。
最后说一个小发现
public DataGridPageChangedEventArgs(object commandSource, int newPageIndex)
{
this.commandSource = commandSource;
this.newPageIndex = newPageIndex;
}
这是通过.NET Reflector得到的DataGridPageChangedEventArgs的构造函数
public int NewPageIndex
{
get
{
return this.newPageIndex;
}
}
这是通过.NET Reflector得到的DataGridPageChangedEventArgs的NewPageIndex属性的定义。

 

/// <returns>Cached SqlParamters array</returns>
public static SqlParameter[] GetCachedParameters(string cacheKey) {
//从缓存中取出值 
SqlParameter[] cachedParms = (SqlParameter[])parmCache[cacheKey];
if (cachedParms == null)
return null;
SqlParameter[] clonedParms = new SqlParameter[cachedParms.Length];
//使用clone通过遍历得到一个新的参数数组
for (int i = 0, j = cachedParms.Length; i < j; i++)
clonedParms[i] = (SqlParameter)((ICloneable)cachedParms[i]).Clone();
return clonedParms;
}

用来缓存参数的parmCache实际上一个private static Hashtable,因为是static,所以自然是独一无二的了,创建一次就可以永远使用,起到和缓存差不多的效果。
在返回参数时要用clone来创建一个新的参数数组返回,是因为parmCache只有一个,而返回的参数数组则至少一个人用一个。更为重要的是,现在保存在parmCache的参数列表都是没有具体值的参数,而返回后则根据每个用户的不同情况而被赋于不同的值。

从这里子可以看出,有一个自己的daab是多么的重要。先前在论坛看到过有人喜欢用拖的方式来进行数据库的连接,那种做法我觉得不是很好,手写代码很重要的,写一个自己顺手的daab更是重要。

 

 


 

看!这样根据系统当前Web.config文件的配置描述(这也应该是系统运行时实际的配置),BLL层只要像下面这样:

// Get an instance of the account DAL using the DALFactory

IAccount dal = PetShop.DALFactory.Account.Create();

AccountInfo account = dal.SignIn(userId, password);//<<?----看看上面第4点的IAccount接口

就可以直接调用接口方法通过下层DAL层操作数据库了(在此具体为用户账号相关操作),而BLL层并不用知道应该通过SqlserverDAL还是OracleDAL访问数据库,这由都DAL Factory决定,你用的是什么数据库以及底层细节,更不用BLL知道,这样做的好处是对于BLL层以及更上层的程序不会或很少机率会因为底层程序变动影响,因为BLL层中调用接口就行了,只要那个接口定义没变,一切仍然OK.


3.相关sdk文档资料。

Assembly 类
定义一个 Assembly,它是可重用、无版本冲突并且可自我描述的公共语言运行库应用程序构造块。

有关此类型所有成员的列表,请参阅 Assembly 成员。

[Visual?Basic]
<Serializable>
<ClassInterface(ClassInterfaceType.AutoDual)>
Public Class Assembly
   Implements IEvidenceFactory, ICustomAttributeProvider, _
   ISerializable
public static Assembly Load(string assemblyString);
描述 
通过给定程序集的长格式名称加载程序集。
参数
assemblyString 
程序集名称的长格式。 
返回值
加载的程序集。

public object CreateInstance(string typeName);
描述 
使用区分大小写的搜索,从此程序集中查找指定的类型,然后使用系统激活器创建它的实例。
参数
typeName 
要查找的类型的 Type.FullName。 
返回值
表示该类型的 Object 的实例,其区域性、参数、联编程序和激活属性设置为空引用(Visual Basic 中为 Nothing),并且 BindingFlags 设置为 Public 或 Instance,或者设置为空引用 (Nothing)(如果没有找到 typeName)。



相关文章: