我懒得加载我的所有成员。我已经这样做了一段时间,只是采取懒惰的负载,在面值是一件好事。
让我们说我们有
public class SomeClass
{
public int anInt;
public SomeReferenceType member1;
public SomeClass()
{
//initialize members in constructor when needed (lazy load)
anInt = new int();
member1 = new SomeReferenceType();
}
}
以这种方式做事有什么不利之处吗?这是一个适当的延迟加载模式吗?延迟加载值类型是否有意义(现代RAM甚至是否重要)?
public class SomeClass
{
public int anInt;
public SomeReferenceType member1 = new SomeReferenceType();
public SomeClass()
{
}
}
首先,初始化构造函数内的成员不是延迟加载。
延迟加载是在第一次请求时初始化成员。 .NET中的一个简单示例(带有一些双重检查锁定,因此我们没有线程问题):
public class SomeClass
{
private object _lockObj = new object();
private SomeReferenceType _someProperty;
public SomeReferenceType SomeProperty
{
get
{
if(_someProperty== null)
{
lock(_lockObj)
{
if(_someProperty== null)
{
_someProperty= new SomeReferenceType();
}
}
}
return _someProperty;
}
set { _someProperty = value; }
}
}
幸运的是,如果你使用的是.NET 4,你现在可以使用Lazy<T>
Class为你处理问题并使事情变得更容易。
其次,延迟加载是一个好主意,当你有许多成员加载成本高,并且你确定你将使用所有这些值。该成本将导致类型实例化的速度不一定很慢。
为了延迟加载而延迟加载会给代码增加不必要的复杂性,如果做得不正确(例如处理线程时)可能会导致问题。
这不是一个懒惰的负载。这是在建设初期。通常我们在延迟加载中的意思是在第一次引用时构造项。
private string _someField;
public string SomeField
{
get
{
// we'd also want to do synchronization if multi-threading.
if (_someField == null)
{
_someField = new String('-', 1000000);
}
return _someField;
}
}
它曾经是懒惰加载的典型方法之一是检查,锁定,检查,如果它已经创建,你不会锁定,但由于两个项目可能通过检查并等待锁定,你检查再次锁定:
public class SomeClass
{
private string _someField;
private readonly object _lazyLock = new object();
public string SomeField
{
get
{
// we'd also want to do synchronization if multi-threading.
if (_someField == null)
{
lock (_lazyLock)
{
if (_someField == null)
{
_someField = new String('-', 1000000);
}
}
}
return _someField;
}
}
}
有多种方法可以做到这一点,实际上在.NET 4.0中,有一种Lazy<T>
类型可以帮助您轻松地进行线程安全的延迟加载。
public class SomeClass
{
private readonly Lazy<string> _someField = new Lazy<string>(() => new string('-', 10000000), true);
private readonly object _lazyLock = new object();
public string SomeField
{
get
{
return _someField.Value;
}
}
}
至于为什么,如果您创建的对象往往是昂贵的(内存或时间)并且无法保证您将需要它,通常延迟加载是一个很好的方案。如果你有理由确定它将永远使用,那么你应该直接构造它。
从我看到的代码,你没有做懒惰的负载。您正在构造函数中初始化成员,这些成员总是会发生并在实例的生命周期中很快发生。
因此,我想知道,什么是非懒加载?
延迟加载通常是在您访问它时仅初始化某些内容时。
这里有一个例子,使用.NET 4.0 Lazy类,它可以帮助你做到这一点,延迟加载:
public class Foo
{
private Lazy<int> _value = new Lazy<int>(() => 3);
public int Value { get { return _value.Value; } }
}
关于线程安全 - 你可以传递第二个参数LazyThreadSafetyMode
,它知道指定线程安全的两种方法:一种初始化方法的执行可能会多次发生,但是所有线程都获得首先创建的值,或者其中一个执行也受到保护,不会被多次运行。
这不是延迟加载。
延迟加载意味着您只需在实际访问时加载该值(在初始化程序中不会发生)
延迟加载是这样的:
private SomeClass _someRef = null;
public SomeClass SomeRef
{
get
{
if(_someRef == null)
{
//initialisation just in case of access
_someRef = new SomeClass();
}
return _someRef;
}
}
int的真正延迟加载属性可能如下所示:
private int? _heavyLoadedInt;
public int HeavyLoading
{
get
{
if (_heavyLoadedInt == null)
_heavyLoadedInt = DoHeavyLoading();
return _heavyLoadedInt.Value;
}
}
现在,如果你看一下,你会发现这里有一些开销:你必须将值存储在可空(额外的内存)中;在每次访问时检查null
,并在每次访问时从nullable中检索值。
如果你的整数真的需要一些非常繁重的计算,那么这个结构是有意义的。但new int()
不是一个繁重的计算,它只是返回0
。开销很小,但是如果你将这个开销添加到更简单的操作(即读取整数),那就没有意义了。
当对象创建的成本非常高并且对象的使用很少时,延迟加载是必不可少的。所以,这是值得实现延迟加载的场景。延迟加载的基本思想是在需要时加载对象/数据
延迟加载是一个概念,我们将对象的加载延迟到我们需要它的位置。简单地说,按需加载对象而不是不必要地加载对象。
例如,考虑下面的示例,其中我们有一个简单的Customer类,并且此Customer类中包含许多Order对象。仔细查看Customer类的构造函数。创建Customer对象时,它还会在此时加载Order对象。因此,即使我们需要或不需要Order对象,它仍然会被加载。
List<int> number = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var result = number.Where(x => x % 2 == 0);
number.Add(20);
//foreach (int item in result)
//{
// Console.WriteLine("loop1:" + item);
//}
foreach (int item in result)
{
if (item == 4)
break;
Console.WriteLine("loop2:" + item);
}
number.Add(40);
foreach (int item in result)
{
Console.WriteLine("loop3:"+item);
}
Console.ReadLine();
取消注释第一个循环,看看差异。非常使用示例来表示deffred执行和延迟加载。