[转载]Webkit是如何加载网页的

mikel阅读(1053)

[转载]Webkit是如何加载网页的 – jz1108 – 博客园.

原文:How WebKit Loads a Web Page

在WebKit渲染网页之前,它需要将页面和所有引用的资源加载完毕。其中会涉及到不同层面的工作。在本文中,我将重点关注WebCore(WebKit中主要渲染组件)是如何在加载过程中发挥作用的。

WebKit包含两条加载流水线,其中一条负责将文档加载到frames当中,另一条负责加载其他资源(比如图片、脚本一类)。下图描述了两条流水线中涉及的主要对象。

 

 

加载Frames

FrameLoader负责将文档加载到frames当中,当点击链接时,FrameLoader会创建一个新的DocumentLoader对 象,并置于“policy”状态,接着就等待WebKit客户端决定该如何处理这次加载。通常,客户端会告诉FrameLoader将加载操作视为一次导 航(而不是一次加载阻塞)

一旦客户端告诉FrameLoader将加载视作导航,FrameLoader将DocumentLoader置于“provisional”状态,此时将开始网络请求并等待结论:这个网络请求最终是下载一个文件还是一份可解析的文档。

DocumentLoader接下来会创建MainResourceLoader对象,它的作用是与浏览器所运行的系统所提供的网络库打交道,网络 库通过ResourceHandle接口提供。将MainResourceLoader和DocumentLoader分离开主要有两个目的:(1) MainResourceLoader处理ResourceHandle回调过程与DocumentLoader分离。(2) MainResourceLoader对象的生命周期与DocumentLoader的生命周期解耦(DocumentLoader的生命周期与 Document对象绑定在一起)。

一旦加载系统通过网络获得足够多信息,以便把文档进行呈现,FrameLoader将DocumentLoader置于“committed”状态,这时Frame对象就要显示这个新加载的文档了。

加载子资源

网页不仅仅由HTML组成。我们还需要加载其中的图片、脚本等等。DocLoader对象就来负责加载这些资源(注意DocLoader和DocumentLoader名字很像,但是分工是不同的)。

我们以加载图片为例。为了加载一张图片,DocLoader首先询问Cache是否已经有该图片的副本(以CachedImage对象存在)。如果存在,DocLoader则可快速响应。为了更加高效,Cache经常在内存中保存解码之后的图片数据,这样避免解码两次。

如果图片没有在Cache中,Cache会创建一个CachedImage对象来表示这个图片。CachedImage对象让Loader对象来发 起网络请求,Loader会创建SubresourceLoader来做这个事情。SubresourceLoader所扮演的角色与刚刚介绍的 MainResourceLoader的角色类似。

改进点

WebKit加载流水线当中有很多需要改进的地方。FrameLoader过于复杂,除了加载frame以外还承担了很多其他工作。比 如,FrameLoader有好几个名为“load”的方法,很容混淆。它来负责创建窗口,看上去和加载frame没有什么关系。另外,加载流水线的若干 阶段没有必要像现在耦合的这么紧,层次划分也不合理,存在不同层次的对象互相访问,比如:MainResourceLoader将获取到的字节直接丢给 FrameLoader而不是DocumentLoader。

如果研究了上面的图,你会发现Cache只会被子资源利用。主要资源的加载并没有得到WebKit内存缓存的支持。如果能够统一这两个加载过程,那么主资源的加载性能也会得到提升。一直以来我们都在不断优化性能来让页面加载的越来越快。

[转载]高性能javascript笔记:数据的存储与访问性能优化

mikel阅读(1113)

[转载]高性能javascript笔记:数据的存储与访问性能优化 – 无赖君子 – 博客园.

  在JavaScript中,数据的存储位置对代码的整体性能有着重要的影响。有四种数据访问类型:直接量局部变量数组项对象成员。直接量和局部变量访问的都非常快,数组项和对象成员的访问速度就有待优化了。

局部变量也就可以理解为在函数内部定义的变量,很明显访问局部变量要比域外的变量要快,因为它位于作用域链的第一个变量对象中(关于作用域链的介绍可以阅读这篇文章)。变量在作用域链的位置越深,访问所需要的时间就越长,全局变量总是最慢的,因为它们位于作用域链的最后一个变量对象。

每种数据类型的访问都需要付出点性能代价,对于直接量和局部变量基本都能消费得起,而访问数组项和对象成员则要代价高点。下图显示了不同浏览器,分别对这四种数据类型进行了200’000次操作所用的时间。

由上图可以看出,要想优化代码的性能,那么尽量使用直接量和局部变量,限制数组项和对象成员的访问次数(将对象成员用一个局部变量来保存)。

首先我们需要了解一下对象成员的访问过程。其实函数就是一个特殊的对象,所以对象成员的访问跟函数的内部变量的访问都差不多,都是基于链的查找,前者是原型链,后者是作用域链,只是怎么个链法有点差别而已。

对象成员包含属性和方法,如果该成员是一个函数就称为方法,否则就称为属性。

JavaScript中的对象是基于原形(原形本身就是一个对象)的,原形是其他对象的基础。当你实例化一个Object对象或其它JS的内置对象时(var obj=new Object() or var obj={}),实例obj的原形由后台自动创建,浏览器FF,safari,Chrome可通过obj.__proto__属性(等同于 Object.prototype)可以访问到这个原形,也正是因为这个原形,每一个实例都能共享原形对象的成员。如:

复制代码
var book = {   name:"Javascript Book",   getName = function(){     return this.name;   } }; alert(book.toString()); //"[object Object]"
复制代码

此代码中,book对象有两个私有成员,分别是属性name和方法getName。book对象并没有定义成员toString,但调用了也没有抛出错误,原因是book对象继承了原形对象的成员。book对象与原形的关系如下:

访问book对象成员toString的过程是这样的,当book.toString()被调用时,后台对成员进行名为”toString”的搜 索,首先从实例book本身开始,如果在book发现名为”toString”的成员,则搜索结束,否则继续向__proto__指向的原型对象搜索,如 果在Object的原形对象都找不到该成员,则表示该成员未定义。通过这种方式,book就可以访问它的原型对象所拥有的每个属性或方法。

对象的另一高级用法就是模拟类和继承类,我喜欢叫这样用法的对象为对象类。继承对象类主要就是依靠原型链来完成的,这个知识点太多需要另外详细 说明。通过上面的对象成员搜索过程,访问对象成员的速度,随着原型链的越深,搜索的速度就越慢。下图就显示了对象成员在原型链中所处的深度与访问时间的关 系:

由上图可清楚的知道,每深入原型链一层都会增加性能的损失,所以像那种遍历对象成员的操作开销很大。还有另外一种常用且损耗性能的做法就是嵌套对象 成员(如window.location.href),像这种最好的做法就是减少点的次数了。比如location.href就比 window.location.href快。

好了,啰啰嗦嗦那么多都不知道自己说什么。目前自己的博文水平还不是很高,有时候就算自己理解了也很难表达出来,别误导别人就好了。真正总结起来就一句话:一个属性或方法在原型链的位置越深,访问它的速度就越慢。解决办法就是:将经常使用的对象成员,数组项和域外的变量存入局部变量中,然后访问这个局部变量。

[转载]C# ToString格式字符串整理Format数字、日期和枚举的标准格式设置说明符

mikel阅读(892)

[转载]C# ToString格式字符串整理(Format)(数字、日期和枚举的标准格式设置说明符)(SamWang) – SamWang – 博客园.

日常开发中,格式字符串的情况非常多。经常也会忘记,经常去查有些麻烦,所以今天就花点时间做个整理。

格式字符串用的比较多的有数字、日期与枚举的格式化。

 

 一、数字格式字符串

C或c 本地货币格式
D或d   十进制格式,把整数转换为以10为基数的书,如果给定一个精度说明符,就加上前导0
E或e   科学计数法(指数)格式,精度说明符设置小数位数(默认为6),格式字符串的大小写(e或E)确定指数符号的大小写。
F或f   固定点格式,精度说明符设置小数位数,可以为0
G或g 普通格式,使用E或F格式取决于哪种格式较简单
N或n   数字格式,用逗号表示千分符,例如32,767.44
P或p   百分数格式
X或x 十六进制格式,精度说明符用于加上前导0

先用例子说明几种格式字符串的方法:

double d = 123.456;
Console.WriteLine("ToString:{0}", d.ToString("C"));
Console.WriteLine("Format:{0}", string.Format("{0:C}",d));
Console.WriteLine("Console:{0:C}", d);

 

输出结果:

数字格式化程序例子:

Console.WriteLine("十六进制格式符X:{0}", (145).ToString("X"));//X只支持整型
double[] numbers = {1054.32179, -195489100.8377, 1.0437E21,
-1.0573e-05};
string[] specifiers = { "C", "E", "F", "G", "N","P",
"R","#,000.000", "0.###E-000",
"000,000,000,000.00###" };
foreach (double number in numbers)
{
Console.WriteLine("Formatting of {0}:", number);
foreach (string specifier in specifiers)
{
Console.WriteLine(" {0,5}: {1}",
specifier, number.ToString(specifier));
}
Console.WriteLine();
}

输出结果:

MSDN:Double.ToString 方法 (String)

 

  二、日期格式字符串

static void DateToString()
{
DateTime dateValue = DateTime.Now;
// Create an array of standard format strings.
string[] standardFmts = {"d", "D", "f", "F", "g", "G", "m", "o",
"R", "s", "t", "T", "u", "U", "y"};
// Output date and time using each standard format string.
foreach (string standardFmt in standardFmts)
Console.WriteLine("{0}: {1}", standardFmt,
dateValue.ToString(standardFmt));
Console.WriteLine();

// Create an array of some custom format strings.
string[] customFmts = {"yyyyMMddHHmmss","h:mm:ss.ff t", "d MMM yyyy", "HH:mm:ss.f",
"dd MMM HH:mm:ss", @"\Mon\t\h\: M", "HH:mm:ss.ffffzzz" };
// Output date and time using each custom format string.
foreach (string customFmt in customFmts)
Console.WriteLine("'{0}': {1}", customFmt,
dateValue.ToString(customFmt));
}

输出结果:

    MSDN:DateTime.ToString 方法 (String)

三、枚举格式字符串

enum Colors { Red, Green, Blue, Yellow = 12 };
static void EnumToString()
{
Colors myColor = Colors.Yellow;

Console.WriteLine("Colors.Red = {0}", Colors.Red.ToString("d"));
Console.WriteLine("Colors.Green = {0}", Colors.Green.ToString("d"));
Console.WriteLine("Colors.Blue = {0}", Colors.Blue.ToString("d"));
Console.WriteLine("Colors.Yellow = {0}", Colors.Yellow.ToString("d"));

Console.WriteLine("{0}myColor = Colors.Yellow{0}", Environment.NewLine);

Console.WriteLine("myColor.ToString(\"g\") = {0}", myColor.ToString("g"));
Console.WriteLine("myColor.ToString(\"G\") = {0}", myColor.ToString("G"));

Console.WriteLine("myColor.ToString(\"x\") = {0}", myColor.ToString("x"));
Console.WriteLine("myColor.ToString(\"X\") = {0}", myColor.ToString("X"));

Console.WriteLine("myColor.ToString(\"d\") = {0}", myColor.ToString("d"));
Console.WriteLine("myColor.ToString(\"D\") = {0}", myColor.ToString("D"));

Console.WriteLine("myColor.ToString(\"f\") = {0}", myColor.ToString("f"));
Console.WriteLine("myColor.ToString(\"F\") = {0}", myColor.ToString("F"));
}

输出结果:

MSDN:Enum.ToString 方法 (String)

[转载]解决某物流企业二维码打印问题

mikel阅读(1153)

转载[原创]解决某物流企业二维码打印问题 – 李华星 – 博客园.

以下文章是在解决客户问题时对二维码的感受,希望能给大家提供一些参考。

1问题背景

浙江某医药物流有限公司(以下简称:A物流公司)系某有限责任公司控股的全资子公司,现已投入使用的物流中心面积达6万平方米。多年来,A物流公司 始终坚持“信息化是现代物流的灵魂”,自行设计开发了ERP、WMS、第三方物流系统、二维条码、办公自动化系统(OA)、物流网站,成功地应用了温湿度 自动监测技术、无线射频技术(RF)、电子标签技术、消防安保红外监控报警技术、GPS监控、业务叫号系统等,引进了国际先进的托盘提升机、螺旋输送机、 自动分拣系统等自动化物流设施,是一个集药品储存、验收养护、物流加工、装卸搬运、集中配送和信息服务等六大功能于一体的专业化医药物流基地。

我们从作业现场了解到,在发货前,需通过自动分拣系统对药品进行分拣,所以,仓管员在库房必须为每一箱药品打印一维码和二维码各一张标签,并粘贴在 药品外包装上,然后通过螺旋输送机进入分拣系统。其中一维码标签用于药品在物流基地内部流通过程中的信息采集;而二维码标签用于药品在外部(如医院)流通 过程中的信息采集。条码标签信息是自主开发的管理信息系统,仓管员通过手持终端远程访问部署在服务器的终端软件,终端软件调用我公司开发的打印组件程序驱 动条码打印机打印条码标签。

我公司主要为A物流公司供应的设备主要有条码打印机、手持终端、无线路由等。其中条码打印机有两种类型:一种是移动打印机,型号为Zebra QL/420 Plus,另一种是台式打印机,型号为Zebra 888;手持终端采用的是Motorola MC50。二维码类型采用的是QR Code,包含近400至500个汉字,标签纸大小为80 * 40。

系统试运行时,台式打印机打印出来的标签上的二维码图像很清晰,手持终端对其识别率很高,扫描效果很好。但是移动式打印机打印出来的标签上的二维码 图像很模糊,而且还会出现重影现象,手持终端对其识别率很低,省物品编码中心的专业仪器对其检测出来的参数未达到识别标准。试用期间,尽管我公司采取了一 些措施,如放大二维码图像大小,调整打印机参数等,还邀请移动条码打印机厂商–斑马公司的工程师协助,二维码移动打印问题仍未得到圆满解决,直接导致A 物流公司的条码系统的推广计划被迫搁置。

2二维码知识

在分析上述问题之前,我们先来了解二维码方面的重要知识。

2.1二维码简介

二维码就是在平面(二维方向)上按照一定规律通过显示黑白相间的图形来记录数据信息的媒介。它可以被图象输入设备或光电扫描设备自动识读以实现信息自动处理。

相对一维码来说,二维码的主要优点是:

1) 高密度编码,信息容量大,可容纳多达1850个大写字母或2710个数字或1108个字节,或500多个汉字;

2)编码范围广,可以把图片、声音、文字、签字、指纹等可以数字化的信息进行编码;

3)容错能力强,具有纠错功能,即使局部损坏的二维码也可以正确得到识读;

4)译码可靠性高,二维码的误码率不超过千万分之一;

5)可引入加密措施,使得条码信息具有很好的保密性和防伪性。

使用二维码,可以很好的解决如下问题:

1)  表示包含汉字在内的数据信息。

2)  在有限的面积上表示大量信息。

3)  对物品进行精确描述。

4)  防止各种证件、卡片及单证的伪造。

5)  在无法连接数据库和不便联网的地方实现数据采集。

2.2二维码分类

按照图形结构来分,常见的二维码可分为行排式二维码和矩阵式二维码。

行排式二维码建立在一维码基础上,按需要由两行或多行的一维码堆积而成。它在编码设计、检验原理、识读方式等方面继承了一维条码的特点,其识读设备、条码印刷与一维条码兼容。常见的行排式二维码主要有PDF417、Code 49、Code 16K等,如下图:

 

PDF417

Code 49

Code 16K

1)  PDF417可以表示 1100 个字节;

2)  Code 49可以表示全部的 ASCⅡ字符;

3)  Code 16K可以表示全字符集的 128个字符及扩展 ASC Ⅱ字符。

矩阵式二维码以矩阵的形式组成,在矩阵相应元素位置上,用点的出现表示二进制“1”,点的不出现表示二进制的“0”,一系列的点的排列组合确定了二 维码所代表的意义。矩阵式二维码是建立在计算机图像处理技术、组合编码的基础上的一种新型图形符号自动识读处理码制。常见的的矩阵式二维码主要有QR Code、Date Matrix、 Maxicode 、Code one等,如下图:

QR Code

Date Matrix

Maxicode

Code one

Grid Matrix

Compact Matrix

1)  QR Code除外可以表示与PDF417相同的字符外,还可以表示有效表示中国汉字、日本汉字。

2)  Data Matrix 能表示 128个字符及扩展 ASCⅡ字符;

3)  Maxicode可表示 128个 ASCⅡ字符及扩展 ASCⅡ字符;

4)  Code one可表示256个 ASCⅡ字符,另加 4个功能字符及一个填充字符。

5)  Grid Matrix和Compact Matrix标准是中国信息产业部于2006年5月份,所颁布的两项国产行业推荐标准。

2.3二维码技术指标

二维码有最重要的两个技术指标,即单位面积的信息存储容量和信息纠错能力,这两个技术指标同时影响着二维码的识别率。有限的单位面积内信息存储容量越大,可供存储纠错信息越少,纠错能力越低,反之,单位面积的信息存储容量越小,可供存储纠错信息越多,纠错能力越强。

3问题分析

根据上述二维码的知识,我们可以分析出,影响二维码识别率的因素主要包括以下方面:

a)       不同的二维码类型;

b)      条码识别设备的能力;

c)       二维码图像的打印质量;

d)      二维码单位面积的信息存储容量;

e)       二维码图像生成算法。

由于A物流公司的药品信息中包含了汉字信息,所以其二维码必须采用QR Code。由于台式打印机打印的二维码能够被MC50很好识别,而移动打印机打印的二维码却识别率很低,所以可以排除上述a)和b)两个因素的影响。这样 一来,我们就应该从上述c)、d)和e)三个因素入手解决问题。

4试验过程及解决方案

第一次试验:由于之前没有接触过二维码,对二维码的知识知之甚少。首先,我在网上到处查找资料,然后潜心研究二维码相关知识,最后,我们得出的结论 是,识别率低主要是纠错能力差的原因造成的;纠错等级越高,图像密度越大,扫描效果越差;QR Code最适合存储汉字的。首次试验没有实质性结果。

第二次试验:采用QR Code,通过逐步调整图像大小来提高打印质量,经过测试后发现确实有些效果,能够略微提高识别率。第二次试验收效甚微。

第三次试验:一位专门研究打印机的专家提醒,可以通过调整打印机的速度和浓度来提高打印质量,但经过测试后发现效果依然不明显。

第四次试验:通过与条码事业部的沟通过程中得知,不同价格的标签纸质量不一样。在本次测试过程中,我用质量最差的标签纸打印出来的二维码根本无法识别,而用质量最好的标签纸(理光)打印出来的二维码质量明显提高。所以,打印纸的质量好坏严重影响打印质量。

第五次试验:经过与A物流公司的工程师讨论,确定把二维码得字符容量降到300个汉字左右,配合前四次试验的方案,识别率明显提升,但是又发现扫描距离在很小的范围,这对操作员太麻烦了。

第六次试验:通过研究资料发现,生成二维码图像的程序算法会影响条码打印质量。所以接下来,我们立即对扫描接口程序结构分析,发现我公司开发的打印 接口程序PLable.dll调用了一个第三方二维码生成组件。为了验证程序算法的影响,我们从网上下载了一个二维码生成软件PtBarcodeEnc (试用版),在相同的移动打印机、相同的标签、相同的字符数的情况下,我们发现生成二维码的程序算法对扫描效果影响很大。

第七次试验:经过前面六次之后,基本上可以确定能解决问题,关键是能找到最优的状态。所以,我们在本次测试中,设定5种不同的图像大小,4种不同的字符容量,经过排列组合成20种情况,并且把20种组合的效果打印出来,最后得到最优的效果。

总结七次测试,我们主要从改变图像大小、更换二维码生成程序、提高纸张质量、调整打印机参数等四个方案结合起来,并对接口程序进行优化和完善,这样就大幅提升二维码的打印质量。

5实施效果

我们从改变图像大小、更换二维码生成程序、提高纸张质量、调整打印机参数,大幅提升二维码的打印质量,把包含600个字符容量的二维码在标签纸打印 后,识别率基本上可以达到100%,扫描距离的有效范围在6CM至15CM之间,经过现场的真实测试后,二维码已经集成到管理信息系统系统。

[转载]Android 4.1 Jelly Bean(果冻豆) SDK4.1最新下载

mikel阅读(924)

[转载]Android 4.1 Jelly Bean(果冻豆) SDK4.1最新下载 – vus520 – 博客园.

昨天晚上Android 4.1的最新操作系统发布了,我在第一时间为大家进行了谷歌大会的图文直播,为了方便广大的网友,我们第一时间同时为大家提供SDK各平台下载文件,大家赶快来下载吧!
1、【eoeAndroid社区】Google I/O 2012 主题演讲直播帖

2、Google I/O 2012 主题演讲实录,五弹齐发的震撼感
链接:http://www.eoeandroid.com/thread-181498-1-1.html

3、Android 4.1正式发布 十大新特性
链接:http://www.eoeandroid.com/thread-181506-1-1.html


附上cdn链接
Windows版:
http://s1.eoeandroid.com/sdk/4.1/android-sdk_r20-windows.zip(推荐)

http://s1.eoeandroid.com/sdk/4.1/installer_r20-windows.exe

Mac版:

http://s1.eoeandroid.com/sdk/4.1/android-sdk_r20-macosx.zip

linux版:

http://s1.eoeandroid.com/sdk/4.1/android-sdk_r20-linux.tgz

[转载]新浪微博API使用

mikel阅读(985)

[转载]新浪微博API使用 – Dufresne – 博客园.

  如今社交网络异常活跃,接触到的几乎每个应用都使用有一个功能:分享。分享到各种SNS上,

现今以新浪微博最火,其提供了良好的API接口等文档供开发者使用,正好称此有机会也来接触一下,

说不定那天也能用得上。下面简单记录下初步使用过程中遇到的问题,以便对后来者有点作用。

授权认证

  在使用新浪微博API前,还是很有必要先了解一下新浪微博授权认证过程如何。

可以参考博文:http://whatandroid.blog.51cto.com/2172732/818665

APP KEY的申请

       为了使用新浪微博开放平台提供的API,你需要先注册一个应用,获得一个专属的App Key和App Secret。

Key跟Secret的使用方式跟其他一些协议中的公钥私钥的方案相类似,你可以使用你所熟悉的编程语言将key和secret结合,

为你发出的每个请求添加签名,以此来向新浪微博开放平台表明自己身份的合法性。

       注册方法:登录新浪微博,点击应用 找到:

   

然后就可以创建应用,填写信息,不需要审核通过就可以得到App Key。

下载SDK

       http://open.weibo.com/wiki/SDK

       到微博开放平台下载就可以了,我的是Android-SDK版本。weibo4Android-1.2.1.zip。

里面已经提供了很多example,可以参考。直接导入到Eclipse里面也是可以使用的。

需要配置一下版本,将Weibo类中的

public class Weibo {

       public static String CONSUMER_KEY = “”; //填入申请到的

       public static String CONSUMER_SECRET = “”;//填入申请到的

}

然后就可以测试运行。

API使用

       其中提供了很多example,这样我们就可以来熟悉微博API的使用。

在开发中我们可能需要将其集成到我们的APP中,这其中会有一些问题,下面看看我遇到的以及解决方法。在android4.0中:

1  E/AndroidRuntime(3595): java.lang.RuntimeException: Unable to start activity ComponentInf{com.weibo/com.weibo.ShareActivity}:android.os.NetworkOnMainThreadException.

       NetworkOnMainThreadException:在主线程UI线程中访问网络 异常。访问网络是一件耗时的操作,可能被禁止这样使用。

       解决方法:开辟新的线程中访问网络。(最好是这样

              或者使用android提供的类StrictMode自动进行优化。

              具体使用方法在在程序入口如onCreate中加入:

StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()    

               .detectDiskReads()    

               .detectDiskWrites()    

               .detectNetwork() 

               .penaltyLog()    

               .build());

              StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()    

               .detectLeakedSQLLiteObjects()    

               .detectLeakedClosableObjects()    

               .penaltyLog()    

               .penaltyDeath()    

               .build());

 

  StrictMode用意具体可参考http://www.cnblogs.com/zelos/archive/2011/02/27/1966403.html

 

2  E/AndroidRuntime(6195): java.lang.NoClassDefFoundError: org.apache.commons.httpclient.methods.PostMethod

       分享图片的时候会遇上,找不到此类定义,可是明明已经导入相应的SDK中提供的包了。

       将包都放入到lib文件下,在Eclipse中右键点击lib文件夹,选Bulid path –> Use as source folder。

 

3  weibo.getOAuthRequestToken(callback_url);中callback_url问题

       设置不正确可能在登录之后无法返回到相应的Activity中;

       这里的设置和AndroidManifest.xml中对activity的filter对应:

       <data

       android:host=”ShareActivity”

       android:scheme=”share” />

然后requestToken =weibo.getOAuthRequestToken(“share://ShareActivity“); 要对应。

才能返回到ShareActivity中;

scheme是工程名,必须得小写。host是要返回的activity。

 

发布微博例子

  顺便附上一个来自网络上,能够分享图片的例子。我把它移到我这里工程上来的完整程序。

  来自文章:http://www.pocketdigi.com/20110727/424.html

  程序例子下载:

      http://files.cnblogs.com/bastard/Share.rar

[转载]JavaScript判断浏览器类型及版本

mikel阅读(964)

[转载]JavaScript判断浏览器类型及版本 – 李战 – 博客园.

你知道世界上有多少种浏览器吗?除了我们熟知的IE, Firefox, Opera, Safari四大浏览器之外,世界上还有近百种浏览器。

几天前,浏览器家族有刚诞生了一位小王子,就是Google推出的Chrome浏览器。由于Chrome出生名门,尽管他还是个小家伙,没有人敢小看他。以后,咱们常说浏览器的“四大才子”就得改称为“五朵金花”了。

 

网站前端开发中,浏览器兼容性问题本已让我们手忙脚乱,Chrome的出世不知道又要给我们添多少乱子。浏览器兼容性是前端开发框架要解决的第一个问题,要解决兼容性问题就得首先准确判断出浏览器的类型及其版本。

 

JavaScript是前端开发的主要语言,我们可以通过编写JavaScript程序来判断浏览器的类型及版本。JavaScript判断浏览器类型一 般有两种办法,一种是根据各种浏览器独有的属性来分辨,另一种是通过分析浏览器的userAgent属性来判断的。在许多情况下,值判断出浏览器类型之 后,还需判断浏览器版本才能处理兼容性问题,而判断浏览器的版本一般只能通过分析浏览器的userAgent才能知道。

 

我们先来分析一下各种浏览器的特征及其userAgent。

 

IE

 

只有IE支持创建ActiveX控件,因此她有一个其他浏览器没有的东西,就是ActiveXObject函数。只要判断window对象存在 ActiveXObject函数,就可以明确判断出当前浏览器是IE。而IE各个版本典型的userAgent如下:

Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2)
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Mozilla/4.0 (compatible; MSIE 5.0; Windows NT)

其中,版本号是MSIE之后的数字。

 

Firefox

 

Firefox中的DOM元素都有一个getBoxObjectFor函数,用来获取该DOM元素的位置和大小(IE对应的中是 getBoundingClientRect函数)。这是Firefox独有的,判断它即可知道是当前浏览器是Firefox。Firefox几个版本的 userAgent大致如下:

 

        Mozilla/5.0 (Windows; U; Windows NT 5.2) Gecko/2008070208 Firefox/3.0.1
Mozilla/5.0 (Windows; U; Windows NT 5.1) Gecko/20070309 Firefox/2.0.0.3
Mozilla/5.0 (Windows; U; Windows NT 5.1) Gecko/20070803 Firefox/1.5.0.12
      其中,版本号是Firefox之后的数字。

 

Opera

 

Opera提供了专门的浏览器标志,就是window.opera属性。Opera典型的userAgent如下:

Opera/9.27 (Windows NT 5.2; U; zh-cn)
Opera/8.0 (Macintosh; PPC Mac OS X; U; en)
Mozilla/5.0 (Macintosh; PPC Mac OS X; U; en) Opera 8.0

其中,版本号是靠近Opera的数字。

 

Safari

 

Safari浏览器中有一个其他浏览器没有的openDatabase函数,可做为判断Safari的标志。Safari典型的userAgent如下:

 

 

Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13
Mozilla/5.0 (iPhone; U; CPU like Mac OS X) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A93 Safari/419.3

其版本号是Version之后的数字。

 

Chrome

 

Chrome有一个MessageEvent函数,但Firefox也有。不过,好在Chrome并没有Firefox的getBoxObjectFor 函数,根据这个条件还是可以准确判断出Chrome浏览器的。目前,Chrome的userAgent是:

Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13

其中,版本号在Chrome只后的数字。

 

有趣的是,Chrome的userAgent还包含了Safari的特征,也许这就是Chrome可以运行所有Apple浏览器应用的基础吧。

只要了解了以上信息,我们就可以根基这些特征来判断浏览器类型及其版本了。我们会将判断的结果保存在Sys名字空间中,成为前端框架的基本标志信息,供今 后的程序来读取。如果判断出谋种浏览器,Sys名字空间将有一个该浏览器名称的属性,其值为该浏览器的版本号。例如,如果判断出IE 7.0,则Sys.ie的值为7.0;如果判断出Firefox 3.0,则Sys.firefox的值为3.0。下面是判断浏览器的代码:

<script type="text/javascript">

        var Sys = {};

        var ua = navigator.userAgent.toLowerCase();

        if (window.ActiveXObject)

            Sys.ie = ua.match(/msie ([\d.]+)/)[1]

        else if (document.getBoxObjectFor)

            Sys.firefox = ua.match(/firefox\/([\d.]+)/)[1]

        else if (window.MessageEvent && !document.getBoxObjectFor)

            Sys.chrome = ua.match(/chrome\/([\d.]+)/)[1]

        else if (window.opera)

            Sys.opera = ua.match(/opera.([\d.]+)/)[1]

        else if (window.openDatabase)

            Sys.safari = ua.match(/version\/([\d.]+)/)[1];

        

        //以下进行测试

        if(Sys.ie) document.write('IE: '+Sys.ie);

        if(Sys.firefox) document.write('Firefox: '+Sys.firefox);

        if(Sys.chrome) document.write('Chrome: '+Sys.chrome);

        if(Sys.opera) document.write('Opera: '+Sys.opera);

        if(Sys.safari) document.write('Safari: '+Sys.safari);


    </script>

我们把对IE的判断放在第一,因为IE的用户最多,其次是判断Firefox。按使用者多少的顺序来判断浏览器类型,可以提高判断效率,少做无用功。之所以将Chrome放在第三判断,是因为我们预测Chrome很快会成为市场占有率第三的浏览器。其中,在分析浏览器版本时,用到了正则表达式来析取其中的版本信息。

如果你的JavaScript玩得很高,你还可以将前面的判断代码写成这样:

<script type="text/javascript">

        var Sys = {};

        var ua = navigator.userAgent.toLowerCase();

        window.ActiveXObject ? Sys.ie = ua.match(/msie ([\d.]+)/)[1] :

        document.getBoxObjectFor ? Sys.firefox = ua.match(/firefox\/([\d.]+)/)[1] :

        window.MessageEvent && !document.getBoxObjectFor ? Sys.chrome = ua.match(/chrome\/([\d.]+)/)[1] :

        window.opera ? Sys.opera = ua.match(/opera.([\d.]+)/)[1] :

        window.openDatabase ? Sys.safari = ua.match(/version\/([\d.]+)/)[1] : 0;

        

        //以下进行测试

        if(Sys.ie) document.write('IE: '+Sys.ie);

        if(Sys.firefox) document.write('Firefox: '+Sys.firefox);

        if(Sys.chrome) document.write('Chrome: '+Sys.chrome);

        if(Sys.opera) document.write('Opera: '+Sys.opera);

        if(Sys.safari) document.write('Safari: '+Sys.safari);

    </script>

这样可以使JavaScript代码更精简些。当然,可读性稍差一些,就看你是重视效率还是重视可维护性了。

使用不同特征来判断浏览器的方法,虽然在速度上比用正则表达式分析userAgent要来的快,不过这些特征可能会随浏览器版本而变化。比如,一种浏览器本来独有的特性取得了市场上的成功,其他浏览器也就可能跟着加入该特性,从而使该浏览器的独有特征消失,导致我们的判断失败。因此,相对比较保险的做法是通过解析userAgent中的特征来判断浏览器类型。何况,反正判断版本信息也需要解析浏览器的userAgent的。

通过分析各类浏览器的userAgent信息,不难得出分辨各类浏览器及其版本的正则表达式。而且,对浏览器类型的判断和版本的判断完全可以合为一体地进行。于是,我们可以写出下面的代码:

<script type="text/javascript">

        var Sys = {};

        var ua = navigator.userAgent.toLowerCase();

        var s;

        (s = ua.match(/msie ([\d.]+)/)) ? Sys.ie = s[1] :

        (s = ua.match(/firefox\/([\d.]+)/)) ? Sys.firefox = s[1] :

        (s = ua.match(/chrome\/([\d.]+)/)) ? Sys.chrome = s[1] :

        (s = ua.match(/opera.([\d.]+)/)) ? Sys.opera = s[1] :

        (s = ua.match(/version\/([\d.]+).*safari/)) ? Sys.safari = s[1] : 0;



        //以下进行测试

        if (Sys.ie) document.write('IE: ' + Sys.ie);

        if (Sys.firefox) document.write('Firefox: ' + Sys.firefox);

        if (Sys.chrome) document.write('Chrome: ' + Sys.chrome);

        if (Sys.opera) document.write('Opera: ' + Sys.opera);

        if (Sys.safari) document.write('Safari: ' + Sys.safari);

    </script>

其中,采用了“… ? … : …”这样的判断表达式来精简代码。判断条件是一条赋值语句,既完成正则表达式的匹配及结果复制,又直接作为条件判断。而随后的版本信息只需从前面的匹配结果中提取即可,这是非常高效的代码。

以上的代码都是为了打造前端框架所做的预研,并在五大浏览器上测试通过。今后,判断某种浏览器只需用if(Sys.ie)或if(Sys.firefox)等形式,而判断浏览器版本只需用if(Sys.ie == ‘8.0’)或if(Sys.firefox == ‘3.0’)等形式,表达起来还是非常优雅的。

前端框架项目已经启动,一切就看过程和结果了…

[转载]揭秘应用程序池帐号ApplicationPoolIdentity

mikel阅读(1040)

[转载]揭秘应用程序池帐号ApplicationPoolIdentity – 陈希章 – 博客园.

内容摘要

在IIS 7和IIS 7.5中,我们可以为应用程序池设置一个特殊的Identity(用户标识):ApplicationPoolIdentity。 那么这个标识到底是什么意思?它是具体什么身份呢?这一讲我将为你揭晓答案。

这个标识设置会在应用程序池启动的时候,动态地产生一个用户身份,这个用户身份只具备对网站根目录的访问权限,这样可以进一步地保证服务器的安全性。

作为管理员和开发人员,都需要了解这个设置的特性。

讲义地址

[转载]行转列:SQL SERVER PIVOT与用法解释

mikel阅读(1214)

[转载]行转列:SQL SERVER PIVOT与用法解释 – 柯柏文 – 博客园.

在数据库操作中,有些时候我们遇到需要实现“行转列”的需求,例如一下的表为某店铺的一周收入情况表:

WEEK_INCOME(WEEK VARCHAR(10),INCOME DECIMAL)

我们先插入一些模拟数据:

INSERT INTO WEEK_INCOME
SELECT '星期一',1000
UNION ALL
SELECT '星期二',2000
UNION ALL
SELECT '星期三',3000
UNION ALL
SELECT '星期四',4000
UNION ALL
SELECT '星期五',5000
UNION ALL
SELECT '星期六',6000
UNION ALL
SELECT '星期日',7000

一般我们最经常使用的查询是查询一周中每天或某几天的收入,例如查询周一至周日全部的收入:

SELECT WEEK,INCOME FROM WEEK_INCOME

得到如下的查询结果集:

WEEK INCOME
星期一 1000
星期二 2000
星期三 3000
星期四 4000
星期五 5000
星期六 6000
星期日 7000

但是在一些情况下(往往是某些报表中),我们希望在一行中显示周一至周日的收入,这时候查询结果集应该是这样的:

星期一 星期二 星期三 星期四 星期五 星期六 星期日
1000 2000 3000 4000 5000 6000 7000

这种情况下,SQL查询语句可以这样写:

SELECT
SUM(CASE WEEK WHEN '星期一' THEN INCOME END) AS [星期一],
SUM(CASE WEEK WHEN '星期二' THEN INCOME END) AS [星期二],
SUM(CASE WEEK WHEN '星期三' THEN INCOME END) AS [星期三],
SUM(CASE WEEK WHEN '星期四' THEN INCOME END) AS [星期四],
SUM(CASE WEEK WHEN '星期五' THEN INCOME END) AS [星期五],
SUM(CASE WEEK WHEN '星期六' THEN INCOME END) AS [星期六],
SUM(CASE WEEK WHEN '星期日' THEN INCOME END) AS [星期日]
FROM WEEK_INCOME

但是,在SQL SERVER 2005中提供了更为简便的方法,这就是”PIVOT”关系运算符。(相反的“列转行”是UNPIVOT),以下是使用PIVOT实现“行转列”的SQL语句

SELECT [星期一],[星期二],[星期三],[星期四],[星期五],[星期六],[星期日]
FROM WEEK_INCOME
PIVOT
(
SUM(INCOME) for [week] in([星期一],[星期二],[星期三],[星期四],[星期五],[星期六],[星期日])
)TBL

请参考MSDN中关于PIVOT的用法:

http://technet.microsoft.com/zh-cn/library/ms177410(v=sql.105).aspx

 

但是MSDN上的描述太过于规范严肃,我看了半天还没弄清楚怎样使用PIVOT,搞不清楚PIVOT里面的语法的含义。于是又google了很多资料,以及通过上面提到的WEEK_INCOME表例子作了试验,最终搞清楚了其用法。在网上有篇博文解释的很好:T-SQL PIVOT語法剖析與實戰,基本上我要写的就是参照该博文,再加上自己一点个人理解。

要理解PIVOT语法,就是要清楚微软为什么这样设计PIVOT,但我相信是现实需求催生设计思路,所以归根到底我们还是要弄清楚什么是“行转列”:

正常情况下的查询结果是这样:

星期一           1000
星期二           2000
星期三           3000
星期四           4000
星期五           5000
星期六           6000
星期日           7000

行转列后是这样:

星期一   星期二   星期三   星期四   星期五   星期六   星期日
1000    2000    3000    4000    5000    6000    7000

也就是说,行转列后,原来的某个列的值变做了列名,在这里就是原来WEEK列的值“星期一”,”星期二”…”星期日”边做了列名,而我们需要做的另一个工作就是计算这些列的值(这里的“计算”其实就是PIVOT里面的聚合函数(sum,avg等))

现在结合注释来分析一下PIVOT语法(在这之前最好看看我上面提到博文:T-SQL PIVOT語法剖析與實戰,里面说到的PIVOT语法的三个步骤挺重要):

SELECT [星期一],[星期二],[星期三],[星期四],[星期五],[星期六],[星期日]--这里是PIVOT第三步(选择行转列后的结果集的列)这里可以用“*”表示选择所有列,也可以只选择某些列(也就是某些天)
FROM WEEK_INCOME --这里是PIVOT第二步骤(准备原始的查询结果,因为PIVOT是对一个原始的查询结果集进行转换操作,所以先查询一个结果集出来)这里可以是一个select子查询,但为子查询时候要指定别名,否则语法错误
PIVOT
(
    SUM(INCOME) for [week] in([星期一],[星期二],[星期三],[星期四],[星期五],[星期六],[星期日])--这里是PIVOT第一步骤,也是核心的地方,进行行转列操作。聚合函数SUM表示你需要怎样处理转换后的列的值,是总和(sum),还是平均(avg)还是min,max等等。例如如果week_income表中有两条数据并且其week都是“星期一”,其中一条的income是1000,另一条income是500,那么在这里使用sum,行转列后“星期一”这个列的值当然是1500了。后面的for [week] in([星期一],[星期二]...)中 for [week]就是说将week列的值分别转换成一个个列,也就是“以值变列”。但是需要转换成列的值有可能有很多,我们只想取其中几个值转换成列,那么怎样取呢?就是在in里面了,比如我此刻只想看工作日的收入,在in里面就只写“星期一”至“星期五”(注意,in里面是原来week列的值,"以值变列")。总的来说,SUM(INCOME) for [week] in([星期一],[星期二],[星期三],[星期四],[星期五],[星期六],[星期日])这句的意思如果直译出来,就是说:将列[week]值为"星期一","星期二","星期三","星期四","星期五","星期六","星期日"分别转换成列,这些列的值取income的总和。
)TBL--别名一定要写

以上是我对PIVOT的理解,我尽所能表达出来。不过话说回来,个人的理解的方式也不同,就如我开始看了很多篇博文,都没有搞清楚PIVOT用法。结果还是硬的通过例子和别人的博文再加上思考才弄懂了,所以如果各位看了本篇之后仍不能理解,那很正常,配合例子再加上自己思考,慢慢的定能理解。

[转载]Linq学习之关键字

mikel阅读(1211)

[转载]Linq学习之关键字 – line.cheng – 博客园.

书写Linq时,分为方法语法和查询表达式两种语法方式。

这里假设你对Lambda表达式,匿名类型,静态扩展方法 有一定的认识。

以下,对Linq中的部分关键字进行介绍。

学习过程中,主要参考了 http://www.cnblogs.com/goscan/archive/2011/05/05/Linq_study_log.html 一文,在这里表示对作者进行感谢。

From关键字 

使用From时,From后跟数据源的一个变量,in 后指定数据源。数据源的类型必须是实现IEnumerable<T>,如果仅仅实现了IEnumerable类型可以通过Cast<T>方法来转换

示例代码

//from 的Container必须是IEnumerable<T> 类型
         public void StudyFrom()
         {
 
             //IEnumerable<BookMarker> query = from bm in Container select bm;
             string[] array = new string[] { "fa", "dsfsa", "fdsaf" };
             IEnumerable<string> res = from s in array select s;//数组可以
             ArrayList al = new ArrayList() { "fds", "dsaf", "fdsa" };//ArrayList并没有实现IEnumerable<T>
             IEnumerable<string> data = al.Cast<string>();//可以转换为IEnumerable<T>类型
         }

Select关键字

Select可以看做事对查询结果的投影,类似与SQL中查询出来的新表,在这里是新的对象。

可以对新的对象进行重组(查询结果的投影),生成新的对象。

示例代码

//select 是对查询结果的投影
        //
        public void StudySelect()
        {
            //将结果投影到一个匿名类型上
            var bs = from p in Container select new { p.Name, p.Value };
            //将投影结果进行计算重组
            var bs2 = from b in Container
                      select new
                                 {
                                     Name = b.Name,
                                     Tag = b.Category + "$" + b.Tag
                                 };
            var bs3 = Container.Select(p => new { Name = p.Name, Tag = p.Category + "$" + p.Tag });//利用Lambda查询
        }

Join关键字

Join关键字稍显复杂。格式如下

From a in aSet join b in bSet on a.ID equals b.ID select new{a,b}

或者

From a in aSet join b in bSet on value1 equals value2 select *****

或者

From a in aSet join b in bSet on Func1() equals Func2() select ***

方法语法的格式为

aSet.Join(bSet , 获得判等左值的委托,获得判等右值的委托,查询结果投影委托 )

例如 aSet.Join(bSet,a=>a.ID,b=>b.ID,(a,b)=>new {a,b})

示例代码

/// <summary>
        /// Join方法 from a in aSet join b in bSet on a.Id equals b.Id select new { a,b}
        /// from a in aSet join b in bSet on value1 equals value2 ……
        /// from a in aSet join b in bSet on Func1() equals
        /// 操作符号需要是equals 或者not equals 
        /// Join方法 ( 第二个需要连接的数据源,表达式1,表达式2,输出结果 ) 表达式1会默认和表达式2相等最为连接条件
        /// </summary>
        public void StudyJoin()
        {
            string[] indexs = new string[] { "1", "2", "6", "9" };
            var res = from i in indexs
                      join b in Container
                          on "ID-" + i equals b.ID
                      select new string[] { i, b.Name };
            foreach (string[] strings in res)
            {
                Console.WriteLine(string.Format("index->{0},value-{1}", strings[0], strings[1]));
            }

            var res2 = indexs.Join(Container, i => "ID-" + i, c => c.ID, (i, c) => new { i, c });//函数式查询
            foreach (var item in res2)
            {
                Console.WriteLine(item.i + "==>" + item.c.Name);
            }
        }

Group关键字

Group关键字表示对数据源进行分组,使用GroupBy组合,Group后跟数据源,by后跟分组依据,into 表示将分组结果存放到的变量,该变量会默认有一个Key属性,即为by 的依据(组别),该变量类型是IGrouping类型,所以查出来就是IGrouping的一个集合。如果想要访问其中的元素,需要使用两层ForEach进行遍历。

示例代码

/// <summary>
        /// Group将对查询结果进行分组,使用Group By 组合,group后边跟为哪个对象分组,by后边跟上分组的依据
        /// 分组完成需要使用into将分组后的结果放在一个变量中
        /// 访问该变量的Key属性可以得到组别
        /// </summary>
        public void StudyGroup()
        {
            var res = from bookMarker in Container
                      group bookMarker by bookMarker.ID.Length
                          into bg
                          select new { bg.Key, bg };
            
            res.ToList().ForEach
                (
                    item =>
                        {
                            Console.WriteLine(string.Format("Key:{0},----", item.Key));
                            item.bg.ToList().ForEach(
                                bookmarker => Console.WriteLine(string.Format("ID:{0},Name:{1}", bookmarker.ID, bookmarker.Name)));
                        }

                );
        }

Let关键字

Let关键字允许在查询过程中,自定一个一个变量,供使用。

示例代码

/// <summary>
        /// let运行在查询过程中,自定义一个变量,供使用
        /// </summary>
        public void StudyLet()
        {
            var query = from bookmarker in Container
                        let Type = "书签"
                        select new
                                   {
                                       Type = Type,
                                       Name = bookmarker.Name,
                                       Url = bookmarker.Value
                                   };
            foreach (var VARIABLE in query)
            {
                Console.WriteLine(string.Format("书签名{0},地址是{1}。类型为{2}", VARIABLE.Name, VARIABLE.Url, VARIABLE.Type));
            }
        }

Take和Skip关键字

Skip表示跳过集合的若干个元素,Take表示从当前集合的开始处取N个元素

示例

public void StudyTakeSkip()
        {
            var query = from bookmarker in Container select bookmarker;
           IEnumerable<BookMarker> source=   query.Skip(2).Take(6);
            Printer<BookMarker>.ConPrint( source, item=>string.Format( "名字:{0}ID:{1}",item.Name,item.ID ) );
        }

Yield关键字

Yield关键字可以使得查询延迟加载。也就是在使用到该数据时,在进行数据的添加或者访问。但是如果使用了聚合函数,例如MAX,OrderBy,等,该特性竟会失效,将遍历所有元素。

示例代码

将会发生死循环。

//LazyLoad示例

        //yield 关键字可以使得 查询时延迟加载,即使用是再向数据容器中添加数据
        public static  IEnumerable<int> InitData()
        {
            int i = 0;
            while (true)
            {
                yield return i++;
            }
        }


        public void LazyLoad()
        {
            var query = from i in InitData() select i;
            Printer<int>.ConPrint(query.Take(10),item=>item.ToString());
        }

        public void CannotLazyLoad()
        {
            var query = from i in InitData() select i;
            Printer<int>.ConPrint(query.OrderBy(item=>item).Take(10),item=>item.ToString());//使用OrderBy时,会尝试遍历所有元素,故会无限循环

        }

之下是DataContainer.cs

public class DataContainer
    {
        public static List<BookMarker> BookMarkers { get; set; } 

       static DataContainer()
        {
            InitBookMarkers();
        }

        static private void InitBookMarkers()
        {
            BookMarkers=new List<BookMarker>();
            for (int i = 0; i < 20; i++)
            {
                BookMarker b=new BookMarker()
                                 {
                                     ID = string.Format("ID-{0}",i),
                                     Name = string.Format("Name-{0}",i),
                                     Category = string.Format("Category-{0}",i),
                                     Tag = string.Format("Tag-{0}",i),
                                     Value=string.Format("Value-{0}",i)
                                 };
                BookMarkers.Add(b);
            }
            
        }
    }

    public class  BookMarker
    {
        public string Name { get; set; }
        public string ID { get; set; }
        public string Category { get; set; }
        public string Tag { get; set; }
        public string Value { get; set; }
    }

Printer.cs

public class Printer<T>
    {
        public static void ConPrint<T>(IEnumerable<T> source,Func<T,string> toStrFunc  )
        {
            foreach (T t in source)
            {
                Console.WriteLine( toStrFunc(t) );
            }
        }
    }

Program.cs

static void Main(string[] args)
        {
            KeyWordsStudy keyWordsStudy=new KeyWordsStudy();
            //keyWordsStudy.StudyJoin();
            //keyWordsStudy.StudyLet(); 
            keyWordsStudy.StudyGroup();
            keyWordsStudy.StudyTakeSkip();
            //keyWordsStudy.LazyLoad();
            //keyWordsStudy.CannotLazyLoad();会抛出内存溢出的异常,应为orderby的存在,导致 试图遍历所有成员//Console.WriteLine("结束");
            Console.ReadKey();

        }