1.3 加载公共语言运行库
VS中类库项目可以单独运行吗?
“我们构建的每个程序集可以是一个可执行应用程序,也可以是一个DLL”。
我记得以前写类库的时候,用VS2005新建一个ClassLibrary,比如说写一个字符串处理的辅助类StringHelper。写好以后要调试的时候就犯愁了,类库是不能单独运行的呀,所以我又增加一个控制台程序,然后引用这个类库来进行调试。
实际上,类库和EXE的唯一区别是有没有Main函数。我们新建一个ClassLibrary,然后在Class1中增加main函数,它其实就变成了一个应用程序。

Code
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4
5 namespace ClassLibrary1
6 {
7 public class Class1
8 {
9 public static void Main(string[] args)
10 {
11 }
12 }
13 }
然后F5开始运行,出现一个错误提示:
这个错误并不是程序的错,而是编译的错,既然 “我们构建的每个程序集可以是一个可执行应用程序,也可以是一个DLL”,就应该要告诉编译器,我们是要把这个项目编译成dll还是exe,因此在项目属性->application->Output type中选成Console Application就行了。
我刚刚知道能这么用的时候觉得异常惊讶,原来VS里那么多项目类型都是蒙人的,我原来以为完全不同的东西(windows应用程序,控制台应用程序和类库)原来差别如此之小。看来可视化编程的方式有时确实会起到误导的作用。实际上EXE和DLL文件都是PE格式。
我们可以使用VS自带的调试工具,Modules窗口查看一下这个程序的进程中的模块。

我们看到里面有两个EXE模块。我们在任务管理器中查看进程的时候,一个进程好像总对应一个EXE文件。但是为什么这里有两个呢?
一个进程就像一列火车,只有EXE能做火车头。
一个进程中包含很多模块,这非常象一列火车,EXE是火车头,DLL是车厢。只有火车头才能带动火车开动,但是,一列火车可以有多个火车头吗,当然可以。在这个例子当中,火车头其实是ClassLibrary1.vshost.exe,而ClassLibrary1.exe虽然是个火车头,在这里充当了第一节车厢。
PE文件格式就像一本书
一个项目总是包含很多源代码文件,包括资源文件等等,编译程序只是把多个源代码文件编译成了一个程序集文件,也可以说PE格式文件。这有点像把几十篇散文编成一本散文集。实际上,PE格式虽然非常复杂,但是粗略的看起来,PE文件格式非常像书的结构。大致分为三部分,封面前言,目录,内容。

知道这些有啥用?
PE文件格式当然是编译器开发人员,加密解密的基本知识,但是对应用程序开发者有什么用处?
托管程序集也好,win32函数库也好,com也好,都是PE文件格式。我想知道这一点的好处是,我们至少知道,在技术日新月异的背后,总是有些不变的东西,而掌握这些不变的东西,以不变应万变,也许能减少一点对学习新技术的心理压力吧。
详细的PE文件格式文档可到看雪论坛http://bbs.pediy.com/showthread.php?threadid=21932
【前言】
一直想学习很多大师级人物,对于一个很小的事情,都能入木三分,来龙去脉的讲的头头是道。曾经为了搞清楚反射的概念,一直追述到Smith的博士论文(Smith, B. C. (1982): Reflection and Semantics in a Procedural Language, Ph.D. thesis, MIT Laboratory for Computer Science Report MIT-TR-272.)好不容易下载到原文,发现居然长达几百页。当时热情的火苗子刺溜吧唧,就灭了。
我想我本就不是大师,何必去装呢。干嘛非要把自己折磨成这样,此时终于明白“好读书不求甚解”的真正含义。生活工作,不都是为了开心吗。我想还是回归本性,用我喜欢的随意的风格,对于技术方面,工作中用不到的就跳过去,有偏见的地方也不怕人笑话,我把我看书的所思所想,好好说个痛快,这不是博客本来的初衷么。
1.1将源代码编译为托管模块。
“编译”就是“翻译”
说实在的,长期以来一直对“编译”这两个词抱着敬畏的态度。觉得编译技术高深莫测遥不可及。突然有天恍然大悟,“编译”就是“翻译”,编译就是把一种语言翻译成另一种语言,C#编译成IL,C++编译成汇编,"How are you"翻译成"你好吗",啥区别没有。什么是编译优化?就是把"How are you"翻译成"你吃了吗?"程序员是干什么的?不就是替人类充当与机器对话的翻译嘛!玩的都是文字游戏。如果程序员还有点社会价值的话,那纯粹是因为机器的价值。
CLR也是个翻译
CLR的名字没有JVM好听,关键是runtime这个单词给人一种空荡荡的感觉,而machine比较形象。其实JIT才是真正的翻译,把IL翻译成汇编语言。这让我想起我一个朝鲜族同学,她说,她学英语的时候,先要把英语在脑子里翻译成汉语,再把汉语翻译成韩语才能理解。这不就是C#->IL->汇编么。
IL的引入其实是为了解决COM的一些问题,比如一个名为Show的方法,COM里面包含的是Show方法的偏移地址,而.NET程序集里放的是Show方法签名。我坦白交代,以前没有做过COM的开发,所以开始的时候无法理解.NET里那么多互操作的东东。但是至少我现在知道了,office编程得用。一个函数动则几十个参数,还好C#4.0来救大家了。
“现在我们决定使用哪种编程语言”
由此谈到语言选择的问题,其实在现实工作中,使用什么语言我们根本做不了主,往往取决于毕业后第一家公司用什么语言。一旦选择了一种语言,基本就决定了以后大致的职业发展方向。如果是使用C语言做比较低层的开发,那么用到.NET平台的机会就少了。因此虽然.NET平台提供了多种语言选择的机会,但是有一个隐形的边界是我们可能会忽略的,那就是不管用什么语言开发,你都在使用.NET平台.
.NET平台主要是针对应用程序的,当我们选择了这个平台时,就意味着我们的工作会关注行业,关注领域,关注客户需求比较多。应用程序的特点是针对行业,变化多端,因人而变。在这个基础之上,我们在.NET平台上选择语言的唯一准则就是工作效率。这个工作效率并不光指语言的工作效率,同时包含团队成员的熟悉程度,语言普及程度等,都会影响效率。因此我们在开发过程中,或者使用开发语言和工具的过程中,尽量的忘记与此无关的东西,甚至忘记语言的名字,C#或者是JAVA,我们要的只是客户价值和工作效率。
IT技术和编程语言的百花齐放日新月异往往对程序员来说是个莫大的压力,如果是这样的话,应该是违背了语言发明者的初衷。总得来说,新的东西出来总是为了解决老东西解决不了的问题,因此对于新技术的关注点应该放在经常性的总结工作中碰到的疑难杂症,然后去新技术中寻找好的解决方案。如果你工作中真的啥问题没有,那么就恭喜你,可以不用管那些花开花落了。
我所在的公司在面向服务的新技术潮流上面就是一个很好的获益的例子。因为比较早的开展了Web Service的服务,现在已经有一个庞大的数据中心,行业内超过一半的信息都放在这里,我们的服务和软件是捆绑在一起销售的,服务费一年交一次。现在就算软件不卖钱,只卖服务费都够吃个七顿巴八顿的了,因此其他公司根本没法竞争,基本在这个行业站稳了老大的位置,这次金融风暴不仅没有倒下,反而收购了不少公司。
最后一个事情是最头疼的,就是对程序员的生涯规划问题。这么多新技术新语言,学哪个更有钱途呢?不知道,所以什么都得学。我觉得还是把现在公司里的技术彻底搞明白,然后寻找里面潜伏的问题,再从新技术中寻找解决方案比较踏实可靠。假如公司确实没有啥技术可言,那得考虑换家好点的公司了。
说点正事
一个程序集可以包含几个文件,我们通常看到的都是一个文件,我工作上也没用到过,不管它。
托管程序集也是DLL,com也是dll,win32也是dll.我一直很纳闷这三种东西有多少人十分清楚。首先,它们都是PE格式,再者,win32的dll是函数库,com的dl是类型库,而托管的dll嘛,就是加了IL和元数据的啦。不知道我说的对不对。
元数据又是一个不好听的名词。它可以说是IL类型的使用说明书,.NET本质论提到一点,它对于COM世界的改进来说,它是“机器可读”的。机器可读是CLR或者是虚拟机的前提呀。
1.2 将托管模块合并成程序集
源代码是原料,模块是零部件,DLL是汽车。
托管模块这破玩意一直困扰我很久,我们平常只看到一个类库在VS中编译完以后就生成DLL,好像跟模块并不是很熟的样子。结果是因为VS偷偷调用了MSBUILD。MSBUILD再调用SDK自带的编译器。如果把源代码看成原材料的话,把DLL看成汽车的话,讲源代码生成模块应该是把原材料变成零件的过程,结果VS太好心,直接把汽车就装好了。Assembly本来就有装配的意思。
资源文件也可以编译成模块,因此模块有两种,一种来自源代码,一种来自资源文件。你当然也可以把资源文件看成源代码, whatever.
foreach (Module var in Assembly.GetExecutingAssembly().GetModules()) {
string moduleName = var.Name;
}
上述代码遍历正在执行的程序集中所有模块,默认情况下只有一个模块,而且这个模块的名字跟生成的DLL或者EXE名字一样一样的,而且是带扩展名的。Type 类也有一个Module属性。
在实际工作中,我目前还没有碰到要单独处理Module的情况。倒是跟生成程序集有关的一大问题是每日构建的问题,Visual Build Pro和R2Build好像都不错,可以针对VS项目文件直接编译,不用任何配置。而且后者是免费的。既然有免费的,就可以不用研究MSBuild了。
摘要:【目的】了解数据库中角色(Role)的概念和用处。【方法】用李老板的公司发展中碰到的问题例证角色的重要性【结论】角色在用户越多的情况下越能凸显出它的作用。
3.1 深入了解架构(Schema)
在进入李老板的故事之前,让我们先对Sql Server2005中的架构做一个更深入的了解。
用户(User)和架构(Schema)的关系
- 一个架构有且只有一个所有者Owner。
- 一个用户可以拥有多个架构。
这跟第二节中介绍的货架权限清单有所出入,第二节中的例子是多对多的关系,而实际Sql Server是采用一对多的关系。这反而比较像是银行账户。一个人可以有多个存折,但是一个存折只有一个用户。
- 创建一个用户,系统将自动创建一个同名的架构。
- 创建一个架构必须指定所有者,否则将默认为当前登陆用户。
- 表属于不同的架构也不能重名。
- 删除一个用户时,架构也会被删除?
架构(Schema)相关的SQL
创建架构
CREATE SCHEMA [SchemaName] AUTHORIZATION [User]
更改架构的所有者
删除架构
数据库设计与架构
架构的目的应该在于在同一数据库中提供一种隔离机制。按照这种思路,以最典型的产供销结构,我们可以这样设计一个数据库MyDatabase:
| 用户 |
架构 |
表 |
表全称 |
| 生产者 |
生产 |
表1 |
生产.表1 |
| 供应者 |
供应 |
表2 |
供应.表2 |
| 销售者 |
销售 |
表3 |
销售.表3 |
这样生产者登陆以后只能操作表1,供应者登陆以后只能操作表2...隔离效果非常好。但是,如果总经理想要查看一个产供销的综合报表该如何办呢?在这样的设计下,是根本做不到的。所以我们目前看到的实际情况是,基本上一个数据库只用一个架构和一个用户登陆。
| 用户 |
架构 |
表 |
表全称 |
| MyUser |
MySchema |
表1 |
MySchema.表1 |
|
|
表2 |
MySchema.表2 |
|
|
表3 |
MySchema.表3 |
这下总经理要看报表是没问题了,但是这样架构就形同虚设了。这也是为什么大家搞不清楚架构和用户的关系的原因。假如一个架构可以有多个拥有者,那么就可以彻底解决这个问题。
有人要说了,但是好像我用dbo登陆以后,是可以操作不同架构的表啊,这是为什么呢?那是因为上面的讨论是建立在没有角色的基础上的,要说清楚角色的问题,我们还是要回到李老板的故事。
3.2 人多不好管
1.大量的重复
2.工作调动
3.3 所有权与经营权
3.4 DB Role与App Role
3.5 数据库设计与角色
.NET技术大会的票终于到手了。号码是103,那天打电话到博客园还说没到票,我以为能弄个前五名呢。不过这并没有扑灭我的热情。希望这个技术大会给我明年一个好的预兆。

明天就回家过年了。2月7号还要去水木年华试讲,离MCT又近了一步。所以这些天没办法写技术随笔了,希望不要被人误以为昙花一现。2月20号21号参加技术大会。希望不会跟上课冲突。
明年是关键的一年。我有好多想做的,如果一一实现的话。真的要感谢博客园。
【摘要】【目的】比较字段与属性读取的性能【结论】使用字段比属性赋值,快50-60倍左右,读取,快3倍左右。但是它不可能成为影响性能的主要瓶颈
Effective C# 原则1:尽可能的使用属性(property),而不是数据成员(field)。
把所有的字段都设为私有字段,如果要暴露它们,则把它们封装成属性。这也是微软推荐的方式。具体的好处此处不赘述。
属性的实质是方法(get和set方法),IL中没有属性的概念。因此,使用属性必然会带来性能的降低。到底使用属性和使用字段的性能差多少。我自己写了一个小程序进行测试。主要代码如下:

Code
public class TestFeildProperty
{
static readonly int _RunTime = 10000000;
public static void UseField()
{
DateTime begin = DateTime.Now;
int milliSecondBegin = DateTime.Now.Millisecond;
for (int i = 0; i < _RunTime; i++)
{
_TestField = 1;
}
DateTime end = DateTime.Now;
TimeSpan result = end - begin;
Console.WriteLine(result.ToString());
}
public static void UseProperty()
{
DateTime begin = DateTime.Now;
int milliSecondBegin = DateTime.Now.Millisecond;
for (int i = 0; i < _RunTime; i++)
{
TestField = 1;
}
DateTime end = DateTime.Now;
TimeSpan result = end - begin;
Console.WriteLine(result.ToString());
}
static int _TestField;
static int TestField
{
set { _TestField = value; }
get { return _TestField; }
}
}
测试方式为对字段和属性循环赋值和读取(以上代码只有赋值部分)。

Code
class Program
{
static void Main(string[] args)
{
TestFeildProperty.UseField();
TestFeildProperty.UseProperty();
}
}
测试结果
| 表1 |
循环赋值 |
|
| |
一千万次 |
一亿次 |
| 字段 |
0.03125秒 |
0.3125秒
|
| 属性 |
1.702315秒 |
17.140625秒 |
| 表2 |
循环读取 |
|
| |
一千万次 |
一亿次 |
| 字段 |
0.03125秒 |
0.2825秒
|
| 属性 |
0.09375秒 |
0.984375秒 |
结论:使用属性和字段之间的性能差距大概是:赋值,50-60倍左右,读取,3倍左右。
补充:这个测试结果只是针对int型的,也许对字符串或者其他类型的会有不同结果。
但是可以看出来,对属性赋值一次的时间是一千万分之一秒,所以即使它比字段赋值慢50倍,它不可能成为影响性能的主要瓶颈。应该可以放心使用。
摘要: 在第一节中,我们了解了架构的意义。在第二节的开始,我们暂时忘记架构这个东西。我们假设我们的数据库只有数据库对象。 李老板开了一个小公司,公司有个仓库,堆放了一些货物,由于仓库小,为了节约成本,这个仓库根本没有锁。只要知道仓库在哪里,就可以去取货。这种情况对应数据库来说,就是只要我知道数据库名和表名,...
阅读全文
摘要: 我相信很多人接触这些概念的时候一头雾水。要把这些概念理清楚真不是件容易的事,哪像原始社会,只要能分清楚什么能吃什么不能吃就行了。 但是我始终坚信,每一个概念的产生必然是因为碰到了无法解决的问题。换句话说,如果没有它,必然会导致某些问题难以解决。所以我想从这个角度切入,希望能把这几个复杂而暧昧的多角...
阅读全文
摘要: 最近要为我们的程序增加数据导出的功能,需用连接数据库,再列出数据库中的表。功能并不复杂,实现也没有难度。而且VS本身就自带了这个东东。如图: 我所奇怪的是,既然微软已经做了这个工作。为什么没有给出现成的类库给我们使用呢?我觉得非常明显的,应该有一个System.Data.DBManagement命名空间,用于开发者开发出自己的数据库管理程序。如果仅仅因为不同的数据库不一样。那么可以分别有Syste...
阅读全文