我定义了一个通用类“Lazy<T>”,用于延迟评估和缓存委托(delegate)Func<T>的结果。

我还定义了两个隐式强制转换运算符,因此我可以从Lazy<T>创建一个Func<T>,并且可以将Lazy<T>分配给T(获取ValueLazy<T>)

这个想法是,您可以绕过Lazy<T>代替T的实例,但是在将值分配给T的实际实例之前,不进行计算/获取值的工作。

// class Lazy<T>
// Encapsulates a value which can be retrieved when first accessed,
// and is then cached.
class Lazy<T>
{
  private Func<T> _getter;
  private T _cached;
  private bool _isCached;

  // Get/set the getter delegate
  // that 'calculates' the value.
  public Func<T> Getter
  {
    get
    {
      return _getter;
    }
    set
    {
      _getter = value;
      _cached = default(T);
      _isCached = false;
    }
  }

  // Get/set the value.
  public T Value
  {
    get
    {
      if (!_isCached)
      {
        _cached = Getter();
        _isCached = true;
        _getter = null;
      }
      return _cached;
    }
    set
    {
      _cached = value;
      _isCached = true;
      _getter = null;
    }
  }

  // Implicit casts:

  // Create a T from a Lazy<T>
  public static implicit operator T(Lazy<T> lazy)
  {
    return lazy.Value;
  }

  // Create a Lazy<T> from a Func<T>
  public static implicit operator Lazy<T>(Func<T> getter)
  {
    return new Lazy<T> {Getter = getter};
  }
}

但是在某些情况下,该类无法正常工作,在下面的测试应用程序中突出显示了该类:
class Program
{
  static void Main()
  {
    // This works okay (1)
    TestLazy(() => MakeStringList());

    // This also works (2)
    Lazy<string> lazyString = new Func<string>(() => "xyz");
    string s = lazyString;

    //This doesn't compile (3)
    //
    Lazy<IList<string>> lazyStrings = new Func<IList<string>>(MakeStringList);
    IList<string> strings = lazyStrings; //ERROR
  }


  static void TestLazy<T>(Func<T> getter)
  {
    Lazy<T> lazy = getter;
    T nonLazy = lazy;
  }

  private static IList<string> MakeStringList()
  {
    return new List<string> { new string('-', 10) };
  }
}

在标有//ERROR的行上,出现编译错误:

错误CS0266:无法将Lazy<System.Collections.Generic.IList<string>>类型隐式转换为System.Collections.Generic.IList<string>。存在显式转换(您是否缺少类型转换?)

此错误令人困惑,因为确实存在从源到目标类型的隐式转换。
而且,从表面上看,代码块(3)与(1)的作用相同
而且,它与(2)的不同之处仅在于专门用于懒惰的类型。

谁能告诉我这是怎么回事?

最佳答案

问题在于,您试图隐式转换为IList<T>,并且IList<T>不包含IList<T>(即使它们是同一类型)-仅包含对非接口(interface)类型的转换。根据C#3.0规范的6.4.3节:



在6.4.4节中,谈论用户定义的转换,步骤之一是(强调我的):


IList<T>不包含IList<T>,因此此步骤失败。

但是,在其他情况下,编译器将执行“链接”的隐式转换-因此,如果您实际上有一个Lazy<List<T>>,则可以编写:

object strings = lazyStrings;

之所以可行,是因为List<T>包含了object(因为它们都是类,并且存在从List<T>object的标准隐式转换)。

现在,为什么会这样,我怀疑是要停止奇怪的情况,在这种情况下您希望进行引用转换,但实际上会得到隐式转换。假设我们有:
class ListLazy : Lazy<IList<string>>, IList<string>
{
    // Stuff
}
...
Lazy<IList<string>> x = new ListLazy();
IList<string> list = x;

应该使用哪种转换?从实际类型到IList<string>有一个隐式引用转换...但是编译器不知道,因为表达式的类型是Lazy<IList<string>>。基本上,接口(interface)很尴尬,因为它们可以稍后显示在类型层次结构中,而对于类,如果您明白我的意思,就总是知道您在哪里。 (禁止在同一个层次结构中涉及两个类的隐式转换。)

10-06 13:01
查看更多