[转载]Variant 与 内存泄露

mikel阅读(1065)

[转载]Variant 与 内存泄露 – CPP魔幻游乐场 – 博客园.

今天遇到一个内存泄露的问题。是师兄检测出来的。Variant类型在使用后要Clear否则会造成内存泄露,为什么呢?

Google一下找到下面一篇文章,主要介绍了Com的内存泄露,中间有对Variant的一些解释吧。

1. 引用计数泄漏
由于C++的一些对象生命周期难以管理,在COM中加入了引用计数,用来解决这个问题,引用计数是COM最重要的特性 之一。但讽刺的是,虽然很多COM组件是用C++写的,但是在所有编程语言中,C++使用COM是最麻烦的,而且最容易产生引用计数的泄漏,从而最终造成 内存泄漏。

引用计数泄漏一旦产生,比new了之后忘了delete还难以定位,因为一个对象可能被很多地方使用,根本就不知道到底是那个使用的地方在 AddRef之后没有Release,只能知道对象泄漏了,但不知道是哪里导致泄漏。

因此在使用COM的时候,尽量使用智能指针,而且最好是所有使用COM的地方全部使用智能指针,这样能在很大成都上防止产生COM对象的引用计数泄 漏。

然而使用了智能指针一定能解决问题吗?也不一定,至少有以下两种情况,使用了智能指针仍然会产生引用计数泄漏,而且比上面的情况更加难以找到原因:

对象循环引用导致引用计数泄漏
如果有两个对象A和B,在A中使用了B,同时在B中使用了A,B对象中有一个智能指针指向A,A对象中有一 个智能指针指向B,这时候就会产生循环引用导致。因为A对象能被析构的前提是B对象先析构,而B对象析构的前提同样是A对象先析构,这就成了一个死局,两 个对象都无法析构,A和B都产生了内存泄漏。

要想解决这种问题,一种方法是把这个循环的链打断,在某个合适的地方把某一个对象中的智能指针显示地把智能指针赋值为NULL,这样循环的条件被打 破,两个对象就都可以析构了。另一种方法是使用弱引用,自动完成。但弱引用相对有点复杂,在本文中不作介绍,读者可以自行在网上搜索相关资料。

不正确的智能指针使用方法导致引用计数泄漏
有时候使用了智能指针,并且也没有循环引用,但是对象仍然有引用计数泄漏,是不是很困惑?对于 这种情况,有可能是因为使用智能指针不正确引起的。

我们有时候会这样使用智能指针:

pUnknown->QueryInterface(__uuidof(IMyInterface), & spMyInterface);

spMyInterface变量是一个智能指针,在大多数情况下这样使用不会有任何问题,尤其是使用了微软的_com_ptr_t或CComPtr 等。但如果我们使用一些第三方的智能指针,例如Boost里的intrusive_ptr。我们知道,在_com_ptr_t和CComPtr中,重载 了&操作符,在&操作符中,会先释放目前的引用计数,然后返回一个指向指针的指针。这样带来的好处是使用&操作符的时候,都会先 释放当前的对象,不会造成内存泄漏,坏处是这个智能指针可能无法放到一些STL的容器中,因为有一些STL的容器,在移动容器中的数据单元时,会用 到&操作符,这样的结果是一旦移动,对象就被释放了。Boost的智能指针,例如intrusive_ptr是没有重载&操作符的,因此 可以放心地在STL容器中使用,但如果写类似于QueryInterface(__uuidof(IMyInterface), & spMyInterface)就要特别小心了,例如像下面的连续两次QueryInterface用法,就会产生引用计数泄漏:

boost::intrusive_ptr    spMyInterface;

pUnknown->QueryInterface(__uuidof(IMyInterface), & spMyInterface);

pUnknown2->QueryInterface(__uuidof(IMyInterface), & spMyInterface);

因为第二次调用QueryInterface之前,spMyInterface并没有Release,因此就有引用计数泄漏了。如果在某个循环中这 样使用,则问题会更加严重。

要解决这类问题,有两种方法。第一是同一个变量永远不要多次使用,在变量声明周期中类似的操作只使用一次,如果有多次使用的需求,用别的变量来实 现。另一种方法是每次使用之前,都先显示地把变量清空,释放引用计数,例如先spMyInterface,再调用QueryInterface。

2. 字符串(BSTR)泄漏
字符串泄漏容易被忽视,而且COM中使用的BSTR字符串,并不是用new或者malloc等来分配的,而 是用类似于::SysAllocString等API来分配的,要检测是否有泄漏更加困难。

我们一般在调用某个类似于这样的函数GetString([out] BSTR * str)的返回BSTR的指针的函数后,需要调用::SysFreeString把字符串释放掉,和new/delete、malloc/free、 AddRef/Release类似。如果忘记调用::SysFreeString,就会产生内存泄漏。

一般我们使用字符串类来自动释放,例如使用CComBSTR,就可以这样写:

CComBSTR str;

GetString(&str);

str在析构的时候会自动调用::SysFreeString把字符串释放掉。

但如果看一下CComBSTR类的实现,我们会发现,这个类和Boost的intrusive_ptr类似,是没有重载&操作符的,这就带 来和intrusive_ptr一样的问题,如果连续两次以上使用这个字符串,就会产生内存泄漏,例如字符串类我们有时候会这样用:

CComBSTR str;

for(int I = 0;I < 100;I ++)

{

pInterface->GetString(&str);

// do something

}

这样的后果是每调用一次,就会 产生一个内存泄漏。因此一定要保证在字符串对象的生命周期中,只被使用一次,代码改为这样就不会有问题:

for(int I = 0;I < 100;I ++)

{

CComBSTR str;

pInterface->GetString(&str);

// do something

}

另外还有一种方法是使用_bstr_t的GetAddress函数,这个函数返回的是 BSTR *,每次调用里面都会先释放当前的字符串,因此使用 _bstr_t属于比较保险并且方便的选择。不过遗憾的是在VC6中这个类并没有实现GetAddress函数,用起来可能会影响代码的移植性。

3. VARIANT泄漏
这个情况和字符串泄漏类似,变量不再使用之后,应该调用::VariantClear来释放内存,否则就可能会 产生内存泄漏。

解决方法也和字符串类似,可以使用VARIANT的CComVariant,但这个类和CComBSTR也有一样的问题,因此使用的时候也一样要注 意,必须保证每个变量的生命周期中只使用一次。

另外有一个和_bstr_t类似的类:_variant_t,里面有一个GetAddress函数可以比较方便和安全的使用。具体的细节请参考这几 个类的实现代码,这里不多做介绍。

4. 总结
对于本文中提到的几种内存泄漏的原因,根源在于使用一个第三方的类的时候,其实并没有真正理解这些类的实现原理,我们以为正确 地使用了,但其实用法并不对。因此在使用一个不是自己写的第三方类的时候,不能直接拿过来就用,而是应该花一点时间去仔细了解一下这个类,知道怎样使用是 合理的,而怎样使用是不正确的,把所有的隐患在编码之前就解决掉,整个项目开发过程就会更加顺利,产品质量就会更高。

关于第三方类库的使用,在本人另一篇博文《编码原则十日谈》中有提及,这里给出地址,供读者参考。

http://blog.csdn.net/cplusplus_zk/archive/2008/11/29/3407731.aspx
文章出处:飞诺网(www.firnow.com):http://dev.firnow.com/course/3_program/c++/cppjs/20091129/183786.html

[转载]Asp.net MVC2.0系列文章-运行Web MVC2.0 Demo

mikel阅读(965)

[转载]Asp.net MVC2.0系列文章-运行Web MVC2.0 Demo – 灵动生活 – 博客园.

安装VS2010

首先安装VS2010,安装过程请参考文章:

http://www.cnblogs.com/ywqu/archive/2010/01/27/1657450.html

创建第一个MVC2.0程序

新建一个ASP.NET MVC2.0网站程序,如下图:

提示是否新建单元测试工程,选择创建Unit Test Project

MVC2.0网站结构图

默认新建一个ASP.NET MVC2.0网站程序,结构图如下所示

Content文件夹:存放CSS样式文件

Controllers文件夹:存放控制器文件,文件名称以Controller结尾

Model文件夹:存放数据模型以及业务逻辑和规则,文件名称以Models结尾

Scripts文件夹:存放JavaScript文件,VS2010整合了JQuery,所以此文件夹下有JQuery类库

View文件夹:存放视图文件。

MvcAppDemo.Tests是默认的单元测试工程。

网站运行结果

运行默认的网站,如下图所示:

首页:

About页面

注册页面

登录页面

在此登录页面中,如果不输入任何信息,点击【Log On】按钮,会触发非空验证。

总结

此篇文章只是让初学者对 MVC2.0网站有个大概的印象,学习和创建MVC2.0网站应用程序并不是想象的那么苦难,不妨动手试一试。接下来文章,我们就使用MVC2.0的新特 性创建一些实例。

版权

作者:灵动生活

出处:http://www.cnblogs.com/ywqu

如果你认为 此文章有用,请点击底端的【推荐】让其他人也了解此文章

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[转载]Flex PropertyGrid 实现

mikel阅读(971)

[转载]Flex PropertyGrid 实现 – 镜涛的家 JT – 博客园.

PropertyGrid在界面设计工具中是比较常用的组件,在.NET的WinForm设计中,PropertyGrid作为内置的组件来实现对 button、label等组件的属性设置,不仅满足了设计时的需要,还能够在运行时提供美观实用的帮助;但是Flex作为目前界面设计展现方向上的主流 技术,并没有提供该组件。为了弥补这一不足,本文将介绍并实现Flex PropertyGrid组件模型。

PropertyGrid模型可以分为两个部分,即行为模型和数据展现模型。

所谓的行为模型就是PropertyGrid所包含的事件及其在不同状态下的展示方式。事件部分我们暂且只支持PropertyGrid的 selectedObjectChanged事件。展示方式主要是悬浮状态下,Mini选项卡开启,鼠标移到Mini选项卡的时候 PropertyGrid主面板弹出,鼠标移出PropertyGrid时,PropertyGrid自动关闭,同时显示出Mini选项卡,;非悬浮状态 下,PropertyGrid停靠在Container中,Mini选项卡关闭。

数据展现模型就是PropertyGrid所展示内容的元数据定义,用这些定义好的元数据标记数据类型后,PropertyGrid可以自动的从数 据类型实例中读取元数据定义,并根据类别、描述等信息自动展示。

事件模型处理相关代码如下

[Event(name=“selectedObjectChanged”,type=“propertygrid.PropertyGridEvent”)]

[Event(name=“floatStateChanged”,type=“propertygrid.PropertyGridEvent”)];

public class IPropertyGrid extends Canvas

{

//是否 悬浮

private var isFloat:Boolean=false;

//悬浮 状态需要的mini属性卡

private var miniPropertyGird:Canvas=null;

//当前 选择对象

private var selectedObject:Object=null;

//属性 窗口容器

public var container:DisplayObjectContainer=null;

//属性 窗口内容展示区

public var content:VBox=null;

//悬浮 状态切换图片

public var title_img_pin:Image=null;

//最小 化图片

public var title_img_min:Image=null;

/**

* 构造函数

* */

public function IPropertyGrid()

{

this.addEventListener(FlexEvent.CREATION_COMPLETE,init);

this.addEventListener(PropertyGridEvent.FLOAT_STATE_CHANGED,floateStateChangedHandler);

}

/**

* 悬浮状态切换的处理函数

*

* 逻辑:悬浮状态,从容器中移除属性窗口,将mini属性窗口增加到容器中,同时为属性窗口增加Rollout处理函数

* 内嵌状态,将属性窗口增加到容器,从容器中mini属性窗口,移除属性窗口的Rollout处理函数

* */

public function floateStateChangedHandler(e:PropertyGridEvent):void

{

if(isFloat)

{

this.container.removeChild(this);

this.container.addChild(miniPropertyGird);

this.addEventListener(MouseEvent.ROLL_OUT,mouseClosePropGrid);

}

else

{

this.container.addChild(this);

this.container.removeChild(miniPropertyGird);

this.removeEventListener(MouseEvent.ROLL_OUT,mouseClosePropGrid);

}

}

/**

* 获取当前选中对象

* */

public function get SelectedObj():Object

{

return this.selectedObject;

}

/**

* 更改当前选中对象,并触发SELECTED_OBJECT_CHANGED事件

* */

public function set SelectedObj(obj:Object):void

{

if(this.selectedObject!=obj)

{

this.selectedObject=obj;

dispatchEvent(new PropertyGridEvent(PropertyGridEvent.SELECTED_OBJECT_CHANGED));

}

}

/**

* 初始化函数

* */

public function init(e:FlexEvent):void

{

Assert.notNull(container,“container”);

Assert.notNull(content,“content”);

Assert.notNull(title_img_pin,“title_img_pin”);

Assert.notNull(title_img_min,“title_img_min”);

title_img_pin.addEventListener(MouseEvent.CLICK,changeFloatState);

title_img_min.addEventListener(MouseEvent.CLICK,minPropertyGrid);

initMiniPropertyGrid();

}

/**

* 更改悬浮状态函数

* */

public function changeFloatState(e:MouseEvent):void

{

this.isFloat=!this.isFloat;

dispatchEvent(new PropertyGridEvent(PropertyGridEvent.FLOAT_STATE_CHANGED));

}

/**

* 悬浮状态下显示属性窗口函数

* */

public function showPropertyGrid(e:MouseEvent):void

{

var point:Point=new Point(container.x+container.width-this.width,container.y);

var targetPoint:Point=container.localToGlobal(point);

this.x=targetPoint.x;

this.y=targetPoint.y;

this.height=container.height;

PopUpManager.addPopUp(this,container,false);

var effect:WipeLeft=new WipeLeft();

effect.duration=300;

effect.target=this;

effect.play();

}

/**

* 初始化mini属性窗口

* */

public function initMiniPropertyGrid():void

{

miniPropertyGird=new Canvas();

miniPropertyGird.height=100;

miniPropertyGird.width=25;

miniPropertyGird.setStyle(“top”,0);

miniPropertyGird.setStyle(“right”,0);

var img:Image=new Image();

img.source=“assets/propertygrid.png”;

img.percentHeight=100;

img.percentWidth=100;

img.addEventListener(MouseEvent.MOUSE_OVER,showPropertyGrid);

miniPropertyGird.addChild(img);

}

/**

* 最小化属性窗口函数

* */

public function minPropertyGrid(e:MouseEvent):void

{

PopUpManager.removePopUp(this);

}

/**

* 关闭悬浮状态属性窗口处理函数

* */

public function mouseClosePropGrid(e:MouseEvent):void

{

var pg:IPropertyGrid=e.target as IPropertyGrid;

if(pg!=null)

{

PopUpManager.removePopUp(pg);

}

}

/**

* 增加属性窗口单元

*

* @param title:单元标题

* */

public function addGridUnit(title:String):GridUnit

{

var tgrid:GridUnit=new GridUnit();

PopUpManager.addPopUp(tgrid,Application.application.document);

tgrid.title_content.text=title;

this.content.addChild(tgrid);

return tgrid;

}

}

/**

* @desc: 属性窗口事件

*     SELECTED_OBJECT_CHANGED 当前选中对象改变时触发的事件

*     FLOAT_STATE_CHANGED 悬浮状态发生改变时触发的事件

*

* @author:    sunjingtao

* */

public class PropertyGridEvent extends Event

{

public static const SELECTED_OBJECT_CHANGED:String=“selectedObjectChanged”;

public static const FLOAT_STATE_CHANGED:String=“floatStateChanged”;

public function PropertyGridEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)

{

super(type, bubbles, cancelable);

}

}

数据模型部分想要做好就比较复杂,但是主要就是反射相关知识以 及元数据的使用

反射:describeType函数

元数据:编译时增加编 译参数-keep-as3-metadata+=MetaData1,MetaData2…

使用

假设我们对以下对象进 行属性设置:

public class Activity

{

[Appearance(name=“imageSource”,description=图像资源)]

public var ImageSource:String;

[Behavior(name=“do1”,description=行为)]

public var DoEvent1:String;

[Behavior(name=“do2”,description=行为)]

public var DoEvent2:String;

public var Test:String;

public function Activity(source:String,doEvent:String)

{

this.ImageSource=source;

this.DoEvent1=doEvent+“11”;

this.DoEvent2=doEvent+“22”;

}

}

主程序为:

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute

xmlns:propertygrid=”ui.propertygrid.*>

<mx:Script>

<![CDATA[

import datastructure.Map;

import mx.managers.PopUpManager;

import ui.propertygrid.GridUnit;

import mx.controls.Label;

import mx.containers.GridItem;

import mx.containers.GridRow;

import mx.collections.ArrayCollection;

import reflect.MetaDataInfo;

import reflect.MetaData;

import mx.containers.FormItem;

import reflect.Variable;

import reflect.InstanceInfo;

import flash.utils.describeType;

import xdesigner.Activity;

private function doParse():void

{

var act:Activity=new Activity(“source”,“doEvent”);

var instance:MetaDataInfo=new MetaDataInfo(act);

for each(var key:String in instance.Keys)

{

var tgrid:GridUnit=propgrid.addGridUnit(key);

for each(var obj:Object in instance.getMetaDataRefObjs(key))

{

if(obj is Variable)

{

var variable:Variable=obj as Variable;

var map:Map=new Map();

map.push(“text”,variable.Value);

tgrid.addRow(variable.Name,new Label(),map);

}

}

}

}

]]>

</mx:Script>

<mx:Canvas id=”container” width=”100%” height=”100%>

<mx:Canvas width=”100%” height=”100%” borderStyle=”solid” borderColor=”#F5F7F9>

<propertygrid:PropertyGrid id=”propgrid” container=”{container}” x=”81” y=”31>

</propertygrid:PropertyGrid>

</mx:Canvas>

</mx:Canvas>

<mx:Button label=”Parse” click=”doParse()”/>

</mx:Application>

原创文章,转载请注明出处!
All CopyRight Reserved

主页:http://jingtao.cnblogs.com

QQ:307073463
Email:jingtaodeemail@qq.com
MSN:sunjingtao@live.com

显示效果:

[转载]Asp.net MVC2.0系列文章-MVC简介篇

mikel阅读(1077)

[转载]Asp.net MVC2.0系列文章-MVC简介篇 – 灵动生活 – 博客园.

使用微软VS工具开发Web应用程序主要有两种方 式:一种是常用的创建ASP.NET Web Forms,另外一种就是今天着重介绍的ASP.NET MVC.

Web Forms有两部分组成:Aspx檔以及后台代码aspx.cs文件。

Web Forms优点是:

  • HTML接口与业务逻辑分开
  • 丰富的服务器控件,加速了开发速度
  • 数据绑定技术
  • 基于事件的程序模型
  • 用户控件和第三方控件技术

什么是ASP.NET MVC

ASP.NET MVC Framework是微软在ASP.NET中所添加的一组类别库, 这组类库可以使用Model-View-Controller设计模式来开发ASP.NET的应用程序

Model:包括数据、验证规则、数据访问和业务逻辑等应用程序信息。

View:封装了应用程序的表示 层,是呈现给使用者看的信息

Controller:包括控制流逻辑,控制 信息流和应用程序的执行。接受来自用户的指令与数据,并将ModelView做整合的控制器,当服务器接到对ASP.NET MVC应用程序的要求时,服务器(IIS)会先使用UrlRoutingModule(ASP.NET Routing HTTP 模块),由它来解析是否有包含ASP.NET MVC应用程序的URL,若有,则会产生一个MvcRouteHandler对象,这个对象会装载 执行的必要信息,并且会呼叫包含在URL中的ControllerExecute方法来执行工作。

Web应用程序MVC化的优点有:

  • 更易操作HTML标记
  • 更方便地与JQuery整合,实现Ajax技术
  • 创建SEO友好的URLS
  • 驱动式开发更容易

Asp.net MVC发展史

ASP.NET MVC Framework的第一个版本于2009317释出RTM版本,新的MVC 2.0也已在2010311释出供.NET Framework 3.5版本使用的RTM版本,MVC2.0Visual Studio 2010已有集成。接下来的一系 列文章使用的工具就是VS2010 MVC2.0

Asp.net MVC2.0新特性

MVC2.0的新特性主要有:

  • Areas:允许组织多个逻辑 层,便于团队开发。
  • UI Helpers:可以使用strongly-typed helpers修改和展示数据,更易于 维护旧有程序,从而提供高开发效率。
  • 服务器端验证:可以使用声明式注解定义模型的验证规则。
  • 客户端验证:自动产生基于模型验证的客户端验证。

参考资料

http://aspnet.codeplex.com/wikipage?title=MVC&referringTitle=Home

http://www.asp.net/(S(m4vhrxrb1md4rg451u0b1445))/mvc/whatisaspmvc

版权

作者:灵动生活

出处:http://www.cnblogs.com/ywqu

如果你认为 此文章有用,请点击底端的【推荐】让其他人也了解此文章

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[转载]c#4.0——泛型委托的协变、逆变Demo

mikel阅读(1076)

[转载]c#4.0——泛型委托的协变、逆变Demo – Roy Ming – 博客园.

在编程语言中,“协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。“逆变”则是指能够使用派生程度更小的类型。

在 .NET Framework 4 和 Visual Studio 2010 中,C# 和 Visual Basic 都支持在泛型接口和委托中使用协变和逆变,并允许隐式转换泛型类型参数。

如果泛型接口或委托的泛型参数声明为协变或逆变,则将该泛型接口或委托称为“变体”。C# 和 Visual Basic 都允许您创建自己的变体接口和委托。

在匹配方法签名和委托类型方面,非泛型委托也支持协变和逆变。这样,您不仅可以为委托指派具有匹配签名的方法,而且可以指派这样的方法:它们返回与委托类型指定的派生类型相比,派生程度更 大的类型(协变),或者接受相比之下,派生程度更小的类型的参数(逆变)。

实例

  1. 首先定义一个接口IColor及两个派生类
    public interface IColor { } public class Red : IColor { } public class Blue : IColor { } 
  2. 定义ColorDemo类用来写展示协变与逆变的逻辑
      public class ColorDemo{}
  3. 编写具体实现

      public class ColorDemo { //协变委托 private delegate void CovarianceDelegate<out T>(); //逆变委托 private delegate void ContravarianceDelegate<in T>(); private static string colorInfo; public void CoreMethod() { //协变 CovarianceDelegate<IColor> a1 = ColorMethod; Invoke(a1); CovarianceDelegate<Red> a2= RedMethod; Invoke(a2); a1 = a2; Invoke(a1); //逆变 ContravarianceDelegate<Red> b1 = RedMethod; Invoke(b1); ContravarianceDelegate<IColor> b2 = BlueMethod; Invoke(b2); b1 = b2; Invoke(b1); } private void Invoke(dynamic invokeObj) { Console.WriteLine("invokeObj类型为:" + invokeObj.GetType().Name); invokeObj.Invoke(); } public void RedMethod() { colorInfo = "我现在是红色"; Console.WriteLine(colorInfo); } public void BlueMethod() { colorInfo = "我现在是蓝色"; Console.WriteLine(colorInfo); } public void ColorMethod() { colorInfo = "我现在是无色"; Console.WriteLine(colorInfo); } }
  4. 运行结果

[转载]通用XML读写和配置(二)

mikel阅读(1126)

[转载]通用XML读写和配置(二) – 无待 – 博客园.

上一篇探讨了通用的XML读写配置,有了一点改进,但还不够通用,希望有一个类似“万能工具类”的东西,能够方便的对所有要保存和配置的XML数据 都进行读写,而且数据本身并不需要知道XML文件的保存细节。这看起来很不错,但似乎又不太可能。后来想到了.Net的反射 (Reflection),于是有了一个思路: 写入XML文件时,通过反射获取数据对象的公有字段信息(FieldInfo)和字段值,根据类名和字段名自动构造XML节点结构,并存储对应的值。读取 XML时,根据数据对象的字段信息来查找对应的同名XML节点,并将该节点存储的内容赋值给对应的字段。

这样一来,数据对象的定义不必再附加XML节点信息,实现彻底与XML结构和存储细节相脱离,数据类只要关心自身的职能和业务逻辑即可。继续上一篇 中的示例,则类图变化为:

有了这个“万能工具类”XmlStorageHelper,我们就可以方便的把数据保存到XML中并读取出来,理论上这个数据可以是任何类型,甚至 是一个数组对象。
示例代码如下:

代码

总结:
1.在工具类XmlStorageHelper的实现过程中,遇到并解决了以下几个难点:
A. 字段为自定义类型
B.字段类型为数组或者集合类型
C.字段类型为字典类型(Dictionary)
2.XmlStorageHelper 只保存非静态的公有字段(Public)的值,不保存其它访问权限的字段。
3.XmlStorageHelper不保存函数和属性 (Property),不管它们是否为Public。
4.目前基本支持Framework2.0里的集合类型及其泛型,但如果使用自定义的集合类 型(比如继承自IList)可能会有问题,尚未验证非常复杂的集合嵌套类型,比如 List<List<Dictionary<int,string>>>这样的复杂集合。

实现源代码下载:XmlStorageHelper.cs

示例工程下载:CommonXmlConfigV2.rar

[转载]Spring.NET企业架构实践之 Nhibernate + WCF + ASP.NET MVC + NVelocity 对PetShop4.0重构(三)——持久层

mikel阅读(1028)

[转载]Spring.NET企业架构实践之 Nhibernate + WCF + ASP.NET MVC + NVelocity 对PetShop4.0重构(三)——持久层 – 刘冬的博客 – 博客园.

什么是持久层?先解释什么是持久,英文persistence,将内存中的数据固化,保持在物理储存设备中。然而在企业应用中,往往通过关系 型数据库来完成这一过程。那么持久层的定义是:相对于三层架构中的表示层、业务层而言,专门负责持久化数据的独立领域。 设计模式中的“单一职责”原则确 定了分层的目的,说白了,持久层就是专门与数据库打交道的。如图1所示

图1

PetShop4.0中的DAL(数据库访问层)就是操作数据库的。在其DAL中,通过SQL语句返回 DataReader,然后给Model对象赋值;在添加、修改、删除操作中,通过Model对象的数据生成SQL语句,然后写入数据库。此时,我们能够 看出每张表都用同样的操作。虽然PetShop4.0使 用SQLHelper封装数据库操作,但是却没有一个通用的CRUD封装。目前PetShop4.0每 个表都对应各种的CRUD,这样就会出现大量重复的代码。

对于PetShop4.0的使用多钟数据库来言。对于每种数据库都要写一种DAL。这样数据库的可移植性就不强了。

针对上述问题,我使用ORM框架NHibernate作 为持久层框架,来取代PetShop4.0的DAL。

什么是ORM?对象关系映射,英文Object Relational Mapping。是解决“阻抗不匹配”的一种技术。“阻抗不匹配”是指,关系型数据库中数据与编程语言中的对象存在差异。使用面向对象的语言编程的同时又 要为“面向关系”而犯愁。在企业级的系统开发中,程序员有30%的时间用来解决“阻抗不匹配”的问题,花大量时间去编写SQL语句。而ORM框 架的出现就能很好的解决这样的问题,从而做到快速开发。在一个项目的编码过程中,业务层是比较重要的,则编写表示层和持久层代码所用的时间不应该过长。所 以使用好一个ORM框架尤为重要。至于为什么要使用ORM框 架,有一个重要的原因:提高开发速度,减少开发和维护成本。然而这一点对于一个商业公司来说相当看重。

ORM的O是对象的意思,代表高级语言中的实体(Entity)模型;R是关系的意思,代表关系型数据库;M是 映射的意思,表示通过某种方式实现对象和关系型数据库的映射。如图2所示

图2

本系列文章,使用NHibernate作为ORM框架。这样操作对象就相当于操作数据库,开发人员不需要关心SQL语句怎么 写,一门心思的专注面向对象的开发,从而使得应用程序更OO(面向对象)。作为NHibernate的 实体对象,完全是POJO(上篇文章提到过)。在持久层中,NHibernate框 架借助映射文件和实体所承载的数据自动生成SQL语句,实现自动持久化。但是NHibernate仍 然存在一些缺点:如,批量删除和修改。至于复杂的查询报表来说,java开发人员往往放弃Hibernate,而使用ibatis或jdbc的方式实现。 但是.NET在这一点做的很好,通过NHibernate集 成Linq的 方式,使我们能够通过Linq的语法查询出想要得到的数据。巧妙的把C#语法(更且却的说是.NET)和java中最流行的框架之一 的.NET版NHibernate完美结合起来。可以这么说,HQL(Hibernate Query Language)不是完全的面向对象,但是Linq to NHibernate是觉对的面向对象。

在调用NHibernate的API操作数据库时,每个对象都需要写一个DAO。这里,我们使用基于泛型的 Repository(资源库)模式将相同的代码内聚起来。

IRepository

public interface IRepository<T> where T : class
{
/// <summary>
/// 获取实 体
/// </summary>
/// <param name=”id”>主键</param>
/// <returns></returns>
T Get(object id);

/// <summary>
/// 插入实 体
/// </summary>
/// <param name=”entity”>实体</param>
/// <returns></returns>
object Save(T entity);

/// <summary>
/// 修改实 体
/// </summary>
/// <param name=”entity”>实体</param>
/// <returns></returns>
void Update(T entity);

/// <summary>
/// 保存实 体
/// </summary>
/// <param name=”entity”>实体</param>
/// <returns></returns>
void SaveOrUpdate(T entity);

/// <summary>
/// 删除实 体
/// </summary>
/// <param name=”entity”>实体</param>
/// <returns></returns>
void Delete(T entity);

/// <summary>
/// 获取全 部集合
/// </summary>
/// <returns></returns>
IQueryable<T> LoadAll();

}

RepositoryBase

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

using Spring.Data.NHibernate.Generic.Support;
using NHibernate.Linq;

public abstract class RepositoryBase<T> : HibernateDaoSupport, IRepository<T> where T : class
{

public virtual object Save(T entity)
{
return this.HibernateTemplate.Save(entity);
}

public virtual T Get(object id)
{
return this.HibernateTemplate.Get<T>(id);
}

public virtual IQueryable<T> LoadAll()
{
return this.Session.Linq<T>();
}

public virtual void Update(T entity)
{
this.HibernateTemplate.Update(entity);
}

public void Delete(T entity)
{
this.HibernateTemplate.Delete(entity);
}

public virtual void SaveOrUpdate(T entity)
{
this.HibernateTemplate.SaveOrUpdate(entity);
}

}

这样,同为增、删、改、查的代码,我只需要写一遍就可以了。对于数据库的迁移,可以通过配置数据库方言来实现其目的。在使用Spring.NET作 为IoC框 架中,我们不用关心和有意去编写数据库事务,并且NHibernate的 Session管理也交给Spring.NET来处理。重构后的代码与PetShop4.0的 DAL代码比较起来,代码行数减少了很多,但代码的可维护性和灵活性却有明显的提升。

[转载]SQL 2008 T-SQL(表变量参数)

mikel阅读(1039)

[转载]SQL 2008 T-SQL(表变量参数) – NSun快速开发 – 博客园.

最近装了VS2010尝鲜。在Win7下速度明显快了很多。顺便装了Microsoft SQL Server 2008 R2,在看SQL 2008T-SQL语法的增强的时候注意到了一个表变量作为参数传递的新语法觉得很不错。

首先建立一张测试表:

CREATE TABLE [dbo].[us](
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NULL,
[pass] [varchar](50) NULL,
CONSTRAINT [PK_us] PRIMARY KEY CLUSTERED
(
[id] ASC
)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
)
ON [PRIMARY]

定义一个自定义表类型:

CREATE TYPE [dbo].[UsTable] AS TABLE(
[id] [int] IDENTITY(1,1) NOT NULL,
[NAME] [varchar](50) NULL
)

然后我们写一个存储过程来传递这个类型,这里我们模拟用户批量添加默认密码为123

CREATE PROC [dbo].[addus]
@tab [UsTable] READONLY
AS
BEGIN
INSERT INTO us(name,pass) SELECT name,123 FROM @tab
END

测试一下

定义一个自定义表类型的变量
DECLARE @us UsTable
插入模拟数 据
INSERT INTO @us (name) VALUES (dc)
INSERT INTO @us (name) VALUES (ada)
INSERT INTO @us (name) VALUES (dacey)
执行存储过程
EXEC dbo.addus @tab = @us

查询 us表看下是否都插入进去了:
1 dc 123
2 ada 123
3 dacey 123

[转载]工厂方法模式-4

mikel阅读(947)

[转载]工厂方法模式-4 – 云飞龙行 – 博客园.

3.2  工厂方法模式与IoC/DI

IoC——Inversion of Control  控制反转
DI——Dependency Injection   依赖注入
1:如何理解IoC/DI
要想理解上面两个概念,就必须搞清楚如下的问题:

  • 参与者都有谁?
  • 依赖:谁依赖于谁?为什么需要依赖?
  • 注入:谁注入于谁?到底注入什么?
  • 控制反转:谁控制谁?控制什么?为何叫反转(有反转就应该有正转了)?
  • 依赖注入和控制反转是同一概念吗?

下面就来简要的回答一下上述问题,把这些问题搞明白了,IoC/DI也就明白了。
(1)参与者都有谁:

一般有三方参与者,一个是某个对象;一个是IoC/DI的容器;另一个是某个对象的外部资源。
又要名词解释一下,某个对象指的就是任意的、普通的Java对象; IoC/DI的容器简单点说就是指用来实现IoC/DI功能的一个框架程序;对象的外部资源指的就是对象需要的,但是是从对象外部获取的,都统称资源,比 如:对象需要的其它对象、或者是对象需要的文件资源等等。
(2)谁依赖于谁:

当然是某个对象依赖于IoC/DI的容器
(3)为什么需要依赖:

对象需要IoC/DI的容器来提供对象需要的外部资源
(4)谁注入于谁:

很明显是IoC/DI的容器 注入 某个对象
(5)到底注入什么:

就是注入某个对象所需要的外部资源
(6)谁控制谁:

当然是IoC/DI的容器来控制对象了
(7)控制什么:

主要是控制对象实例的创建
(8)为何叫反转:

反转是相对于正向而言的,那么什么算是正向的呢?考虑一下常规情况下的应用程序,如果要在A里面使用C,你会怎么做呢?当然是直接去创建C的对象,也就是 说,是在A类中主动去获取所需要的外部资源C,这种情况被称为正向的。那么什么是反向呢?就是A类不再主动去获取C,而是被动等待,等待IoC/DI的容 器获取一个C的实例,然后反向的注入到A类中。
用图例来说明一下,先看没有IoC/DI的时候,常规的A类使用C类的示意图,如图7所示:


图7  常规A使用C示意图

当有了IoC/DI的容器后,A类不再主动去创建C了,如图8所示:


图8  A类不再主动创建C
而是被动等待,等待IoC/DI的容器获取一个C的实例,然后反向的注入到A类中,如图9所示:

图 9  有IoC/DI容器后程序结构示意图
(9)依赖注入和控制反转是同一概念吗?
根据上面的讲述,应该能看出来,依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。依赖注入是从应用程序的角度在描 述,可以把依赖注入描述完整点:应用程序依赖容器创建并注入它所需要的外部资源;而控制反转是从容器的角度在描述,描述完整点:容器控制应用程序,由容器 反向的向应用程序注入应用程序所需要的外部资源。
(10)小结一下:

其实IoC/DI对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在 IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC/DI容器来创建并注入它所需要的资源了。
这么小小的一个改变其实是编程思想的一个大进步,这样就有效的分离了对象和它所需要的外部资源,使得它们松散耦合,有利于功能复用,更重要的是使得程序的 整个体系结构变得非常灵活。
2:工厂方法模式和IoC/DI有什么关系呢?
从某个角度讲,它们的思想很类似。
上面讲了,有了IoC/DI过后,应用程序就不再主动了,而是被动等待由容器来注入资源,那么在编写代码的时候,一旦要用到外部资源,就会开一个窗口,让 容器能注入进来,也就是提供给容器使用的注入的途径,当然这不是我们的重点,就不去细细讲了,用setter注入来示例一下,看看使用IoC/DI的代码 是什么样子,示例代码如下:

public class A {

/**

* 等待被注入进来

*/

private C c = null;

/**

* 注入资源C的方法

* @param c 被注入的资源

*/

public void setC(C c){

this.c = c;

}

public void t1(){

//这里需要使用C,可是又不让主动去创建C了,怎么办?

//反正就要求从外部注入,这样更省心,

//自己不用管怎么获取C,直接使用就好了

c.tc();

}

}

接口C的示例代码如下:

public interface C {

public void tc();

}

从上面的示例代码可以看出,现在在A里面写代码的时候,凡是碰到了需要外部资源,那么就提供注入的途径,要求从外部注入,自己只管使用这些对象。
再来看看工厂方法模式,如何实现上面同样的功能,为了区分,分别取名为A1和C1。这个时候在A1里面要使用C1对象,也不是由A1主动去获取C1对象, 而是创建一个工厂方法,就类似于一个注入的途径;然后由子类,假设叫A2吧,由A2来获取C1对象,在调用的时候,替换掉A1的相应方法,相当于反向注入 回到A1里面,示例代码如下:

public abstract class A1 {

/**

* 工厂方法,创建C1,类似于从子类注入进来的途径

* @return C1的对象实例

*/

protected abstract C1 createC1();

public void t1(){

//这里需要使用C1类, 可是不知道究竟是用哪一个

//也就不主动去创建C1了, 怎么办?

//反正会在子类里面实现,这里不用管怎么获取C1, 直接使用就好了

createC1().tc();

}

}

子类的示例代码如下:

public class A2 extends A1 {

protected C1 createC1() {

//真正的选择具体实现,并创建对象

return new C2();

}

}

C1接口和前面C接口是一样的,C2这个实现类也是空的,只是演示一下,因此就不去展示它们的代码了。
仔细体会上面的示例,对比它们的实现,尤其是从思想层面上,会发现工厂方法模式和IoC/DI的思想是相似的,都是“主动变被动”, 进行了“主从换位”,从而获得了更灵活的程序结构。

未完待续……

[转载]工厂方法模式-3

mikel阅读(900)

[转载]工厂方法模式-3 – 云飞龙行 – 博客园.

3.1  认识工厂方法模式

(1)模式的功能
工厂方法的主要功能是让父类在不知道具体实现的情况下,完成自身的功能调用,而具体的实现延迟到子类来实现。
这样在设计的时候,不用去考虑具体的实现,需要某个对象,把它通过工厂方法返回就好了,在使用这些对象实现功能的时候还是通过接口来操作,这非常类似于 IoC/DI的思想,这个在后面给大家稍详细点介绍一下。
(2)实现成抽象类
工厂方法的实现中,通常父类会是一个抽象类,里面包含创建所需对象的抽象方法,这些抽象方法就是工厂方法。
这里要注意一个问题,子类在实现这些抽象方法的时候,通常并不是真的由子类来实现具体的功能,而是在子类的方法里面做选择,选择具体的产品实现对象。
父类里面,通常会有使用这些产品对象来实现一定的功能的方法,而且这些方法所实现的功能通常都是公共的功能,不管子类选择了何种具体的产品实现,这些方法 的功能总是能正确执行。
(3)实现成具体的类
当然也可以把父类实现成为一个具体的类,这种情况下,通常是在父类中提供获取所需对象的默认实现方法,这样就算没有具体的子类,也能够运行。
通常这种情况还是需要具体的子类来决定具体要如何创建父类所需要的对象。也把这种情况称为工厂方法为子类提供了挂钩,通过工厂方法,可以让子类对象来覆盖 父类的实现,从而提供更好的灵活性。
(4)工厂方法的参数和返回
工厂方法的实现中,可能需要参数,以便决定到底选用哪一种具体的实现。也就是说通过在抽象方法里面传递参数,在子类实现的时候根据参数进行选择,看看究竟 应该创建哪一个具体的实现对象。
一般工厂方法返回的是被创建对象的接口对象,当然也可以是抽象类或者一个具体的类的实例。
(5)谁来使用工厂方法创建的对象
这里首先要搞明白一件事情,就是谁在使用工厂方法创建的对象?
事实上,在工厂方法模式里面,应该是Creator中的其它方法在使用工厂方法创建的对象,虽然也可以把工厂方法创建的对象直接提供给Creator外部 使用,但工厂方法模式的本意,是由Creator对象内部的方法来使用工厂方法创建的对象,也就是说,工厂方法一般不提供给Creator外部使用。
客户端应该是使用Creator对象,或者是使用由Creator创建出来的对象。对于客户端使用Creator对象,这个时候工厂方法创建的对象,是 Creator中的某些方法使用。对于使用那些由Creator创建出来的对象,这个时候工厂方法创建的对象,是构成客户端需要的对象的一部分。分别举例 来说明。
①客户端使用Creator对象的情况
比如前面的示例,对于“实现导出数据的业务功能对象”的类ExportOperate,它有一个export的方法,在这个方法里面,需要使用具体的“导 出的文件对象的接口对象” ExportFileApi,而ExportOperate是不知道具体的ExportFileApi实现的,那么怎么做的呢?就是定义了一个工厂方法, 用来返回ExportFileApi的对象,然后export方法会使用这个工厂方法来获取它所需要的对象,然后执行功能。
这个时候的客户端是怎么做的呢?这个时候客户端主要就是使用这个ExportOperate的实例来完成它想要完成的功能,也就是客户端使用 Creator对象的情况,简单描述这种情况下的代码结构如下:

/**

* 客户端使用Creator对 象的情况下,Creator的基本实现结构

*/

public abstract class Creator {

/**

* 工厂方法,一般不对外

* @return 创建的产品对象

*/

protected abstract Product factoryMethod();

/**

* 提供给外部使用的方法,

* 客户端一般使用Creator提供的这些方法来完成所需要的功能

*/

public void someOperation(){

//在这里使用工厂方法

Product p = factoryMethod();

}

}

②客户端使用由Creator创建出来的对象
另外一种是由Creator向客户端返回由“工厂方法创建的对象”来构建的对象,这个时候工厂方法创建的对象,是构成客户端需要的对象的一部分。简单描述 这种情况下的代码结构如下:

/**

* 客户端使用Creator来 创建客户端需要的对象的情况下,Creator的基本实现结构

*/

public abstract class Creator {

/**

* 工厂方法,一般不对外,创建一个部件对象

* @return 创建的产品对象,一般是另一个产品对象的部件

*/

protected abstract Product1 factoryMethod1();

/**

* 工厂方法,一般不对外,创建一个部件对象

* @return 创建的产品对象,一般是另一个产品对象的部件

*/

protected abstract Product2 factoryMethod2();

/**

* 创建客户端需要的对象,客户端主要使用产品对象来完成所需要的功能

* @return 客户端需要的对象

*/

public Product createProduct(){

//在这里使用工厂方法,得到客户端所需对象的部件对象

Product1 p1 = factoryMethod1();

Product2 p2 = factoryMethod2();

//工厂方法创建的对象是创建客户端对象所需要的

Product p = new ConcreteProduct();

p.setProduct1(p1);

p.setProduct2(p2);

return p;

}

}

小结一下:在工厂方法模式里面,客户端要么使用Creator对象,要么使用Creator创建的对象,一般客户端不直接使用工厂方法。当然也可以直接把 工厂方法暴露给客户端操作,但是一般不这么做。
(6)工厂方法模式的调用顺序示意图
由于客户端使用Creator对象有两种典型的情况,因此调用的顺序示意图也分做两种情况,先看看客户端使用由Creator创建出来的对象情况的调用顺 序示意图,如图5所示:

图5  客户端使用由Creator创建出来的对象的调用顺序示意图
接下来看看客户端使用Creator对象时候的调用顺序示意图,如图6所示:

图6  客户端使用Creator对象的调用顺序示意图

未完待续……