随笔- 27  文章- 0  评论- 10 
2008年5月4日

1、 Insert/Remove 必须在 SubmitChanges,才能生效,并且SubmitChanges自动创建事务。在写数据库相关模块时,是否应该调用 SubmitChanges了?如果在每次执行数据库相关操作调用,那么众多事务,效果等于不使用事务。

2、由于插入是Pending的,所以不能及时得到 Id。所有对象都必须是关联的,否则无法通过Id实现操作。

3、为什么一定要先执行Select,然后才能执行Update/Remove了?Hibernate 还有ExecuteUpdate之类的方法,为什么Linq不能提供一个了?

posted @ 2008-05-04 21:31 thh 阅读(34) | 评论 (1)编辑
2008年4月9日

     看了一年左右的设计,发现做出一个优秀的界面设计,还是不太容易。Form/Control的设计方案、MVC、MVP等等,纷繁复杂。让人迷失在众多概念当中,无法自拔。所以先收集一些文章,慢慢研究。

Interactive Application Architecture Patterns
An Introduction to the Model-View-Controller, Model-View-Presenter, and Presentation-Abstraction-Control Patterns
http://ctrl-shift-b.blogspot.com/2007/08/interactive-application-architecture.html

GUI Architectures
There have been many different ways to organize the code for a rich client system. Here I discuss a selection of those that I feel have been the most influential and introduce how they relate to the patterns.
http://martinfowler.com/eaaDev/uiArchs.html

上篇作者提到的参考实例:
http://codebetter.com/blogs/jeremy.miller/archive/2007/05/21/thoughts-rants-and-arguments-my-devteach-2007-rollup.aspx

Application Architecture for .NET: Designing Applications and Services
http://msdn2.microsoft.com/en-us/library/ms978340.aspx

CodeProject上关于Model View Persenter还有好多文章,大多都是类似 View Interface之类的概念。看完《Interactive Application Architecture Patterns》后,比较了众多MVC模式,发现microsoft也再搞View Interface模式,比martinfowler的MVP还多了点发展,并且还推出了Smart Client Factory 和Web Client Factory那个方便的东东。

posted @ 2008-04-09 22:42 thh 阅读(55) | 评论 (0)编辑
2008年4月6日

使用win 2003的机器,好不容易装上个Live Writer啊!

网上有人介绍下在一个.msi 的安装包,然后直接安装。这是一个办法,老外写的,提供的安装包也是英文的,不方便。

更方便的办法是下载2007简体中文版本的msi,然后windows update 一下,方便很多阿。

posted @ 2008-04-06 00:38 thh 阅读(110) | 评论 (0)编辑
2008年1月31日

LR 剖析器

[转自维基百科]

LR 剖析器是一种由下而上(bottom-up)的上下文无关语法剖析器。LR 意指由左(Left)至右处理输入字串,并以最右边优先衍生(Right derivation)的推导顺序(相对于 LL 剖析器)建构语法树。能以此方式剖析的语法称为 LR 语法。而在 LR(k) 这样的名称中,k 代表的是剖析时所需前瞻符号(lookahead symbol)的数量,也就是除了目前处理到的输入符号之外,还得再向右参照几个符号之意;省略 (k) 时即视为 LR(1),而非 LR(0)。


  由于LR 剖析器尝试由剖析树的叶节点开始,向上一层层透过文法规则的化简,最后推导回到树的根部(起始符号),所以它是一种由下而上的剖析方法。许多编程语言使用 LR(1) 描述文法,因此许多编译器都使用 LR 剖析器分析源代码的文法结构。LR 剖析的优点如下:

  • 众多的编程语言都可以用某种 LR 剖析器(或其变形)分析文法。(C++是个著名的例外)
  • LR 剖析器可以很有效率的建置。
  • 对所有“由左而右”扫描源代码的剖析器而言,LR 剖析器可以在最短的时间内侦测到文法错误(这是指文法无法描述的字串)。


  然而 LR 剖析器很难以人工的方式设计,一般使用“剖析产生器(parser generator)”或“编译器的编译器(compiler-compiler, 产生编译器的工具)”来建构它。 LR 剖析器可根据剖析表(parsing table)的建构方式,分类为“简单 LR 剖析器(SLR, Simple LR parser)”、“前瞻 LR 剖析器(LALR, Look-ahead LR parser)”以及“正统 LR 剖析器 (Canonical LR parser)”。这些解析器都可以处理大量的文法规则,其中 LALR 剖析器较 SLR 剖析器强大,而正统 LR 剖析器又比 LALR 剖析器能处理更多的文法。著名的 Yacc 即是用来产生 LALR 剖析器的工具。


目录

[隐藏]

LR 剖析器的结构

圖一 以表格為主由下而上之剖析器的結構
图一 以表格为主由下而上之剖析器的结构

  以表格为主(table-based)由下而上的剖析器可用图一描述其结构,它包含:

  • 一个输入缓冲区,输入的源代码储存于此,剖析将由第一个符号开始依序向后扫描。
  • 一座堆栈,储存过去的状态与化简中的符号。
  • 一张状态转移表(goto table),决定状态的移转规则。
  • 一张动作表(action table),决定目前的状态碰到输入符号时应采取的文法规则,输入符号指的是终端符号(Terminals)与非终端符号(Non-terminals)。

剖析算法

  LR 剖析过程如下:

  1. 将结尾字符 $ 与起始状态 0 依序压入空堆栈,之后的状态与符号会被压入堆栈的顶端。
  2. 根据目前的状态以及输入的终端符号,到动作表中找到对应动作:
    • 移位(shift) sn:
      • 将目前的终端符号由输入缓冲区中移出并压入堆栈
      • 再将状态 n 压入堆栈并成为最新的状态
    • 化简(reduce) rm:
      • 考虑第 m 条文法规则,假设该文法的右边(right-hand side)有 X 个符号,则将 2X 个元素从堆栈中弹出
      • 此时过去的某个状态会回到堆栈顶端
      • 状态转移表中查找此状态遇到文法左边(left-hand side)的符号时的状态转移
      • 将文法左手边的符号压入堆栈
      • 将查找到的新状态压入堆栈
    • 接受,输入字串解析完成。
    • 无对应动作,此情形即为文法错误。
  3. 重复步骤二直到输入的字串被接受或侦测到文法错误。

 范例

考虑以下文法:

(1) E → E * B
(2) E → E + B
(3) E → B
(4) B → 0
(5) B → 1

待剖析的输入字串是:

1 + 1

动作表与状态转移表

LR(0) 剖析器使用的表格如下:

动作 状态转移
状态 * + 0 1 $   E B
0     s1 s2     3 4
1 r4 r4 r4 r4 r4    
2 r5 r5 r5 r5 r5    
3 s5 s6   acc      
4 r3 r3 r3 r3 r3      
5     s1 s2     7
6     s1 s2     8
7 r1 r1 r1 r1 r1      
8 r2 r2 r2 r2 r2      

动作表 用以表示目前状态遇到终端符号(包含结尾字符 $)的对应动作,字段中可能有三种动作:

  • 移位,记为 'sn',表示下个状态是 n
  • 化简,记为 'rm',表示使用第 m 条文法规则化简堆栈中的内容。
  • 接受,记为 'acc',表示剖析正确的完成,输入的字串被文法所定义的语言接受.

状态转移表 用以表示简化后的状态遇到非终端符号时的转移规则。

 剖析过程

  下表是剖析过程中的各步骤,堆栈的顶端在最右边,状态的转移与堆栈的化简都以上表为依据,而特殊字符 '$' 也被加到输入串的尾端表示结尾。

目前的状态 堆栈 输入 将采取的动作
0 $ 0 1+1$ Shift 2
2 $ 0 '1' 2 +1$ Reduce 5
4 $ 0 B 4 +1$ Reduce 3
3 $ 0 E 3 +1$ Shift 6
6 $ 0 E 3 + 6 1$ Shift 2
2 $ 0 E 3 + 6 '1' 2 $ Reduce 5
8 $ 0 E 3 + 6 B 8 $ Reduce 2
3 $ 0 E 3 $ Accept

范例说明

  剖析起始时堆栈会包含元素 $ 与 0:

[$ 0]


  剖析器首先从输入缓冲区看到符号 '1',根据动作表当状态 0 碰到终端符号 '1' 时采用移位动作 s2,即是将 '1' 从输入缓冲区中移出并推入堆栈,再将新的状态 2 也推入堆栈,这时堆栈会变成:

[$ 0 '1' 2]


(为避免终端符号与状态混淆,故堆栈中的终端符号都加上单引号区别)

  接着看到的终端符号是 '+',根据动作表无论状态 2 碰到任何终端符号,都执行 r5 动作(以第五条文法规则 B → 1 化简堆栈内容)。此化简的动作表示剖析器已经在堆栈中认出第五条文法规则的右手边部分,因此可以用该规则的左手边符号 B 取代。因为第五条文法的右边有一个符号,因此我们将两个元素(1 × 2 = 2)自堆栈弹出,此时会回到状态 0,再推入符号 B,并查找转移表中状态 0 遇到非终端符号 B 后的新状态。新的状态是 4,完成此步骤后的堆栈是:

[$ 0 B 4]


  由于上一个终端符号 '+' 尚未被处理,因此仍保留在输入缓冲区中。依据动作表,在状态 4 碰到 '+' 时做 r3 化简。根据第三条文法 E → B,我们将 4B 从堆栈弹出,回到状态 0。接着压入 E ,根据状态转移表,当状态 0 遇到非终端符号 E 时需转移至状态 3 ,因此将 3 压入堆栈:

[$ 0 E 3]


  继续尚未处理的符号 '+',当状态 3 遇到 '+' 时的对应动作是 s6,将 '+' 从输入中移出并压入堆栈,再将新的状态 6 也压入堆栈:

[$ 0 E 3 '+' 6]


  下一个符号是 '1',在状态 6 看到 '1' 时的动作是 s2,将 '1' 从输入中移出并压入堆栈,再将新的状态 2 也压入堆栈:

[$ 0 E 3 '+' 6 '1' 2]


  最后看到的输入符号是 $,状态 2 遇到 $ 时的动作是 r5,以第五条文法规则化简堆栈内容。此化简动作与第二步骤相似,堆栈弹出两个元素后回到状态 6,这时再压入符号 B 后会进入状态 8(根据状态转移表),因此也将 8 压入堆栈:

[$ 0 E 3 '+' 6 B 8]


  在状态 8 看到符号 $ 时剖析器会继续化简,根据动作表执行 r2 化简动作,采用第二条文法规则 E → E + B 简化堆栈。由于该规则的右手边有三个符号,故从堆栈中弹出六个元素。这时回到状态 0,将规则左边的符号 E 推入堆栈后,进入新状态 3(根据状态转移表),将之压入后堆栈为:


[$ 0 E 3]

  最后在状态 3 看到符号 $,对应的动作是 acc,表示剖析顺利完成。

 建构 LR(0) 剖析表

 LR(0) 项目(Items)

  建构剖析表的过程须使用 LR(0) 项目(以下简称为“项目”),这些项目是在文法规则的右手边插入一个特殊的符号“‧”所产生。例如文法 E → E + B 有下列四个对应的项目:

E → ‧ E + B
E → E ‧ + B
E → E + ‧ B
E → E + B ‧

  若文法规则的形式是 A → ε ,则对应的唯一项目是:

A → ‧

  建立项目的用意是要决定剖析器的状态,例如 E → E ‧ + B 的意义是“剖析器已经在输入的符号中认出 E 的部分,目前正等著看到一个 '+' 符号与接续的 B 的部份”。


结论:LR(0)项目是由文法规则所产生



项目集合

  在一般的情形中,剖析器不能预知未来要用哪一条文法规则来化简堆栈内容,因此很难以单一个项目决定状态。例如以下文法:

E → E + B
E → E * B

  当剖析器认出堆栈中的 E 部分时,它无法预测未来会继续看到 '+' 或 '*',因此这时的状态须以两个项目表示:

E → E ‧ + B
E → E ‧ * B

  故我们使用项目的集合 { E → E ‧ + B, E → E ‧ * B } 来表示“剖析器认出 E 并期待 + B* B”的状态。


结论:LR(0)项目可以形成集合并描述剖析过程的状态



项目集合的封闭集

  如前段叙述,剖析器总是期待在下个输入中看到项目中的 '‧' 之后的符号。如果 '‧' 之后的符号是非终端符号,则应加入该符号所推演出的文法规则,如此才能正确的描述状态。例如规则:

E → E + B
B → 0
B → 1

  当我们来到状态 E → E + ‧ B 时,剖析器期待看到非终端符号 B,而 B 又可推演为终端符号 01。因此这时的状态应表示为:

E → E + ‧ B
B → ‧0
B → ‧1

即是“已辨认出 E + 部分,目前期待看到 B,而 B 也就是 '0' 与 '1'”之意。此现象可以描述为:

若项目集合中包含 A → x‧By 形式的项目,其中 B 为非终端符号,则对所有的文法规则 B → w 而言,B → ‧w也会被加入项目集合中。

  每个项目集合都应该以此规则扩充,将潜在的项目加到集合中直到所有在 ‧ 之后的非终端符号都处理过。如此所产生的新集合称作该项目集合的“封闭集”,符号的表示为 closure(I) ,其中 I 表示原项目集合。剖析过程中的各种状态即是由这些封闭集所构成。


结论:项目的封闭集才能完整的描述剖析过程的状态



扩充文法

  在决定状态间的转移前,我们必须先加入一条扩充文法:

(0) S → E

  其中 S 是新的起始符号(start symbol)而 E 是原先的起始符号。考虑以下文法:

(1) E → E * B
(2) E → E + B
(3) E → B
(4) B → 0
(5) B → 1

  加入扩充文法后,我们使用下列规则来决定项目集合与状态:

(0) S → E
(1) E → E * B
(2) E → E + B
(3) E → B
(4) B → 0
(5) B → 1


结论:建构剖析表前必须先加入扩充文法



寻找可到达的集合与之间的转移

  建构剖析表的第一步是找出封闭集合之间的转移。封闭集可以视为自动机中的状态,而状态间的转移则由终端符号与非终端符号决定。起始状态是由扩充的第 0 条文法规则对应的项目所形成的封闭集:

Item set 0
S → ‧ E
E → ‧ E * B
E → ‧ E + B
E → ‧ B
B → ‧ 0
B → ‧ 1

  集合中的第一个项目是该集合的核心,透过集合的封闭规则,我们加入其他项目补足集合使其封闭。这组封闭集合是第一个状态(I0),现在我们要找出这组状态可能的转移情形。

 建构动作表与状态转移表

 关于 LR(0) 与 SLR、LALR 剖析

[编辑] 表格中的冲突

[编辑] LR(0) 的例子

   E->E+T/T
T->T*F/F
F->id
   S->AA
A->aA/b

[编辑] 参考资料

posted @ 2008-01-31 13:45 thh 阅读(151) | 评论 (0)编辑
2007年10月9日

   MVP模式实现了View Interface,让Controller代码从View层很好的分离出来,逻辑也更清晰。这个和传统的Page_Load的面条代码相比,有很明显的进步。采用MVP模式之后,Page_load里的代码明显减少了。对于网站开发而言,你可以在dll里写出控制器代码和视图的接口了,以后页面的具体布局,风格也就可以少抄心很多了。但是新问题也产生了:
    1、Presenter 中往往有好多种状态,比如简单的登陆界面,至少有3个状态:登陆前,登陆错误,登陆成功。是应该把这3个状态做成一个View Interface,还是做成3个独立的View Interface呢?按照OO的原则,类应该保持单一的职责,所以选中3个独立的View Interface。但是这个在Asp.net中实现起来好像并不容易。如果设计成一个,那么在View 中,必然存在很多的if,这里简单的称为 界面逻辑。如果状态多了,这些界面逻辑也可能成为一堆面条。所以这里是一个问题。我也看过类似 Response.Redirect的方案,这个增加了Http请求的数目。
      可能的解决办法,是让Presenter支持多个View,增加自动加载视图的功能,类似CakePhp。

   2、事件中调用Presenter的代码问题。如果有一个比较复杂的页面,可能有多个Presenter类,并且页面会响应很多事件。但是如果要仔细的区分这些事件,在Asp.net如果不采用WebControl,做起来就比较的麻烦。因为HtmlControl和Html代码之类的都支持 回发事件,并且在Asp.net中操作不方便。

   以上是我想到的2个问题,也是在实际使用中感觉需要改进的问题。总体来说,MVP模式是基于Asp.net的事件模型的重大改进啊,如果你现在还为Page_Load,OnClick中的重复代码、复杂逻辑感到发愁,建议了解一下MVP模式。

参考:
     http://www.codeproject.com/useritems/Advanced_MVP.asp

posted @ 2007-10-09 11:53 thh 阅读(151) | 评论 (0)编辑
2007年10月2日

   比较了一下创建记录的时间,和表空间的大小,首先创建了3个表:

CREATE TABLE [dbo].[Role](
    
[Id] [uniqueidentifier] NOT NULL,
    
[Name] [nvarchar](255NULL,
    
[Description] [nvarchar](255NULL,
    
PRIMARY KEY CLUSTERED 
    (
        
[Id] ASC
    )
)

CREATE TABLE [dbo].[RoleId](
    
[Id] [int] NOT NULL,
    
[Name] [nvarchar](255NULL,
    
[Description] [nvarchar](255NULL,
    
PRIMARY KEY CLUSTERED 
    (
        
[Id] ASC
    )
)

CREATE TABLE [dbo].[RoleBigId](
    
[Id] [bigint] NOT NULL,
    
[Name] [nvarchar](255NULL,
    
[Description] [nvarchar](255NULL,
    
PRIMARY KEY CLUSTERED 
    (
        
[Id] ASC
    )
)

   然后分别添加100万条记录
  
declare @i int
set @i=0
while @i <1000000
begin
   
insert into role(Id) values(newid())
   
set @i = @i +1 
end   

declare @j int 
set @j=0
while @j <1000000
begin
   
insert into roleid(Id) values(@j)
   
set @j = @j +1 
end


declare @m bigint
set @m=4294967296
while @m <4295967296
begin
   
insert into RoleBigId(Id) values(@m)
   
set @m = @m +1 
end  


空间 时间
Guid data:33792KB
index_size:160KB
20.47s
Int data:12864kb
index_size:56 KB
3.39s
Bigint data:16808kb
index_size:88kb
3.38s


分析:
   运行速度:Guid的插入速度比其他的明显慢很多。然后select newid() 1万条看,大约18s,按比例计算,主要的时间是在newid函数上。
   存储空间:Guid是耗费的空间大约是bigint的2倍,bigint大约比int大1/3.

结论:因为在同一个系统(可能存在多个服务器)内要产生唯一的Id,有2个类型的Id,一种是Guid,一种是int(bigint)。Guid的实现比较简单,只要newid()类似的功能。而使用int类编号,必须附加一些额外的产生机制,保证不重复。额外的机制需要建立一个Id生成的方法,如分配一定的范围为每个使用者,建立一个多用户环境的同步机制。 这个部分的代码的效能可能比newid需要更多的时间,或者更多的配置,结果导致系统更复杂。 从这个综合考虑看,newid()的方法在更适用于多服务器环境。
posted @ 2007-10-02 15:29 thh 阅读(275) | 评论 (0)编辑
2007年9月13日
Implementing the Singleton Pattern in C#
原文:http://www.yoda.arachsys.com/csharp/singleton.html

The singleton pattern is one of the best-known patterns in software engineering. Essentially, a singleton is a class which only allows a single instance of itself to be created, and usually gives simple access to that instance. Most commonly, singletons don't allow any parameters to be specified when creating the instance - as otherwise a second request for an instance but with a different parameter could be problematic! (If the same instance should be accessed for all requests with the same parameter, the factory pattern is more appropriate.) This article deals only with the situation where no parameters are required. Typically a requirement of singletons is that they are created lazily - i.e. that the instance isn't created until it is first needed.

There are various different ways of implementing the singleton pattern in C#. I shall present them here in reverse order of elegance, starting with the most commonly seen, which is not thread-safe, and working up to a fully lazily-loaded, thread-safe, simple and highly performant version. Note that in the code here, I omit the private modifier, as it is the default for class members. In many other languages such as Java, there is a different default, and private should be used.

All these implementations share four common characteristics, however:

  • A single constructor, which is private and parameterless. This prevents other classes from instantiating it (which would be a violation of the pattern). Note that it also prevents subclassing - if a singleton can be subclassed once, it can be subclassed twice, and if each of those subclasses can create an instance, the pattern is violated. The factory pattern can be used if you need a single instance of a base type, but the exact type isn't known until runtime.
  • The class is sealed. This is unnecessary, strictly speaking, due to the above point, but may help the JIT to optimise things more.
  • A static variable which holds a reference to the single created instance, if any.
  • A public static means of getting the reference to the single created instance, creating one if necessary.

Note that all of these implementations also use a public static property Instance as the means of accessing the instance. In all cases, the property could easily be converted to a method, with no impact on thread-safety or performance.

First version - not thread-safe

 

// Bad code! Do not use!
public sealed class Singleton
{
    
static Singleton instance=null;

    Singleton()
    {
    }

    
public static Singleton Instance
    {
        
get
        {
            
if (instance==null)
            {
                instance 
= new Singleton();
            }
            
return instance;
        }
    }
}


As hinted at before, the above is not thread-safe. Two different threads could both have evaluated the test if (instance==null) and found it to be true, then both create instances, which violates the singleton pattern. Note that in fact the instance may already have been created before the expression is evaluated, but the memory model doesn't guarantee that the new value of instance will be seen by other threads unless suitable memory barriers have been passed.

Second version - simple thread-safety

 

public sealed class Singleton
{
    
static Singleton instance=null;
    
static readonly object padlock = new object();

    Singleton()
    {
    }

    
public static Singleton Instance
    {
        
get
        {
            
lock (padlock)
            {
                
if (instance==null)
                {
                    instance 
= new Singleton();
                }
                
return instance;
            }
        }
    }
}

This implementation is thread-safe. The thread takes out a lock on a shared object, and then checks whether or not the instance has been created before creating the instance. This takes care of the memory barrier issue (as locking makes sure that all reads occur logically after the lock acquire, and unlocking makes sure that all writes occur logically before the lock release) and ensures that only one thread will create an instance (as only one thread can be in that part of the code at a time - by the time the second thread enters it,the first thread will have created the instance, so the expression will evaluate to false). Unfortunately, performance suffers as a lock is acquired every time the instance is requested.

Note that instead of locking on typeof(Singleton) as some versions of this implementation do, I lock on the value of a static variable which is private to the class. Locking on objects which other classes can access and lock on (such as the type) risks performance issues and even deadlocks. This is a general style preference of mine - wherever possible, only lock on objects specifically created for the purpose of locking, or which document that they are to be locked on for specific purposes (e.g. for waiting/pulsing a queue). Usually such objects should be private to the class they are used in. This helps to make writing thread-safe applications significantly easier.

Third version - attempted thread-safety using double-check locking

 

// Bad code! Do not use!
public sealed class Singleton
{
    
static Singleton instance=null;
    
static readonly object padlock = new object();

    Singleton()
    {
    }

    
public static Singleton Instance
    {
        
get
        {
            
if (instance==null)
            {
                
lock (padlock)
                {
                    
if (instance==null)
                    {
                        instance 
= new Singleton();
                    }
                }
            }
            
return instance;
        }
    }
}

This implementation attempts to be thread-safe without the necessity of taking out a lock every time. Unfortunately, there are four downsides to the pattern:

  • It doesn't work in Java. This may seem an odd thing to comment on, but it's worth knowing if you ever need the singleton pattern in Java, and C# programmers may well also be Java programmers. The Java memory model doesn't ensure that the constructor completes before the reference to the new object is assigned to instance. The Java memory model underwent a reworking for version 1.5, but double-check locking is still broken after this without a volatile variable (as in C#).
  • Without any memory barriers, it's broken in the ECMA CLI specification too. It's possible that under the .NET 2.0 memory model (which is stronger than the ECMA spec) it's safe, but I'd rather not rely on those stronger semantics, especially if there's any doubt as to the safety. Making the instance variable volatile can make it work, as would explicit memory barrier calls, although in the latter case even experts can't agree exactly which barriers are required. I tend to try to avoid situations where experts don't agree what's right and what's wrong!
  • It's easy to get wrong. The pattern needs to be pretty much exactly as above - any significant changes are likely to impact either performance or correctness.
  • It still doesn't perform as well as the later implementations.

Fourth version - not quite as lazy, but thread-safe without using locks

 

public sealed class Singleton
{
    
static readonly Singleton instance=new Singleton();

    
// Explicit static constructor to tell C# compiler
    
// not to mark type as beforefieldinit
    static Singleton()
    {
    }

    Singleton()
    {
    }

    
public static Singleton Instance
    {
        
get
        {
            
return instance;
        }
    }
}

As you can see, this is really is extremely simple - but why is it thread-safe and how lazy is it? Well, static constructors in C# are specified to execute only when an instance of the class is created or a static member is referenced, and to execute only once per AppDomain. Given that this check for the type being newly constructed needs to be executed whatever else happens, it will be faster than adding extra checking as in the previous examples. There are a couple of wrinkles, however:

  • It's not as lazy as the other implementations. In particular, if you have static members other than Instance, the first reference to those members will involve creating the instance. This is corrected in the next implementation.
  • There are complications if one static constructor invokes another which invokes the first again. Look in the .NET specifications (currently section 9.5.3 of partition II) for more details about the exact nature of type initializers - they're unlikely to bite you, but it's worth being aware of the consequences of static constructors which refer to each other in a cycle.
  • The laziness of type initializers is only guaranteed by .NET when the type isn't marked with a special flag called beforefieldinit. Unfortunately, the C# compiler (as provided in the .NET 1.1 runtime, at least) marks all types which don't have a static constructor (i.e. a block which looks like a constructor but is marked static) as beforefieldinit. I now have a discussion page with more details about this issue. Also note that it affects performance, as discussed near the bottom of this article.

 

One shortcut you can take with this implementation (and only this one) is to just make instance a public static readonly variable, and get rid of the property entirely. This makes the basic skeleton code absolutely tiny! Many people, however, prefer to have a property in case further action is needed in future, and JIT inlining is likely to make the performance identical. (Note that the static constructor itself is still required if you require laziness.)

Fifth version - fully lazy instantiation

 

public sealed class Singleton
{
    Singleton()
    {
    }

    
public static Singleton Instance
    {
        
get
        {
            
return Nested.instance;
        }
    }
    
    
class Nested
    {
        
// Explicit static constructor to tell C# compiler
        
// not to mark type as beforefieldinit
        static Nested()
        {
        }

        
internal static readonly Singleton instance = new Singleton();
    }
}

 

Here, instantiation is triggered by the first reference to the static member of the nested class, which only occurs in Instance. This means the implementation is fully lazy, but has all the performance benefits of the previous ones. Note that although nested classes have access to the enclosing class's private members, the reverse is not true, hence the need for instance to be internal here. That doesn't raise any other problems, though, as the class itself is private. The code is a bit more complicated in order to make the instantiation lazy, however.

Performance vs laziness

In many cases, you won't actually require full laziness - unless your class initialization does something particularly time-consuming, or has some side-effect elsewhere, it's probably fine to leave out the explicit static constructor shown above. This can increase performance as it allows the JIT compiler to make a single check (for instance at the start of a method) to ensure that the type has been initialized, and then assume it from then on. If your singleton instance is referenced within a relatively tight loop, this can make a (relatively) significant performance difference. You should decide whether or not fully lazy instantiation is required, and document this decision appropriately within the class. (See below for more on performance, however.)

Exceptions

Sometimes, you need to do work in a singleton constructor which may throw an exception, but might not be fatal to the whole application. Potentially, your application may be able to fix the problem and want to try again. Using type initializers to construct the singleton becomes problematic at this stage. Different runtimes handle this case differently, but I don't know of any which do the desired thing (running the type initializer again), and even if one did, your code would be broken on other runtimes. To avoid these problems, I'd suggest using the second pattern listed on the page - just use a simple lock, and go through the check each time, building the instance in the method/property if it hasn't already been successfully built.

Thanks to Andriy Tereshchenko for raising this issue.

A word on performance

A lot of the reason for this page stemmed from people trying to be clever, and thus coming up with the double-checked locking algorithm. There is an attitude of locking being expensive which is common and misguided. I've written a very quick benchmark which just acquires singleton instances in a loop a billion ways, trying different variants. It's not terribly scientific, because in real life you may want to know how fast it is if each iteration actually involved a call into a method fetching the singleton, etc. However, it does show an important point. On my laptop, the slowest solution (by a factor of about 5) is the locking one (solution 2). Is that important? Probably not, when you bear in mind that it still managed to acquire the singleton a billion times in under 40 seconds. That means that if you're "only" acquiring the singleton four hundred thousand times per second, the cost of the acquisition is going to be 1% of the performance - so improving it isn't going to do a lot. Now, if you are acquiring the singleton that often - isn't it likely you're using it within a loop? If you care that much about improving the performance a little bit, why not declare a local variable outside the loop, acquire the singleton once and then loop. Bingo, even the slowest implementation becomes easily adequate.

I would be very interested to see a real world application where the difference between using simple locking and using one of the faster solutions actually made a significant performance difference.

Conclusion (modified slightly on January 7th 2006)

There are various different ways of implementing the singleton pattern in C#. A reader has written to me detailing a way he has encapsulated the synchronization aspect, which while I acknowledge may be useful in a few very particular situations (specifically where you want very high performance, and the ability to determine whether or not the singleton has been created, and full laziness regardless of other static members being called). I don't personally see that situation coming up often enough to merit going further with on this page, but please mail me if you're in that situation.

My personal preference is for solution 4: the only time I would normally go away from it is if I needed to be able to call other static methods without triggering initialization, or if I needed to know whether or not the singleton has already been instantiated. I don't remember the last time I was in that situation, assuming I even have. In that case, I'd probably go for solution 2, which is still nice and easy to get right.

Solution 5 is elegant, but trickier than 2 or 4, and as I said above, the benefits it provides seem to only be rarely useful.

(I wouldn't use solution 1 because it's broken, and I wouldn't use solution 3 because it has no benefits over 5.)

posted @ 2007-09-13 14:38 thh 阅读(135) | 评论 (0)编辑
2007年9月11日
    在给程序添加Nhibernate.dll 引用后,居然出现 :错误 MSB3095: 参数无效。区域性 ID 2155 (0x086B)不是受支持的区域性。在网上好多地方都没有找到解决办法,只好直接奋斗以下。
     想了个歪主意,通过.net 2.0的配置控制台,把Nhibernate.dll添加到程序集缓存中,然后在项目中用应用GAC里的NHibernate,重新编译通过了。
    怪问题.....
posted @ 2007-09-11 17:21 thh 阅读(344) | 评论 (2)编辑
2007年7月25日
首先 在event filter 中选中loal module,enable,直到 mscorwks.dll被加载,然后执行 bp mscorwks!Assembly::Init,然后 g。

当clr程序加载 assembly时,一般都会遇到上面设置的断点,这个时候就可以用机会使用 !bpmd ,给一些static 之类的类设置断点了。
posted @ 2007-07-25 17:13 thh 阅读(188) | 评论 (0)编辑
2007年7月5日

 

def Ip2Int(ip):
    
import struct,socket
    
return struct.unpack("!I",socket.inet_aton(ip))[0]


def Int2Ip(i):
    
import socket,struct
    
return socket.inet_ntoa(struct.pack("!I",i))

posted @ 2007-07-05 11:24 thh 阅读(218) | 评论 (0)编辑