我已经定义了一个通用类“Lazy<T>
”,用于对委托Func<T>
的结果进行延迟评估和缓存。
我还定义了两个隐式转换运算符,因此我可以从Lazy<T>
创建Func<T>
,我可以将Lazy<T>
分配给T
(获取{ {1}}的{1}}
我们的想法是,您可以传递Value
来代替Lazy<T>
的实例,但是在将值分配给{的实际实例之前,不要进行计算/检索值的工作{1}}。
Lazy<T>
但是这个课程不能像我在一个案例中预期的那样工作,在下面的测试应用程序中突出显示:
T
在标有T
的行上,出现编译错误:
错误CS0266:无法将类型// 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) };
}
}
。存在显式转换(您是否缺少演员?)
此错误令人困惑,因为确实存在从源到目标类型的隐式转换。 并且,从表面上看,代码块(3)与(1)做同样的事情 此外,它与(2)的区别仅在于用于专门化Lazy的类型。
有人能向我解释这里发生了什么吗?
答案 0 :(得分:17)
问题在于您是否尝试隐式转换为IList<T>
,并且IList<T>
未包含IList<T>
(即使它们属于同一类型) - 仅转换非界面类型被认为是包含在内的。来自C#3.0规范的第6.4.3节:
如果是标准的隐式转换 (§6.3.1)从A类到a类存在 B型,,如果A和B都不是 接口类型,然后说A是 B包含,B说 包含A。
在6.4.4节,谈论用户定义的转换时,其中一个步骤是(强调我的):
- 查找适用的用户定义和提升转化的集合 运营商,U。
此集包含用户定义的 并解除了隐含的转换 由班级或 D中从一种类型转换而来的结构 包含S 包含的类型 由T 。如果U为空,则转换为 undefined和编译时错误 发生。
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>
的隐式引用转换...但是编译器不知道,因为表达式是{{1 }}。基本上界面很尴尬,因为它们可以在类型层次结构中稍后出现,而对于一个类,你总是知道你在哪里,如果你看到我的意思。 (禁止在同一层次结构中涉及两个类的隐式转换。)
答案 1 :(得分:3)
这可能是一个小错字吗?
Lazy<List<string>> lazyStrings = new Func<List<string>>(MakeStringList);
IList<string> strings = lazyStrings; //ERROR
List<string> strings = lazyStrings; //OK
如果您 想要一个IList&lt;&gt;,这是一个两步转换,我想编译器不想超越自己
IList<string> istrings = strings;