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

mac*_*nir 10 c# generics delegates

我已经定义了一个泛型类" Lazy<T>",用于惰性评估和缓存委托的结果Func<T>.

我还定义了两个隐式转换运算符,所以我可以Lazy<T>从一个Func<T>s 创建一个,我可以分配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};
  }
}
Run Code Online (Sandbox Code Playgroud)

但是这个类不能像我预期的那样工作,在下面的测试应用程序中突出显示:

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) };
  }
}
Run Code Online (Sandbox Code Playgroud)

在标有的行上//ERROR,我收到编译错误:

错误CS0266:无法隐式转换Lazy<System.Collections.Generic.IList<string>>System.Collections.Generic.IList<string>.存在显式转换(您是否错过了演员?)

此错误令人困惑,因为存在从源到目标类型的隐式转换.并且,从表面上看,代码块(3)与(1)做同样的事情.另外,它与(2)的区别仅在于用于专门化Lazy的类型.

任何人都可以向我解释这里发生了什么?

Jon*_*eet 17

问题是你试图IList<T>隐式地转换,并且IList<T>不包括IList<T>(即使它们是相同的类型) - 只考虑转换为非接口类型的包含.从C#3.0规范的6.4.3节:

如果从类型A到类型B存在标准隐式转换(第6.3.1节),并且如果A和B都不是接口类型,那么A被认为包含在B中,并且B被认为包含A.

在6.4.4节讨论用户定义的转换时,其中一个步骤是(强调我的):

  • 找到适用的用户定义和提升转换运算符集合U.

该集合由D中的类或结构声明的用户定义和提升的隐式转换运算符组成,这些运算符从包含S 的类型转换为包含在T中的类型.如果U为空,则转换未定义,并发生编译时错误.

IList<T>不包括在内IList<T>,因此这一步骤失败了.

编译器会在其他场景中进行"链式"隐式转换 - 所以如果你真的有一个Lazy<List<T>>可以写的:

object strings = lazyStrings;
Run Code Online (Sandbox Code Playgroud)

作品,因为List<T>被包围object(因为二者都是类,并有一个标准的隐式转换从List<T>object).

至于为什么会出现这种情况,我怀疑它是为了阻止你期望引用转换的奇怪情况,但你实际上会得到隐式转换.假设我们有:

class ListLazy : Lazy<IList<string>>, IList<string>
{
    // Stuff
}
...
Lazy<IList<string>> x = new ListLazy();
IList<string> list = x;
Run Code Online (Sandbox Code Playgroud)

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

  • 事实上,你的猜想就像往常一样乔恩.我们不允许在接口上进行这种转换的原因是因为我们永远不想用昂贵的"用户定义"表示改变转换来"替换"廉价的"内置"表示保留转换.转换为接口应始终只是一种类型检查,以查看接口是否已实现. (5认同)
  • 我会把包含的定义放到答案中......坚持下去. (2认同)
  • 此外,规范的这一部分中的"包含"内容令人困惑,而不是我们实际实现的内容.我一直想把它清理干净一段时间,但从来没有真正想要通过它. (2认同)
  • 这些东西也让我疯狂.我总是要查一查,以确保我做对了.非泛型类型是绑定和未绑定类型的事实坦率地说是奇怪的. (2认同)