我定义了一个通用类“Lazy<T>
”,用于延迟评估和缓存委托(delegate)Func<T>
的结果。
我还定义了两个隐式强制转换运算符,因此我可以从Lazy<T>
创建一个Func<T>
,并且可以将Lazy<T>
分配给T
(获取Value
的Lazy<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)很尴尬,因为它们可以稍后显示在类型层次结构中,而对于类,如果您明白我的意思,就总是知道您在哪里。 (禁止在同一个层次结构中涉及两个类的隐式转换。)