为什么不利用这个隐式类型转换的工作吗? [英] Why doesn't this use of implicit casts work?

查看:86
本文介绍了为什么不利用这个隐式类型转换的工作吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我定义泛型类懒< T> ,对于懒惰的评价和代表结果的缓存 Func键< T> ;



我也定义了两种隐式转换运营商,所以我可以创建一个懒< T> Func键< T> s,而我可以指定一个懒< T> T (获得的懒<的; T>



我们的想法是,你可以围绕懒<通;在<$ C $的一个实例; T> C> T ,但不做计算/直到它被分配到 T

的实际情况提取值的工作

  //类懒< T> 
//封装,可当第一次访问,
//,然后缓存中检索的值。
类懒< T>
{
私人Func键< T> _getter;
私人牛逼_cached;
私人布尔_isCached;

//获取/设置吸气委托
//那'计算'的值。
公共Func键< T>吸气
{
得到
{
返回_getter;
}

{
_getter =价值;
_cached =默认(T);
_isCached = FALSE;
}
}

//获取/设置的值。
公众吨价
{
得到
{
如果
{
_cached =吸气剂()(_isCached!);
_isCached = TRUE;
_getter = NULL;
}
返回_cached;
}

{
_cached =价值;
_isCached = TRUE;
_getter = NULL;
}
}

//隐式转换:

//创建从懒℃的笔; T>
公共静态隐运营商T(懒惰< T>懒惰)
{
返回lazy.Value;
}

//创建一个懒< T>从Func键< T>
公共静态隐运营商懒< T>(Func键< T>吸气)
{
返回新懒人< T> {消气=吸气};
}
}



不过,正如我预期这个类不起作用一个案例,突出表现在以下测试程序:

 类节目
{
静态无效的主要()
{
//这个工作好(1)
TestLazy(()=> MakeStringList());

//这也适用(2)
&懒LT;字符串> lazyString =新Func键与LT;串>(()=>中的xyz);
字符串s = lazyString;

//这不会编译(3)
//
&懒LT; IList的<串GT;> lazyStrings =新Func键与LT; IList的<串GT;>(MakeStringList);
&IList的LT;字符串>字符串= lazyStrings; //错误
}


静态无效TestLazy< T>(Func键< T>吸气)
{
懒< T>懒惰=吸收剂; $ B $(B T)= nonLazy偷懒;
}

私有静态的IList<串GT; MakeStringList()
{
返回新的List<串GT; {新的字符串( - ,10)};
}
}

在标记行//错误,我得到一个编译错误:



错误CS0266:无法隐式转换类型懒< System.Collections.Generic.IList<串GT;> System.Collections.Generic.IList<串GT; 。一个显式转换存在(是否缺少强制转换?)



此错误,因为确实存在从源头隐式转换到目标类型是混乱题。
和,在它面前,代码块(3)在做同样的事情,(1)
此外,由式(2)仅用于专门的懒惰类型。

任何人都可以向我解释这是怎么回事呢?


解决方案

问题是,你想转换为的IList< T> 含蓄和的IList< T> 不包含通过的IList< T> (即使它们是同一类型) - 只能转换到非接口类型在围绕考虑。从C#3.0规范的6.4.3节:




如果(第6.3.1节)的存在标准隐式转换
A型到
类b,如果A和b都不是
接口类型
,然后A被说成是
经b包围,和b就是
涵盖说。




在第6.4.4节,谈论用户定义的转换,其中一个步骤是(重点煤矿):





  • 寻找适用的用户自定义和提升转换
    运营商,U



这组由用户定义的
的,并取消了申报隐式转换
运营商一个从类型
涵盖小号,以一个类型转换类或
结构D中T包含
。如果U为空,则转换为
未定义,编译时错误
发生。




的IList< T> 不受的IList<涵盖; T> ,因此该步骤失败

$ b $。 b

,编译器会做链,在其他情况下的隐式转换,但 - 所以,如果你真的有一个懒<名单< T>> 你的可能的写:

 对象的字符串= lazyStrings; 

工作,因为列表< T> 是通过对象包含(因为二者都是类,有一个从列表℃的标准隐式转换; T> 对象)。



现在为的为什么的是这样的话,我的犯罪嫌疑人的是停止奇怪情况下,你所期望的基准转换,但实际上你会得到隐式转换。假设我们有:

 类ListLazy:懒< IList的<串>>中的IList<串> 
{
//东西
}
...
&懒LT; IList的<串GT;> X =新ListLazy();
&IList的LT;字符串>列出= X;



转换哪些应该使用?有一个隐含的参考的距离的实际的类型的IList<转换;串> ...但是编译器不知道这一点,因为表达式的类型为懒<&IList的LT;串GT;> 。基本上接口是尴尬的,因为他们可以在类型层次结构后显示出来,而用一个类你总是知道你在哪里,如果你明白我的意思。 (涉及同一层级中两类隐式转换是禁止的。)


I've defined a generic class "Lazy<T>", for lazy evaluation and caching of the result of a delegate Func<T>.

I also define two implicit cast operators so I can create a Lazy<T> from a Func<T>s, and I can assign a Lazy<T> to a T (gets the Value of the Lazy<T>)

The idea is that you can pass around a Lazy<T> in place of an instance of T, but not do the work to calculate/retrieve the value until it is assigned to an actual instance of 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};
  }
}

But this class doesn't work as I expected in one case, highlighted in the test app below:

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) };
  }
}

On the line marked with //ERROR, I get a compile error:

error CS0266: Cannot implicitly convert type Lazy<System.Collections.Generic.IList<string>> to System.Collections.Generic.IList<string>. An explicit conversion exists (are you missing a cast?)

This error is confusing as there does exist an implicit cast from the source to the target type in question. And, on the face of it, code chunk (3) is doing the same thing as (1) Also, it differs from (2) only by the type used to specialize the Lazy.

Can anyone explain to me what's going on here?

解决方案

The problem is that you're trying to convert to IList<T> implicitly, and IList<T> isn't encompassed by IList<T> (even though they're the same type) - only conversions to non-interface types are considered in encompassing. From section 6.4.3 of the C# 3.0 spec:

If a standard implicit conversion (§6.3.1) exists from a type A to a type B, and if neither A nor B are interface-types, then A is said to be encompassed by B, and B is said to encompass A.

In section 6.4.4, talking about user defined conversions, one of the steps is (emphasis mine):

  • Find the set of applicable user-defined and lifted conversion operators, U.

This set consists of the user-defined and lifted implicit conversion operators declared by the classes or structs in D that convert from a type encompassing S to a type encompassed by T. If U is empty, the conversion is undefined and a compile-time error occurs.

IList<T> isn't encompassed by IList<T>, therefore this step fails.

The compiler will do "chained" implicit conversions in other scenarios though - so if you actually had a Lazy<List<T>> you could write:

object strings = lazyStrings;

works, because List<T> is encompassed by object (as both are classes, and there's a standard implicit conversion from List<T> to object).

Now as for why this is the case, I suspect it's to stop odd cases where you'd expect a reference conversion, but you would actually get the implicit conversion. Suppose we had:

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

Which conversion should be used? There's an implicit reference conversion from the actual type to IList<string>... but the compiler doesn't know that, because the expression is of type Lazy<IList<string>>. Basically interfaces are awkward because they can show up later in the type hierarchy, whereas with a class you always know where you are, if you see what I mean. (Implicit conversions which involve two classes in the same hierarchy are prohibited.)

这篇关于为什么不利用这个隐式类型转换的工作吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆