简介

从net 4.0开始,C#开始支持延迟初始化,通过Lazy关键字,我们可以声明某个对象为仅仅当第一次使用的时候,再初始化,如果一直没有调用,那就不初始化,省去了一部分不必要的开销,提升了效率,同时Lazy是天生线程安全的

应用场景

  • 对象创建成本高且程序可能不会使用它。

​ 例如,假定内存中有具有 Orders 属性的 Customer 对象,该对象包含大量 Order 对象,初始化这些对象需要数据库连接。 如果用户 永远不要求显示 Orders 或在计算中使用该数据,则无需使用系统内存或计算周期来创建它。 通过使用 Lazy 来声明 Orders 对象用 于迟缓初始化,可以避免在不使用该对象时浪费系统资源。

  • 对象创建成本高,且希望将其创建推迟到其他高成本操作完成后。

​ 例如,假定程序在启动时加载多个对象实例,但是只需立即加载其中一部分。 可以通过推迟初始化不需要的对象,直到创建所需对 象,提升程序的启动性能。

基本用法

方式一:使用默认的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;

namespace LazyUsage
{
class LazyDemo
{
static void Main()
{
Lazy<Data> lazyData = new Lazy<Data>();
Console.WriteLine("Main->is lazyData Initialized? value = " + lazyData.IsValueCreated);
lazyData.Value.Print();//此处访问时才会将Data真正的初始化
Console.WriteLine("Main->is lazyData Initialized? value = " + lazyData.IsValueCreated);

Console.ReadKey();
}
}

class Data
{
public Data()
{
Console.WriteLine("Data::.ctor->Initialized");
}

public void Print()
{
Console.WriteLine("Data::Print->println");
}
}
}

输出结果

1
2
3
4
Main->is lazyData Initialized? value = False
Data::.ctor->Initialized
Data::Print->println
Main->is lazyData Initialized? value = True

方式二:使用Func委托返回指定的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using System;

namespace LazyUsage
{
class LazyDemo
{
static void Main()
{
//指定委托来初始化Data
Lazy<Data> lazyData = new Lazy<Data>(
() =>
{
Console.WriteLine("Main->lazyData will be Initialized!");
return new Data("Test");
});
Console.WriteLine("Main->is lazyData Initialized? value = " + lazyData.IsValueCreated);
lazyData.Value.Print();
Console.WriteLine("Main->is lazyData Initialized? value = " + lazyData.IsValueCreated);


Console.ReadKey();
}
}

class Data
{
public string Name { get; private set; }

public Data(string name)
{
Name = name;
Console.WriteLine("Data::.ctor->Initialized,name = "+name);
}

public void Print()
{
Console.WriteLine("Data::Print->name = " + Name);
}
}
}

输出结果

1
2
3
4
5
Main->is lazyData Initialized? value = False
Main->lazyData will be Initialized!
Data::.ctor->Initialized,name = Test
Data::Print->name = Test
Main->is lazyData Initialized? value = True

关于Lazy.Value的使用

Lazy对象创建后,并不会立即创建对应的对象,只有在变量的Value属性被首次访问时才会真正的创建,同时会将其缓存到Value中,以便将来访问。

Value属性是只读的,也就意味着如果Value存储了引用类型,将无法为其分配新对象,只可以更改此对象公共的属性或者字段等,如果Value存储的是值类型,那么就不能修改其值了,只能通过再次调用变量的函数使用新的参数来创建新的变量

拓展

实现延迟属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Customer
{
private Lazy<Orders> _orders;

public string CustomerID {get; private set;}

public Customer(string id)
{
CustomerID = id;
_orders = new Lazy<Orders>(() =>
{
return new Orders(this.CustomerID);
});
}

public Orders MyOrders
{
get
{
return _orders.Value;
}
}
}
  • 在实际使用中,Lazy类多声明为private甚至还要readonly
  • Value的属性是只读的,所以示例中只提供了Get的访问器,并未提供Set的访问器。
  • 如果需要支持读取与写入属性的话,则Set访问器必须创建一个新的Lazy对象,同时必须编写自己的线程安全代码才能执行此操作