C#基础知识回顾---你不知道的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提供的关注度实在不少,我们眼花缭乱了。

 郑重声明本文非原创……

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏dotnet core相关

WCF入门(12)

  上次写是小半年前的事情了,还在原来的公司,还在原来的项目,同时认识了不少人。外包公司总是有些不适应的地方,总在很闲和很忙之间徘徊。凌晨2点被客户电话叫醒,只...

491
来自专栏林德熙的博客

Roslyn 静态分析

首先创建一个项目,项目使用.net Framework 4.6.2 ,控制台项目。然后需要安装一些需要的库

181
来自专栏Spring相关

第3章—高级装配—bean的作用域

单例是默认的作用域,但是正如之前所描述,对于易变的类型,这并不合适.如果选择其他作用域,要使用@Scope注解,他可以和@Component或@Bean一起使用...

552
来自专栏Java成神之路

Spring_总结_04_高级配置(四)_bean的作用域

Spring应用上下文中所有的bean默认都是单例的。也就是说,不管一个bean被注入到其他bean多少次,每次注入的都是同一个实例。

182
来自专栏hbbliyong

[译]WebAPI下的如何实现参数绑定

本文将概述在WebAPI方式下将如何将参数绑定到一个action方法,包括参数是如何被读取,一系列规则决定特定环境采用的那种绑定方式,文章最后将给出一些实际的例...

3496
来自专栏智能大石头

GridView绑定小技

1,使用表达式。如下,缺货的产品用红色,别的绿色。 ? ? 实际上,就这个表达式: (Int32)Eval("Num")<(Int32)Eval("MinNum...

1888
来自专栏全栈

SpringMVC+GSON 对象序列化--日期格式的处理

1052
来自专栏Google Dart

Dart 服务端开发 shelf_bind 包

shelf_bind倾向于约定优于配置,因此您可以编写必要的最小代码,但仍然可以根据需要覆盖默认值。

632
来自专栏猛牛哥的博客

JMail接收发送邮件使用参考

853
来自专栏爱撒谎的男孩

地址管理之省市区三级联动菜单

2843

扫描关注云+社区