Castle IOC容器快速入门

摘要:IOC模式是近年来非常流行的一种模式,相信大家都不陌生了,如果你还不是很熟悉的话,可以看看Martin Fowler大师的文章http://martinfowler.com/articles/injection.html,网上可以下载到中文版本。在Castle中,Windsor就是它的一个IOC容器,它构建于MicroKernel之上,功能非常强大。本文将通过一个简单的例子来带你快速走进Castle IOC容器。

 

主要内容

1.为什么要IOC

2.什么是Castle IOC容器

3.快速入门示例

4.几个重要的概念

 

一,为什么要IOC

IOC(控制反转或者叫依赖注入)Martin Fowler大师在他的文章中已经讲解的非常精彩了,这里实在不敢班门弄斧,只好简单地解释几句。我们使用抽象接口来隔离使用者和具体实现之间的依赖关系,但是不管再怎么抽象,最终还是要创建具体实现类的实例,这种创建具体实现类的实例对象就会造成对于具体实现的依赖,为了消除这种创建依赖性,需要把依赖移出到程序的外部(比如配置文件)。使用依赖注入后,这些类完全是基于抽象接口编写而成的,所以可以最大限度地适应需求的变化。依赖注入的形式有三种,分别为构造子注入(Constructor Injection)、设值方法注入(Setter Injection)和接口注入(Interface Injection)。

 

二.什么是Castle IOC容器

WindsorCastle 的一个IOC容器。它构建于MicroKernel之上,功能非常之强大,能检测类并了解使用这些类时需要什么参数,检测类型和类型之间工作依赖性,并提供服务或者发生错误时提供预警的机制。

 

三.快速入门示例

现在假如我们有这样一个需求,开发一个日志组件,把日志信息输出到文本文件,同时对输出的信息进行格式化,以示意性的代码来实现。

1.新建一个C#工程,添加对以下Dll的引用

Castle.DynamicProxy.dll

Castle.MicroKernel.dll

Castle.Model.dll

Castle.Windsor.dll

2.编写服务

既然是日志组件,我们先添加两个接口分别为ILogILogFormatter,这样的接口我们一般叫做服务,即实现了某种服务的接口(后面会讲到)。

/// <summary>

/// 编写:Terrylee

/// 出处:http://terrylee.cnblogs.com

/// </summary>


public interface ILog

{

    
void Write(string MsgStr);

}

 

/// <summary>

/// 编写:Terrylee

/// 出处:http://terrylee.cnblogs.com

/// </summary>


public interface ILogFormatter

{

    
string Format(string MsgStr);

}


3.编写组件

仅仅有接口还不行,还需要实现了上面两个接口的具体实现类,这些类我们把它叫做组件。

/// <summary>

/// 编写:Terrylee

/// 出处:http://terrylee.cnblogs.com

/// </summary>


public class TextFileLog : ILog

{

    
private string _target;

    
private ILogFormatter _format;

 

    
public TextFileLog(string target,ILogFormatter format)

    
{

        
this._target = target;

        
this._format = format;

    }


 

    
public void Write(string MsgStr)

    
{

        
string _MsgStr = _format.Format(MsgStr);

        _MsgStr 
+= _target;

        

        
//Output Message

        Console.WriteLine(
"Output "+_MsgStr);

    }


}

 

/// <summary>

/// 编写:Terrylee

/// 出处:http://terrylee.cnblogs.com

/// </summary>


public class TextFormatter : ILogFormatter

{

    
public TextFormatter()

    
{

        

    }


 

    
public string Format(string MsgStr)

    
{

        
return "[" + MsgStr + "]";

    }


}


4.编写配置文件

编写配置文件,由于TextFileLog构造函数中除了需要一个ILogFormatter的实例之外,还需要指定信息的输出的文本文件名,所以编写一个配置文件来指定,这个也可以放在应用程序配置文件中(Web.config或者App.config)。

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

    
<components>

        
<component id="txtLog">

            
<parameters>

                
<target>log.txt</target>

            
</parameters>

        
</component>

    
</components>

</configuration>

 

5.使用Castle IOC容器

前面的几步仅仅是为我们Castle IOC做准备,下面就是正式使用了。使用Castle IOC容器非常简单,基本上分为建立容器,加入组件,获取组件,使用组件几个步骤。

 

/// <summary>

/// 编写:Terrylee

/// 出处:http://terrylee.cnblogs.com

/// </summary>


public class App

{

    
public static void Main()

    
{

        
//建立容器

        IWindsorContainer container 
= new WindsorContainer( new XmlInterpreter("http://www.cnblogs.com/BasicUsage.xml") );

        

        
//加入组件

        container.AddComponent( 
"txtLog"

            
typeof(ILog), typeof(TextFileLog) );

 

        container.AddComponent( 
"format"

            
typeof(ILogFormatter), typeof(TextFormatter) );

 

        
//获取组件

        ILog log 
= (ILog) container["txtLog"];

 

        
//使用组件

        log.Write(
"First Castle IOC Demo");

        

        Console.ReadLine();

    }


}

简单的描述一下:

第一步:注册了一个Windsor容器;

第二步:向容器中注册ILog服务,并告诉容器TextFileLog实现了这个服务,这里还设置了一个key的参数,后面可以通过这个参数来获取这个服务,注册ILog时容器会发现这个服务依赖于其他的服务,它会自动去寻找,如果找不到这样的服务,则会抛出一个异常;

第三步:向容器中注册ILogFormatter并告知TextFormatter实现了它;

第四步:容器发现类的构造函数还需要另外一个参数target,这里指定了到BasicUsage.xml中去查找。

运行程序,可以看到输出的结果:

Output [First Castle IOC Demo]log.txt

怎么样?够简单的吧。可以看到整个过程非常的简单,代码也非常优雅,我们并没有使用new关键字创建一个具体实现类的实例,至此,一个简单的使用Castle IOC的过程就完成了。下面我们来理解一下其中几个重要的概念。

四.几个重要的概念

1.服务

服务是一个个的接口,接口约定了服务,从而使随意替换服务的实现对使用接口服务的代码没有任何的影响。像我们上面例子中的ILogILogFormatter都是一个个服务,我们在这个例子中支实现了一个文本文件的日志记录,如果你要是实现数据库记录的日志记录,都必须要遵守ILog这个接口。

2.组件

简单来说组件是一个可重用的程序单元,它实现了某个接口,并仅仅只实现了这一个良好的接口。也就是说,组件是实现了某个服务接口的类。像上例中的TextFileLogTextFormatter都是组件

3.自动装配

在上面的例子中,大家可能都已经注意到了,TextFileLog依赖于TextFormatter,我们却没有在配置文件中指定它们之间的依赖关系,这就是Castle IOC聪明的一个地方,它能够自动管理组件之间的依赖关系,而无需编写特定的xml config来配置,即自动装配的意思。

 

Castle IOC容器快速入门指南就到这儿,欢迎大家多多指教,后续文章我会详细的去写Castle IOC及其Facility文中完整的程序:下载
 

参考资料

Castle的官方网站http://www.castleproject.org

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2006-04-17 12:28 TerryLee 阅读(36924) 评论(52) 编辑 收藏

 回复 引用 查看   
#1楼 2006-04-17 13:01 Bruce Lee      
太好了,正在研究这。
 回复 引用 查看   
#2楼[楼主] 2006-04-17 13:04 Terrylee      
@Bruce Lee
呵呵,我最近会写一系列Castle IOC的文章,希望您关注:)

当然了,我也是刚开始研究Castle,并没有在用它来做过真正的项目,希望我们多多交流!

 回复 引用 查看   
#3楼 2006-04-17 13:06 idior      
castle在容器这块较之sping的配置文件方式(everything is xml),个人比较欣赏.
terrylee可以将两者比较一二。

 回复 引用 查看   
#4楼[楼主] 2006-04-17 13:12 Terrylee      
@idior
谢谢idior的指点,接下来我会写一篇Castle IOC的深入分析的文章,另外也会写Castle IOC的配置部分。

刚开始研究Castle,感觉Castle的确很优秀,呵呵~~~

 回复 引用   
#5楼 2006-04-17 13:53 zou114[未注册用户]


喜欢你的博客,
如果你有任何查询,请“走114”:
zou114查询指南:www.zou114.com
最全的信息查询网,

希望能给你带来方便
------zou114祝您身体健康!

 回复 引用 查看   
#6楼 2006-04-17 14:16 Jasonw      
写的不错,不罗嗦,够清楚!
 回复 引用 查看   
#7楼[楼主] 2006-04-17 14:29 Terrylee      
@Jasonw
谢谢支持:)

 回复 引用 查看   
#8楼 2006-04-17 15:47 Zhongkeruanjian      
ILog log = (ILog) container["txtLog"];

应该是对象只创建一次,就是说每次调用这个方法时其实是一个对象。这样可能是对象很“脏”。。不知实践过没?

 回复 引用 查看   
#9楼 2006-04-17 16:37 YLH      
to Zhongkeruanjian
可以为类指定生命风格(Lifestyle)这样就可以每次都得到一个新对象,如果指定为单例模式,那么每次都是得到的同一对象。 你仔细研究一下castle ioc 源码就清楚了

 回复 引用 查看   
#10楼[楼主] 2006-04-17 16:45 Terrylee      
@Zhongkeruanjian
后面我会写到这方面的内容,希望您关注:)

 回复 引用 查看   
#11楼[楼主] 2006-04-17 16:46 Terrylee      
@YLH

多谢,以后多多交流:)

 回复 引用 查看   
#12楼 2006-04-17 17:23 Zhongkeruanjian      
比如有一个重量级的对象,需要缓存创建,如果在这个重量级对象的构造子里有来自数据库或者配置文件的数据,Castle是否有过期策略配置?
 回复 引用 查看   
#13楼[楼主] 2006-04-17 17:33 Terrylee      
@Zhongkeruanjian

呵呵,我现在还没有研究到这一块,请继续关注本系列文章!

 回复 引用 查看   
#14楼 2006-04-17 17:35 阿不      
@Zhongkeruanjian
在Castle的组件实例中有几种生命周期:对象池,线程模式(在同一线程都使用同一实例),瞬时对象(Transient,普通new的对象),还有单件模式,还可以自定义模式.

 回复 引用 查看   
#15楼[楼主] 2006-04-17 17:50 Terrylee      
@阿不
Thanks~~~~,呵呵!

 回复 引用 查看   
#16楼 2006-04-17 17:53 Zhongkeruanjian      
@阿不

现在不是说对象的生命周期,是说在缓存模式下的过期策略。

 回复 引用 查看   
#17楼 2006-04-17 18:34 阿不      
缓存模式?
 回复 引用 查看   
#18楼 2006-04-17 18:35 阿不      
@Zhongkeruanjian
你是指什么样的缓存模式?

现在有这么多人研究Castle,看来Castle真正流行的时候到了.我1年前开始研究的时候,没有什么文档,现在好多了,在我的blog中有很多Castle的内容.半年前我还给深圳兄弟做了一个Castle的介绍,当时参加的人都是一头雾水.现在还有哪个ppt,http://shanyou.cnblogs.com/archive/2005/08/21/219738.html,这里可以找到Martin Fowler大师的文章的中文版本.
 回复 引用 查看   
#20楼[楼主] 2006-04-18 08:50 Terrylee      
@自由、创新、研究、探索……
我也开始研究不久,不过Castle真的很优秀,该是流行的时候了:-)

谢谢你提供的资料!

 回复 引用 查看   
#21楼 2006-05-17 13:29 Bear.sTaR{R}      
IWindsorContainer container = new WindsorContainer( new XmlInterpreter("../../BasicUsage.xml") );

请问楼主BasicUsage.xml是个什么文件,配置?
这个文件在哪里

新手学习....

 回复 引用 查看   
#22楼 2006-05-17 14:36 无痕      
请教一下Terrylee ,您所举的例子里面的这句话
string _MsgStr = _format.Format(MsgStr);
中的_format对象是否和martinfowler的这句
List allMovies = finder.findAll();
中的finder对象是否具有异曲同工的地方呢?

 回复 引用 查看   
#23楼[楼主] 2006-05-18 08:43 Terrylee      
@无痕
参见文中的第四步:
4.编写配置文件

指的就是BasicUsage.xml文件,也可以放在Web.config或者App.config里面

 回复 引用 查看   
#24楼[楼主] 2006-05-18 08:45 Terrylee      
@无痕

我这里只是为了举例用到几个组件,且它们之间有依赖关系,再用IOC容器来管理!

 回复 引用   
#25楼 2006-05-25 11:26 27745754[未注册用户]
在自动装配的时候如果有多个类实现同一个接口(服务),它会自动选择哪个来进行装配。
 回复 引用 查看   
#26楼[楼主] 2006-05-25 14:11 TerryLee      
 回复 引用 查看   
#27楼 2006-05-29 08:15 木头一个      
其实另外一种注册Component的方法你要附带说明一下就更好了。就是在BaseUsage.xml同样可以注册Component,而可以不需要在代码中去写container.AddComponent();

将BaseUsage.xml文件中的<components/>节点改写如下:
<components>
<component id="target" service="CastleIOC1.ILog, CastleIOC1" type="CastleIOC1.TextFileLog, CastleIOC1">
<parameters>
<target>Log.txt</target>
</parameters>
</component>
<component id="format" service="CastleIOC1.ILogFormatter, CastleIOC1" type="CastleIOC1.TextFormatter, CastleIOC1">
</component>
</components>
在注册组件的代码中可以注释掉那两句注册的代码的。这也非常有用。
当然我也明白你肯定是知道的,只是没有说出来。呵呵,帮你补一下,班门弄斧了,给初学者帮助。

同时也很感谢你的系列文章,让我学到了不少东西。

 回复 引用 查看   
#28楼[楼主] 2006-05-29 15:02 TerryLee      
@木头一个

谢谢补充!的确应该把这个加上:-)

 回复 引用   
#29楼 2006-08-10 16:21 jijl2001[未注册用户]
上面的一段配置有错误
 回复 引用 查看   
#30楼[楼主] 2006-08-10 17:29 TerryLee      
@jijl2001
你指的是哪一段呢?

 回复 引用 查看   
#31楼 2006-09-02 23:10 藤条焖猪肉      
<component id="target" service="CastleIOC1.ILog, CastleIOC1" type="CastleIOC1.TextFileLog, CastleIOC1">

id="txtLog"

打错字而已,没什么大概,继续关注

 回复 引用 查看   
#32楼[楼主] 2006-09-04 08:18 TerryLee      
@藤条焖猪肉

 回复 引用   
#33楼 2006-09-13 15:54 Bob[匿名][未注册用户]
改用配置文件:<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<components>
<component id ="txtLog" service="CastleIoc1.ILog,CastleIoc1" type="CastleIoc1.TextFileLog,CastleIoc1">
<parameters>
<target>Log.txt</target>
</parameters>
</component>
<component id="format" service="CastleIoc1.ILogFormatter,CastleIoc1" type="CastleIoc1.TextFormatter,CastleIoc1"></component>
</components>
</configuration>


注释掉,如下代码:“
//加入组件
//container.AddComponent("txtLog",
// typeof(ILog), typeof(TextFileLog));

//container.AddComponent("format",
// typeof(ILogFormatter), typeof(TextFormatter));”
按F5运行时,在ILog log = (ILog)container["txtLog"];处出现类型转换错误。为什么?

 回复 引用   
#34楼 2006-09-29 17:17 codelover[未注册用户]
.net开源框架qq群30017484,Castle ActiveRecord、Nhibernate、ibatisnet、IOC容器Spring.Net和Castle、等框架讨论学习,欢迎加入,共同f提高
 回复 引用   
#35楼 2006-12-15 09:00 qcrsoft[未注册用户]
添砖加瓦

《属性值注入 还是 构造函数注入》
来源:http://www.isedu.cn/article/dotnet_5.shtml
内容:对于IOC的两种注入方式(属性值注入和构造函数注入)选择,应该考虑到以下几种因素:
1.使用属性值注入使得很容易处理默认值和任意值,在这种情况下并不必须设定每个属性值.使用构造函数注入,容易导致产生多个多个构造函数,彼此调用.多个构造函数或多个参数将使得程序冗长和不易管理.
2.使用属性值注入(属性值不为private)将会自动使子类继承,然而gouzaohanshu却不能.后者的限制使得子类只有创建一个和父类相关的样板函数,他将会调用父类构造函数.然而不论用哪种方式,现在大多数IDE都会很容易生成构造函数或是属性.
3.使用属性值注入生成JAVA文档比构造函数注入更容易.
4.运行时环境下,属性值注入可以使用反射通过属性名称操作属性.然而构造函数注入,编译后的class文件不会维持构造函数的参数空间,因此自动根据名称匹配调用不可能.
5.属性值注入允许获取和设置属性状态(值),这种情况有益于很多状况,比如属性状态要持久化到任何存储媒介.
6.属性值注入可以使用PropertyEditor机制在需要时自动进行类型转化,spring支持这种情况.
7.属性值注入由于可以多次调用setter方法使得容易改变.

 回复 引用 查看   
#36楼[楼主] 2006-12-15 09:15 TerryLee      
@qcrsoft
谢谢补充!

 回复 引用   
#37楼 2006-12-15 09:23 qcrsoft[未注册用户]
添砖加瓦 (窗口关了,不知从哪个BLOG个抄来的了)

《两个个很形象的依赖注入的比喻》
何谓控制反转(IoC = Inversion of Control),何谓依赖注入(DI = Dependency Injection)?一直都半懂不懂,今天看到两个比喻,觉得比较形象。
IoC,用白话来讲,就是由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓"控制反转"的概念所在:控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。
正在业界为IoC争吵不休时,大师级人物Martin Fowler也站出来发话,以一篇经典文章《Inversion of Control Containers and the Dependency Injection pattern》为IoC正名,至此,IoC又获得了一个新的名字:"依赖注入 (Dependency Injection)"。相对IoC 而言,"依赖注入"的确更加准确的描述了这种古老而又时兴的设计理念。从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。
一:
再看上例中,笔记本电脑与外围存储设备通过预先指定的一个接口(USB)相连,对于笔记本而言,只是将用户指定的数据发送到USB接口,而这些数据何去何从,则由当前接入的USB设备决定。在USB设备加载之前,笔记本不可能预料用户将在USB接口上接入何种设备,只有USB设备接入之后,这种设备之间的依赖关系才开始形成。
对应上面关于依赖注入机制的描述,在运行时(系统开机,USB 设备加载)由容器(运行在笔记本中的Windows操作系统)将依赖关系(笔记本依赖USB设备进行数据存取)注入到组件中(Windows文件访问组件)。这就是依赖注入模式在现实世界中的一个版本。
很多初学者常常陷入"依赖注入,何用之有?"的疑惑。想来这个例子可以帮助大家简单的理解其中的含义。依赖注入的目标并非为软件系统带来更多的功能,而是为了提升组件重用的概率,并为系统搭建一个灵活、可扩展的平台。将USB接口和之前的串/并、PS2接口对比,想必大家就能明白其中的意味。
二:
首先想说说IoC(Inversion of Control,控制倒转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。
  那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。如果你还不明白的话,我决定放弃。

 回复 引用   
#38楼 2006-12-15 10:16 qcrsoft[未注册用户]
上例中
public class TextFileLog : ILog
这个类现有构造函数
public TextFileLog(string target,ILogFormatter format)
另外配置文件写的写法是
<component id="txtLog">
 <parameters>
  <target>log.txt</target>
 </parameters>
</component>
经过“自动装配”,第二个参数不用写,所以上面这个构造函数会被调用。

OK,上面读很清楚。

不过我又加了个构造函数
public TextFileLog(string target)
配置文件不变,按想象想必大家大多都认为应该是这个构造函数被调用是吧?实际跑了一下,居然还是TextFileLog(string target,ILogFormatter format)被调用!
(补充一下,不过用处好象也不大,见笑)

 回复 引用   
#39楼 2007-03-04 14:45 damnedmoon[未注册用户]
@Bob
估计和.Net对于动态加载的Assembly和静态引用的Assembly的区别对待有关。在某种条件下,哪怕静态的和动态的指向都为相同Assembly,Framework会把它们视为完全不同。园子里有不少人研究过这个问题,可查询相关文章。
此处,可如下解决:
1。把借口和实现放到独立的dll中,主程序引用此dll;
2。注意:接口和实现的namespace改成和主程序不同的namespace。

 回复 引用   
#40楼 2007-04-13 11:57 QQ124294272[未注册用户]
精典,终于懂得了IOC的概念
 回复 引用   
#41楼 2007-04-18 10:56 鸿江[未注册用户]
刚开始学, 非常感谢
 回复 引用   
#42楼 2007-06-12 17:09 ttloveyou3344[未注册用户]
webconfig里还要加几句代码完整的如下:
?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor"/>
</configSections>
<castle>
<components>
<component id="txtLog" service="castletest.ILog1, castletest" type="castletest.TextFileLog, castletest">
<parameters>
<target>log.txt</target>
</parameters>
</component>
<component id="format" service="castletest.ILogFormatter, castletest" type="castletest.TextFormatter, castletest">
<parameters>
<mstext>sdfsdf</mstext>
</parameters>
</component>
</components>
</castle>
</configuration>
调用的时候把IWindsorContainer container = new WindsorContainer( new XmlInterpreter("../../BasicUsage.xml") );
换成 this.kernel = new WindsorContainer(new XmlInterpreter(new ConfigResource())).Kernel;呵呵,换种方式实现,同时把注册的那两句去掉
调用如ILog1 log = (ILog1)kernel["txtLog"];呵呵,我也来凑热闹

 回复 引用   
#43楼 2007-08-23 19:30 猫猫[未注册用户]
刚接触, 看了两个比喻, 很清楚, 谢谢.
 回复 引用   
#44楼 2007-09-02 11:06 ttt[未注册用户]
有点看不懂

 回复 引用 查看   
#45楼 2007-11-27 19:33 笨→鸟(Bird)      
那为朋友可以帮我发一个上面的实例过来啊,感谢万分!

beniao123@163.com

 回复 引用 查看   
#46楼 2009-06-22 13:42 Old      
:-)
 回复 引用 查看   
#47楼 2009-09-07 10:16 破孩      
恩,不错,那个例子讲的非常明白。谢谢楼主
 回复 引用   
#48楼 2009-11-01 00:52 Castler[未注册用户]
15476416,专题讨论Castle的QQ群
 回复 引用 查看   
#49楼 2009-12-02 16:54 WangKai      
看过恩的很多文章,如Silverlight,ajax等等,受益匪浅,非常感谢,继续关注
 回复 引用 查看   
#50楼 2010-11-03 21:43 黄硕      
TextFileLog类的 Write方法中,不太明白为什么要这样写string _MsgStr = _format.Format(MsgStr);而不写成:string _MsgStr = MsgStr;


public void Write(string MsgStr)
{
string _MsgStr = _format.Format(MsgStr);
_MsgStr += _target;
Console.WriteLine("Output " + _MsgStr);
}

 回复 引用 查看   
#51楼 2010-12-19 16:18 沧海一杰      
我觉得既然是入门,你应该告诉我们这些菜鸟,你那一开始四个dll是从哪儿下载的。