为什么隐式转换的使用不起作用?

时间:2009-10-02 10:04:17

标签: c# generics delegates

我已经定义了一个通用类“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的类型。

有人能向我解释这里发生了什么吗?

2 个答案:

答案 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;