Lazy<T>
Lazy<T>
对象的创建方式,始终代表了软件工业的生产力方向,代表了先进软件技术发展的方向,也代表了广大程序开发者的集体智慧。以new的方式创建,通过工厂方法,利用IoC容器,都以不同的方式实现了活生生实例成员的创生。而本文所关注的Lazy<T>也是干这事儿的。不过,简单说来,Lazy<T>要实现的就是按“需”创建,而不是按时创建。
我们往往有这样的情景,一个关联对象的创建需要较大的开销,为了避免在每次运行时创建这种家伙,有一种聪明的办法叫做实现“懒对象”,或者延迟加载。.NET 4.0之前,实现懒对象的机制,需要开发者自己来实现与管理它的定义如下:

[Serializable]
public class Lazy<T>
{
public Lazy();
public Lazy(bool isThreadSafe);
public Lazy(Func<T> valueFactory);
public Lazy(Func<T> valueFactory, bool isThreadSafe); public bool IsValueCreated { get; }
public T Value { get; } public override string ToString();
}

假设,我们有一个大块头:

public class Big
{
public int ID { get; set; } // Other resources
}

从Lazy<T>的定义可知,其Value属性就是我们包装在Lazy Wrapper中的真实Big对象,那么当我们第一次访问lazyBig.Value时,就回自动的创建Big实例。

static void Main(string[] args)
{
Lazy<Big> lazyBig = new Lazy<Big>(); Console.WriteLine(lazyBig.Value.ID);
}

当然,有其定义可知,Lazy远没有这么小儿科,它同时还可以为我们提供以下的服务:
- 通过IsValueCreated,获取是否“已经”创建了实例对象。
- 解决非默认构造函数问题。
显而易见。我们的Big类并没有提供带参数构造函数,那么如下的Big类:

public class Big
{
public Big(int id)
{
this.ID = id;
} public int ID { get; set; } // Other resources
}

上述创建方式将引发运行时异常,提示包装对象没有无参的构造函数。那么,这种情形下的延迟加载,该如何应对呢?其实Lazy<T>的构造中还包括:
public Lazy(Func<T> valueFactory);
它正是用来应对这样的挑战:
static void Main(string[] args)
{
// Lazy<Big> lazyBig = new Lazy<Big>();
Lazy<Big> lazyBig = new Lazy<Big>(() => new Big(100)); Console.WriteLine(lazyBig.Value.ID);
}
其实,从public Lazy(Func<T> valueFactory)的定义可知,valueFactory可以返回任意的T实例,那么任何复杂的构造函数,对象工厂或者IoC容器方式都可以在此以轻松的方式兼容,例如:
public class BigFactory
{
public static Big Build()
{
return new Big(100);
}
}
可以应用Lazy<T>和BigFactory实现Big的延迟加载:
static void Main(string[] args)
{
Lazy<Big> lazyBig = new Lazy<Big>(() => BigFactory.Build()); Console.WriteLine(lazyBig.Value.ID);
}
- 提供多线程环境支持。
另外的构造器:
public Lazy(bool isThreadSafe);
public Lazy(Func<T> valueFactory, bool isThreadSafe);
中,isThreadSafe则应用于多线程环境下,如果isThreadSafe为false,那么延迟加载对象则一次只能创建于一个线程。
关于Lazy<T>的应用,其实已经不是一个纯粹的语言问题,还涉及了对设计的考量,例如实现整个对象的延迟加载,或者实现延迟属性,考量线程安全等等。就不说教太多。因为,.NET 4.0提供的关注度实在不少,我们眼花缭乱了。
郑重声明本文非原创……
ASP.NET基础之HttpContext学习
上一个随笔主要针对HttpModeul[ASP.NET基础之HttpModule学习]内容进行一个简单的学习;本章内容我将对ASP.NET另一个主要的知识点HttpContext进行学习;首先我们简单了解一些理论的知识,然后结合一些实例演示我们开发时经常要用到的一些功能,其中有些部分是从网上整理所得,希望对您的阅读有所帮助;
一:HttpContext理论知识:
1:HttpContext类它对Request、Respose、Server等等都进行了封装,并保证在整个请求周期内都可以随时随地的调用;为继承 IHttpModule 和 IHttpHandler 接口的类提供了对当前 HTTP 请求的 HttpContext 对象的引用。该对象提供对请求的内部Request、Response 和 Server 属性的访问。HttpContext的命名空间:System.Web(在 system.web.dll 中);除了对几个对象进行封装外它还有个HttpContext.Item,通过它你可以在HttpContext的生存周期内提前存储一些临时的数据,方便随时使用。
2:生存周期:从客户端用户点击并产生了一个向服务器发送请求开始---服务器处理完请求并生成返回到客户端为止。针对每个不同用户的请求,服务器都会创建一个新的HttpContext实例直到请求结束,服务器销毁这个实例。
3:当我们创建一个一般处理程序Handler.ashx时,我们可以在文件中看到这一句 public void ProcessRequest (HttpContext context);
4:可以通过HttpContext.Current获得当前的上下文httpContext的内容;这样可以在多处方便获取我们想要的数据;
二:HttpContext.Item的运用
在文中第一点我们有提到HttpContext.Item可以通过它来存储一些临时的数据;我们将通过一个实例来演示它的实现,以及在此过程中应该注意什么;还针对ASP.NET常见的两种跳转进行比较(分别为:Server.Transfer和Response.Redirect);
1:首先我们新建一个页面对HttpContext.Item它进行写入数据(此处不一样的是当写完值后不同的页面跳转方式):

protected void Btn_Transfer_Click(object sender, EventArgs e)
{
List<String> list = new List<string>();
list.Add("踏浪帅");
list.Add("www.cnblogs.com/wujy");
HttpContext.Current.Items["TransferName"] = list;
Server.Transfer("Index.aspx");
}
protected void Btn_Redirect_Click(object sender, EventArgs e)
{
List<String> list = new List<string>();
list.Add("踏浪帅");
list.Add("www.cnblogs.com/wujy");
HttpContext.Current.Items["RedirectName"] = list;
Response.Redirect("Index.aspx");
}

接着我们再新建一个Index.aspx页面接收此页面的值:

public partial class Index : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
List<string> list = HttpContext.Current.Items["TransferName"] as List<string>;
if (list != null)
{
foreach (string item in list)
{
Response.Write(string.Format("Transfer输出的值为:{0} <br/>", item));
}
} List<string> ResList = HttpContext.Current.Items["RedirectName"] as List<string>;
if (ResList != null)
{
foreach (string item in ResList)
{
Response.Write(string.Format("Redirect输出的值为:{0}", item));
}
}
else
{
Response.Write("Response.Redirect传过来为空!");
}
}
}
}

现在我们看一下运行的效果:
Server.Transfer方式 | Response.Redirect方式 |
![]() |
![]() |
从上面运行的结果我们不然发现使用Server.Transfer方式我们在接收页面成功获得我们写入的数据;而Response.Redirect方式却导致数据的丢失;出现这个问题的原因是Server.Transfer是在服务器直接操作的的和调用的地方属于同一次http请求,此处如果使用Response.Redirect是从客户端重新发起了一次Http请求;而正如我们前面所说的HttpContext的生存周期只能在一个http请求才不会被服务器销毁这个实例;
针对上面Server.Transfer是在服务器直接操作和调用的我们其实可以看一下我们跳转时URL地址的变化就知道了,URL它是没有变化的;
Server.Transfer | |
![]() |
![]() |
而Response.Redirect跳转后的URL地址是发生变化(它是从客户端再去做起一次请求访问到index.aspx页面):
注意:一般的情况下尽可能用Server.Transfer方法(前提是服务器是IIS),Server.Transfer方法更快速,而且因为只在服务器上执行,所以可以和任何浏览器兼容。
Response.Redirect其实上是当服务器碰到这条语句时发送一条指令(包含新的地址)给浏览器,然后让浏览器去发送http请求,请求Response.Redirect后面的那个新的http地址,流程如下:浏览器文件请求->服务器执行->遇到response.redirect语句->服务器发送Server.Transfer后面的地址给客户机端的浏览器->浏览器请求执行新的地址(服务器返回的Response.Redirect后面的地址)这就是一个小小的Response.Redirect的全部过程,
Server.Transfer语句当接受地址后是直接转向后面的地址,流程如下:浏览器文件请求->服务器执行->遇到Server.Transfer语句->服务器转向新的文件。
2:截获Http请求并作特殊处理
主要是用在Application_BeginRequest方法里。Application_BeginRequest方法在global.asax.cs里定义。你可以在Http请求刚刚开始的时候,截获他,做一些特殊的处理。这里你不能用Session来做,因为此时Session对象还没有被建立。 利用Context.Item集合,你可以在Http请求的整个生命期,通过HttpModules, HttpHandlers, Webforms, and Application 事件。
三:HttpContet对象之Request
1:Request理论知识:
ASP.NET Request 封装了客户端请求信息,是从客户端得到数据(从浏览器获取数据);常用的三种取得数据的方法是:Request.Form、Request.QueryString、Request其第三种是前两种的一个缩写,可以取代前两种情况。而前两种主要对应的Form提交时的两种不同的提交方法:分别是Post方法和Get方法。
2:新建一个页面用来显示Requesst一些常见属性信息的内容;

public partial class RequestPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
StringBuilder strBuider = new StringBuilder();
strBuider.Append("客户端主机的IP地址:" + HttpContext.Current.Request.UserHostAddress + "</br>");
strBuider.Append("客户端浏览器版本:" + Request.UserAgent + "</br>");
strBuider.Append("当前由哪个页面URL跳转过来:" + Request.UrlReferrer+ "</br>");
strBuider.Append("当前要求的URL:" + Request.Url + "</br>");
strBuider.Append("当前要求的URL绝对地址:" + Request.Url.AbsolutePath + "</br>");
strBuider.Append("当前要求的URL绝对URI:" + Request.Url.AbsoluteUri + "</br>");
strBuider.Append("当前要求的URL主机名跟端口:" + Request.Url.Authority + "</br>");
strBuider.Append("当前要求的URL实例主机的一部分:" + Request.Url.Host + "</br>");
strBuider.Append("当前要求的URL端口:" + Request.Url.Port + "</br>");
strBuider.Append("当前要求的URL的内容第一个段:" + Request.Url.Segments[0] + "</br>");
strBuider.Append("当前要求的URL的内容第二个段:" + Request.Url.Segments[1] + "</br>");
strBuider.Append("当前要求的URL的内容第三个段:" + Request.Url.Segments[2] + "</br>");
strBuider.Append("浏览器地址栏后的参数"+Request.QueryString + "</br>");
strBuider.Append("当前网页在服务器端的实际路径:" + Request.PhysicalPath + "</br>");
strBuider.Append("当前文件的物理地址:" + Request.PhysicalApplicationPath + "</br>");
strBuider.Append("当前网页的相对地址:" + Request.Path + "<br/>");
strBuider.Append("当前页面的URL:" + Request.RawUrl + "<br/>");
strBuider.Append("客户端上传的文件(个数):" + Request.Files.Count + "<br/>");
strBuider.Append("当前执行网页的相对地址:" + Request.FilePath + "<br/>");
strBuider.Append("客户端浏览器的信息:" + Request.Browser + "<br/>");
strBuider.Append("当前运行程序的服务器端虚拟目录:" + Request.ApplicationPath + "<br/>");
strBuider.Append("客户端浏览器的字符设置:" + Request.ContentEncoding + "<br/>");
Response.Write(strBuider.ToString());
}
}
}

运行结果显示(获得客户端浏览器以及一些相关的信息):
3:针对表单的提交方式如上面所说的有两种方式(POST以及GET);接下来我们则通过几段代码来演示有关表单提交的两种方式以及一些应该注意的事项;
(1)表单Get方式提交:
相信在我们平常的开发过程中使用Get方式进行提交的表单是最经常碰到的,它也是最简单的一种方式,主要注意在使用Get提交时URL对数据的大小是有限止;还经常碰到的情况是对URL传参时一些特殊符号的处理问题;接下来我们将通过小实例来演示两种对URL传参处理特殊符号方式;
首先是使用ASP.NET代码的Server对象对要传参进行一个编码的处理(Server.UrlEncode(string s));

string UserName = "踏浪帅¥%—*@";
Server.Transfer("GetPageResult.aspx?UserName="+Server.UrlEncode(UserName)+"&UserID=001");

接上时再对收到的参数进行一个解码(Get方式使用Request.QueryString[Name]和Request[Name]进行接收参数;解码Server.UrlDecode(string s)):

string UserName = Request.QueryString["UserName"];
Response.Write("第一个传参值:" + Server.UrlDecode(UserName) + "</br>"); string UserID = Request["UserID"];
Response.Write("第二个传参值:" + UserID + "</br>");

查后运行结果:
另外一种方式是使用JS进行传参,此得使用到的JS中的escape();而它接收解码跟上面使用ASP.NET代码是一样;

<head runat="server">
<title></title>
<script type="text/javascript" src="jquery-1.4.2.min.js"></script>
<script language="javascript" type="text/javascript">
$(function () {
$("#Btn_Get").bind('click',GetResult);
})
function GetResult() {
var UserName = "踏浪帅¥%—*@";
location.href = "GetPageResult.aspx?UserID=002&UserName="+escape(UserName);
}
</script>
</head>
<body>
<form id="form1" runat="server">
<input id="Btn_Get" type="button" value="HTML中的JS传值" />
</form>
</body>
</html>

运行结果:
(2)Post方式提交:
Post方式的表单提交比Get方式更加安全,它不会把我们要传递的参数显示在URL里,还有它可以针对一些要传递内容比较大(比如文件上传等);先通过一段代码来演示此方式的提交;后面我们再顺便介绍Post方式提交时常遇到的一个错误;
首先同样新建一个页面(我们把表单通过Post提交到PostPageResult.aspx页面,method就是用来设置我们使用Post方式):

<head runat="server">
<title></title>
</head>
<body>
<form id="form1" action="PostPageResult.aspx" method="post">
<div>
Cnblogs账号:<input type="text" size="20" name="Name" value="踏浪帅"/>
<br />
<input type="submit" value="提交" />
</div>
</form>
</body>
</html>

接收页在的代码(Post可以采用Request.Form[Name]和Request[Name]进行接收参数,若是提交过来有上传文件可以使用 Request.Files进行获得跟处理):

Response.Write("Post方式采用Request.Form[Name]:" + Request.Form["name"] + "</br>"); Response.Write("Post方式采用Request[Name]:" + Request["name"]);

在使用Post经常会无意中碰到一个错误:
解决方式(当runat="server"的表单通过修改action提交数据到其它页面时,也会引发这个问题;把表单中的runat="server"去掉);
网上还有其它解决的办法:
a:添加enableEventValidation="false" enableViewStateMac="false"或在webconfig中添加<pages enableEventValidation="false" enableViewStateMac="false" />
b:不使用跨页面提交,提交到本页后在page.load中redirect.
c:使用 <asp:Button runat="server" PostBackUrl="~/Register/DoRegister.aspx" Text="提交" />
4:平常通过Request获得客户端的Cookies值;

HttpCookie cookie = HttpContext.Current.Request.Cookies["HttpDemo"];

四:HttpContet对象之Response
1:Response理论知识:
Response代表了服务器响应对象。每次客户端发出一个请求的时候,服务器就会用一个响应对象来处理这个请求,处理完这个请求之后,服务器就会销毁这个相应对象,以便继续接受其它客服端请求。
2:Response主要用来一些输出的运用;只要简单记住其几个重要的属性以及方法就差不多了;

protected void Button1_Click(object sender, EventArgs e)
{
Response.Redirect("Default.aspx");
}
protected void Button2_Click(object sender, EventArgs e)
{
string FileContent = Server.MapPath(".") + "\\TextFile.txt";
Response.WriteFile(FileContent);
}
protected void Button3_Click(object sender, EventArgs e)
{
Response.Write("这是第一句");
Response.End(); //停止运行,不再执行任何语句
Response.Write("这是第二句");
Response.Clear(); //清空缓冲区中的所有内容输出
Response.Close();//关闭当前服务器到客户端的连接
}

Response还有个我们经常使用到的地方是针对下载文件时输出时的内容:

public static void ResponseStream(Stream stream, string fileName)
{
if (stream.Length > 0)
{
try
{
long fileSize = stream.Length;
System.Web.HttpContext.Current.Response.ContentType = "application/octet-stream"; //控制送出的文件类型
System.Web.HttpContext.Current.Response.AppendHeader("Content-Disposition", " attachment;filename=" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8));//向响应输出流增加HTTP头信息
System.Web.HttpContext.Current.Response.AddHeader("Content-Length", fileSize.ToString());
byte[] fileBuffer = new byte[fileSize];
stream.Read(fileBuffer, 0, (int)fileSize);
stream.Close();
System.Web.HttpContext.Current.Response.BinaryWrite(fileBuffer);
System.Web.HttpContext.Current.Response.End(); }
catch (Exception ex)
{
throw;
}
}
}

针对Response.ContentType是表示送出的文件类型(下在我们列出一些比较常见的类型,其它类型可以到网络上进行搜索):

'doc' => 'application/msword',
'bin' => 'application/octet-stream',
'exe' => 'application/octet-stream',
'dll' => 'application/octet-stream',
'pdf' => 'application/pdf',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'zip' => 'application/zip',
'mp3' => 'audio/mpeg',
'wav' => 'audio/x-wav',
'pdb' => 'chemical/x-pdb',
'bmp' => 'image/bmp',
'gif' => 'image/gif',
'ief' => 'image/ief',
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'jpe' => 'image/jpeg',
'png' => 'image/png',

另外当我们在写入Cookies时通过Response的Set-Cookie向客户端设置的Cookie;

HttpCookie ck = new HttpCookie("HttpDemo");
ck.Values.Add("UserID", "踏浪帅");
Response.SetCookie(ck);
Response.Redirect("Default.aspx");

五:HttpContet对象之Sever
1:Server理论知识:
Server对象是用于获取服务器的相关信息的对象
2:对于Server对象我们也是简单的了解一些它的属性内容;

StringBuilder strBuilder = new StringBuilder();
strBuilder.Append("获取计算机名:"+HttpContext.Current.Server.MachineName+"</br>");
strBuilder.Append("获取指定相对路径在服务器上的物理路径:" + Server.MapPath(".") + "</br>");
strBuilder.Append("在服务器里实现跳转;不用再返回客户端;Server.Transfer(string Path)</br>");
strBuilder.Append("先执行路径所代表的URL,然后执行完之后再执行本页:Server.Execute(string Path)</br>");
strBuilder.Append("对特殊字符串的处理:Server.HtmlDecode(string s)与Server.HtmlEncode(string s)</br>");
strBuilder.Append("对URL路径字符串进行编解码:Server.UrlDecode(string s)与Server.UrlEncode(string s)</br>");
Response.Write(strBuilder.ToString());

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】按钮。 因为,我的写作热情也离不开您的肯定支持。
感谢您的阅读,[源代码下载]
Lazy<T>的更多相关文章
- 代码的坏味道(15)——冗余类(Lazy Class)
坏味道--冗余类(Lazy Class) 特征 理解和维护类总是费时费力的.如果一个类不值得你花费精力,它就应该被删除. 问题原因 也许一个类的初始设计是一个功能完全的类,然而随着代码的变迁,变得没什 ...
- Mach-O 的动态链接(Lazy Bind 机制)
➠更多技术干货请戳:听云博客 动态链接 要解决空间浪费和更新困难这两个问题最简单的方法就是把程序的模块相互分割开来,形成独立的文件,而不再将它们静态的链接在一起.简单地讲,就是不对那些组成程序的目标文 ...
- Lazy Load, 延迟加载图片的 jQuery 插件.
Lazy Load 是一个用 JavaScript 编写的 jQuery 插件. 它可以延迟加载长页面中的图片. 在浏览器可视区域外的图片不会被载入, 直到用户将页面滚动到它们所在的位置. 这与图片预 ...
- Hibernate之lazy延迟加载
一.延迟加载的概念 当Hibernate从数据库中加载某个对象时,不加载关联的对象,而只是生成了代理对象,获取使用session中的load的方法(在没有改变lazy属性为false的情况下)获取到的 ...
- nhibernate 中 lazy="no-proxy" 时的问题
在 nhibernate,如果将实体的一个关联属性配置为 lazy="no-proxy",那么,从其他属性计算出来的属性不能正确更新.例如,将以下代码中 Foo.Bar 配置为 l ...
- hibernate 中的 lazy=”proxy” 和 lazy=”no-proxy” 的区别
网上找到个描述的很精妙的例子 Child <- many-to-one ->Parent class Child { private ...
- jQuery延迟加载插件(Lazy Load)详解
最 新版本的Lazy Load并不能替代你的网页.即便你使用JavaScript移除了图片的src属性,有些现代的浏览器仍然会加载图片.现在你必须修改你的html代 码,使用占位图片作为img标签的s ...
- RabbitMQ Lazy Queue 延迟加载
Lazy Queue 在著名的单例设计模式中就有懒汉式的实现方式,也就是只有在你需要的时候我才去加载. 这让博主想到了以前上学的时候,每到了假期的假期作业,在假期的时候是从来不做的.只有在快开学老师要 ...
- CURL命令报错:dyld: lazy symbol binding failed: Symbol not found: _SSL_load_error_strings解决办法
Mac OS X 10.11.6, curl 命令报错,错误如下: dyld: lazy symbol binding failed: Symbol not found: _SSL_load_erro ...
- 004-For与Function进阶实战、Lazy的使用
004-For与Function进阶实战.Lazy的使用 For进阶 非常常见的形式 可以加入条件表达式进行数据过滤 Function进阶 函数是有值的(默认的话为Unit),所以可以直接将结果赋值给 ...
随机推荐
- 解决VS 于 致命错误 RC1015: 无法打开包含文件 'afxres.h' 问题
在试验VS2010当一个问题困扰了我,它是开放的c++项目达产后,rc的dialog入口.您不能拖动控制,让我疯狂... 而最有发言权的是在线Directions问题. .题明显不是这个问题. 于是我 ...
- redis修改的源代码zincrby,hincrby命令
在项目中大量使用zincrby命令.究其原因是统计一些统计指标的日志值,和需要返回到顺序topn. 通常情况下,.调用一次的指示器zincrby(zincrby default:type 1 type ...
- vim插件管理器vundle
安装: git clone http://github.com/gmarik/vundle.git ~/.vim/bundle/vundle set nocompatible " be i ...
- The Swift Programming Language-官方教程精译Swift(6)控制流--Control Flow
Swift提供了类似C语言的流程控制结构,包括可以多次执行任务的for和while循环,基于特定条件选择执行不同代码分支的if和switch语句,还有控制流程跳转到其他代码的break和continu ...
- android 使用asm.jar将android手机屏幕投射到电脑
使用asm.jar将Android手机到电脑屏幕投影 有时候可能须要将手机上的一些操作投影出来,比方一些App Demo的展示等.事实上,有专门的硬件设备能干这件事儿.但不是必需专门为展示个Demo去 ...
- Hadoop-2.4.1完全分布式环境搭建
Hadoop-2.4.1完全分布式环境搭建 Hadoop-2.4.1完全分布式环境搭建 一.配置步骤如下: 主机环境搭建,这里是使用了5台虚拟机,在ubuntu 13系统上进行搭建hadoop ...
- MVC页面声命周期
MVC页面声命周期 ASP.Net请求处理机制初步探索之旅 - Part 4 WebForm页面生命周期 开篇:上一篇我们了解了所谓的请求处理管道,在众多的事件中微软开放了19个重要的事件给我们, ...
- POJ3233(矩阵二分再二分)
题目非常有简单: Description Given a n × n matrix A and a positive integer k, find the sum S = A + A2 + A3 + ...
- 快速构建Windows 8风格应用28-临时应用数据
原文:快速构建Windows 8风格应用28-临时应用数据 本篇博文主要介绍临时应用数据概览.如何构建临时应用数据. 一.临时应用数据概览 临时应用数据相当于网页中缓存,这些数据文件是不能够漫游的,并 ...
- leetcode 第42题 Multiply Strings
题目:Given two numbers represented as strings, return multiplication of the numbers as a string. Note: ...