Lazy<T>实现和.NET泛型

15

我正在寻找实现延迟初始化的方法,并发现了包含在.NET 4中的Lazy<T>

我想为.NET 3.5自己实现一个Lazy<T>(使用更简单的多线程策略),但遇到了以下问题:

Lazy<T>基本上有两种类型的构造函数:

class Lazy<T> {

    public Lazy(){...} // ctor #1

它使用 T 的默认构造函数来创建 T 的实例,而


    public Lazy(Func<T> func){...} // ctor #2

这里涉及到了一个让调用者决定T实例创建方式的问题。

现在问题来了:

如果我想对第一个构造函数进行编译时检查,我需要添加一个限制条件。

class Lazy<T> where T: new() {...}

在类级别上设置限制,这样我就可以使用new T()来创建一个实例;但是这个限制对于第二个构造函数来说并不是必要的,更糟糕的是,它还限制了我可以使用的类型(只能使用具有默认构造函数的类型)。
如果我想要使用任何类型来使用第二个构造函数,我将不设置任何限制,并且在第一个构造函数中将使用反射确保T确实具有默认构造函数。然而,这种方法将缺乏编译时检查,只有在使用错误的类型时才会抛出运行时异常。
我的问题是:我能否兼顾两全?
理想情况下,我希望在使用构造函数#1时获得编译时检查,但同时也能够使用构造函数#2来处理没有默认构造函数的类型。
微软的实现是如何做到的?(我没有立即访问.NET 4源或dlls)。
编辑:(在“反编译”MS程序集后)
我检查了参考实现,它没有进行编译时检查。
当然,在“默认构造函数”情况下,它使用反射,如果出现问题,则会伴随运行时异常。

3
您可以使用Reflector查看.NET 4的实现。 - Thomas Levesque
@Thomas 我喜欢引用自己。这让我听起来很重要:“我无法轻松访问.NET 4的源代码或dll”...或者至少在我提出问题时是这样。在此期间,由于Lucas的提示,我获得了程序集,并更新了我的发现问题。 - Cristian Diaconescu
好的,我漏掉了“或 DLL 文件”的部分... 抱歉。 - Thomas Levesque
5个回答

12

我认为该内置实现只是出于简便起见使用了Activator.CreateInstance<T>。我能想到最清晰的欺骗方式是使用单独的工厂:

// non-generic factory class with generic methods
public static class Lazy {
    public static Lazy<T> Create<T>() where T : new() {
        return Create<T>(() => new T());
    }
    public static Lazy<T> Create<T>(Func<T> ctor) { ... }
}
public class Lazy<T> { ... }

一个人的作弊是另一个人的设计模式。 :) - LBushkin

7
你可以使用静态工厂方法代替构造函数的重载:
public class Lazy<T>
{
    public Lazy( Func<T> f ) { /*...*/ }

   public static Lazy<R> Default<R>() where R : T, new()
   {
       return new Lazy<R>( () => new R() );
   }
}

现在,这破坏了与.NET 4.0版本的Lazy<T>的兼容性(在某种程度上),但确实实现了两种用法的编译时安全。

你可以通过将Lazy<T>的构造函数设置为protected internal,然后提供一个静态工厂类来创建实例,从而使其更加简洁:

public static class Lazy {
    static Lazy<T> Create<T>( Func<T> ) { ... }
    static Lazy<T> Create<T>( ) where T : new() { ... }
}

1
抱歉,我很无知,请问我如何从Lazy<T>中调用受保护的构造函数? - Cristian Diaconescu
我本意是写 internal protected - 我会修改的。 - LBushkin

2
为什么不直接下载并安装Parallel Extensions,然后在3.5中使用Lazy<T>直接链接。请注意保留HTML标记。

1
因为使用任何第三方库都需要获得公司的批准,这真是一件痛苦的事情。 :( - Cristian Diaconescu
哦...但现在我可以钻研原始实现了 :) - Cristian Diaconescu

0

像这样的代码应该适合您。在升级到4.0之前,我们在本地代码库中使用了一年左右。

public class Lazy<T> {
  private Func<T> func;
  private T result;
  private bool hasValue;
  public Lazy(Func<T> func) {
    this.func = func;
    this.hasValue = false;
  }
  public T Value {
    get {
      if (!this.hasValue) {
        this.result = this.func();
        this.hasValue = true;
      }
      return this.result;
    }
  }
}

0
我的问题是:我能同时得到最好的两个世界吗?
不行。
基本上你没有编译时可检查的约束。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接