代码之家  ›  专栏  ›  技术社区  ›  Timwi

C语言规范中的隐式转换问题

  •  11
  • Timwi  · 技术社区  · 14 年前

    第6.1节隐含转换 定义 因此:

    标识转换从任何类型转换为同一类型。这种转换的存在使得已经具有所需类型的实体可以说是可转换为该类型的。

    现在,这些句子的目的是什么?

    • [...]
    • 从任何 引用类型 引用类型 T 引用类型 0 T型 0 身份转换为 T型

    (在§6.1.7装箱转换中)

    • I 如果它有到接口类型的装箱转换 0 0 身份转换为 .

    你能举两种类型的例子吗 , T型 2 以至于 隐式转换为 T型 2 如果不是上面引用的段落?

    4 回复  |  直到 14 年前
        1
  •  2
  •   Eric Lippert    14 年前

    Foo<dynamic> Foo<object> 反之亦然。您引用的规范部分是为了确保这个案例得到处理。也就是说,如果存在从T到 C<object, object> 然后还有一个隐式引用转换到 C<object, dynamic> , C<dynamic, object> C<dynamic, dynamic> .

    有人可能会合理地指出:(1)这些短语的意图是不明显的-因此你的问题-和混乱的,(2)关于身份转换的部分应该交叉引用关于动态转换的部分,(3)规范中类似的短语使得规范的实现者很难清楚地翻译将spec语言添加到实现中。怎样才能知道是否存在这样的类型?规范不需要指定精确的算法,但是如果它能提供更多的指导就更好了。

    遗憾的是,这个规范并不是一个完美的文档。

        2
  •  6
  •   Community CDub    5 年前

    我怀疑除了蒂姆维以外,还有人会读这个。即便如此,我还是想对这个答案做一些修改,因为一个新的答案现在已经被接受了,关于这个规范引用的摘录在技术上是否多余的争论仍在继续(至少在我想象的世界里)。我没有补充太多,但内容太多,不适合发表评论。大部分更新可以在标题下找到 “涉及 dynamic 在下面。


    2010年9月19日更新:

    [T] 他的话讲不通。

    该死,蒂姆维,你这么说 . 但好吧,那么,你让我处于守势,就这样吧!

    免责声明:我有 绝对不是

    不同的前提

    ,那么他们就不服侍了 目的 . 我的回答的前提是,如果多余的陈述阐明了 . 这些都是矛盾的前提。如果我们不能在前提上达成一致,我们就不能有一个直接的逻辑论证。我只是想让你重新考虑你的前提。

    不过,你的回答是 重申

    (顺便说一句,我喜欢你将自己设置为规范所有读者的代表。)

    似乎 很明显。在我最初的回答中,我没有给出任何具体的例子。所以下面我将尝试包括一些具体的例子。但首先,让我退后一步,谈谈为什么会这样奇怪 身份转换 概念首先存在于规范中。

    会议的目的 身份转换

    乍一看,这个定义似乎是多余的;它不是说任何类型t的实例都可以转换为。。。好吧,对T?是的,是的。但是我假设这个定义的目的是为spec提供适当的词汇表来使用 转换 .

    0 和T 身份转换为 T、 那么K可以隐式地转换为T 如上所述,“has a identity conversion to”really means“的类型与”相同 .

    但又一次:这个 定义的存在首先是为了给规范配备一种正式的描述语言 转换 不用说“如果 0 和T其实是同一类型的。”

    可以 不明显 一些 开发商

    好多了 Eric Lippert在 his answer to the question . 我把前两个例子作为我观点的补充。我还添加了第三个示例,具体化了 object 正如埃里克在回答中指出的。

    传递引用转换

    假设你有两种类型, M N ,得到了如下定义的隐式转换:

    public static implicit operator M(N n);
    

    然后你可以这样写代码:

    N n = new N();
    M m = n;
    

    using 最上面的声明:

    using K = M;
    

    N n = new N();
    K k = n;
    

    好吧,在我继续之前,我意识到 . 但我的答案是,而且从一开始就是,它可能 显而易见 每个人 ,从而指定它--而 冗余 --仍然有一个

    那个 是:为了让任何挠头的人明白,看着那个代码,这是合法的。安 隐式转换 身份转换 存在于M到K之间(即M和K是同一类型);因此隐式转换存在于N到K之间,而不是 . 否则,人们可能会错误地认为有必要采取以下措施:

    K k = (M)n;
    

    显然不是。

    传递装箱转换

    或者选择 int . 安 内景 可以作为一个盒子 IComparable<int>

    int i = 10;
    IComparable<int> x = i;
    

    现在考虑一下:

    int i = 10;
    IComparable<System.Int32> x = i;
    

    再一次, 明显的 给你,我,还有90%的开发者。但对于那些没有马上看到的苗条的少数人来说:一个 拳击转换 存在于 内景 IComparable<内部> ,和 IComparable<内部> IComparable<System.Int32> i可比较<系统.Int32> 内景

    涉及

    我将借用上面的引用转换示例,稍微调整一下,以说明 对象 动态

    假设我们有 M<T>

    public static implicit operator M<object>(N n);
    

    那么以下是合法的:

    N n = new N();
    M<dynamic> m = n;
    

    显然,上述情况要少得多 明显的 比前面的两个例子。但这里有一个百万美元的问题: 你会喜欢上面的吗 仍然 (我将把这些摘录称为 如果答案是肯定的,那么

    我相信答案是肯定的。

    考虑一下 身份转换

    标识转换从任何类型转换为同一类型。这种转换的存在使得已经具有所需类型的实体可以说是可转换为该类型的。

    因为 对象 如果两者之间存在身份转换 对象 , 以及在替换 动态 具有 对象 [强调我的]

    动态 类型。)

    现在让我们再看一遍代码。我特别感兴趣的是这一行:

    M<dynamic> m = n;
    

    如果 M<T> 都是自定义类型,取决于用户定义之间是否存在隐式转换 M<dynamic> .

    存在来自的隐式转换 M<object> . 根据上面引用的规范部分,在 . 根据定义 身份转换 , M<动态> 是同一类型的

    所以,就像前两个(更明显的)例子一样,我相信确实存在从 M<动态> 即使不吃 考虑 ,正如确实存在从 K 内景 i可比较<系统.Int32>

    没有 ,这就不那么明显了(因此 但这并不意味着它是假的 (即。, 不是 必要的 定义此行为)。只是让事情变得不那么明显。

    结论

    你能举两种类型T的例子吗 ,吨 2 以至于 1 2

    没有人会面对这个挑战,蒂姆维,因为这是不可能的。第一个节选是关于引用转换的。如果K型隐式可转换为T型,则K型隐式可转换为T型 0 和T 0

    所以,也许本·沃伊特的评论是正确的;也许你要问的这些问题最好放在脚注中,而不是正文中。无论如何,我很清楚他们 冗余,所以从前提开始 他们不可能是多余的,否则他们就不在了 就是去做傻事。愿意接受这样一个事实,即一个多余的陈述可能仍然会对一个并非每个人都清楚的概念有所帮助,而接受这些陈述会变得更容易。

    冗余?对。重言?对。毫无意义?在 我的 意见,不。

    *显然,我没有参与编写C语言规范。如果我这么做了,这个答案会更有权威性。事实上,它只是代表了一个人试图理解一个相当复杂的文档的微弱努力。


    原始答案

    我认为你(也许是故意)忽略了这里最明显的答案。

    (1) 起初,它们似乎是多余的 (同义反复)。 (2) 但他们一定在那里

    (1) 是真的,答案是 可能只是: 让所有阅读规范的人都明白所描述的行为

    现在你可能会说即使有些东西不是 ,如果它提供了冗余定义,那么它仍然不属于规范。对于这个潜在的反对意见,我只能说:实事求是。在我看来,梳理一份文件,把那些仅仅是陈述事实的陈述从先前的陈述中剥离出来是不实际的。

    如果这个

    所以:是的,也许它们是多余的。但这并不否定他们的目的。

        3
  •  1
  •   KeithS    14 年前

    . 这个 转换的存在使得 实体 已具有所需类型的 可以 类型。

    这就是说,在C#-land中,1==1;黑桃就是黑桃。这是将对象引用分配给同一类型变量的基础;如果类型为T1的变量和类型为T2的变量实际上都是Spade,则可以将一个变量分配给另一个变量,而不必显式地将一个变量转换为Spade。想象一下一个C变量,其中任务必须如下所示:

    Spade mySpade = new Spade();
    Spade mySpade2;
    
    mySpade2 = (Spade)mySpade; //explicit cast required
    

    此外,数学中的一个“恒等式”表示,一个表达式在给定一组输入的情况下计算结果,它等价于在给定相同输入的情况下产生相同结果的另一个表达式。在编程中,这意味着计算为某个类型实例的表达式或函数与该类型等价,而无需显式转换。如果不成立,则需要以下代码:

    public int myMethod() { /*Do something*/ }
    ...
    int myInt = (int)myMethod(); //required even though myMethod() evals to an int.
    ...
    int myInt = (int)(1 + 2); //required even though 1, 2, and 1+2 eval to an int.
    

    第二条规则基本上说,如果在某种程度上,成员变量(定义为装箱类型,因为其容器是引用类型)被声明为同一类型,则可以将值类型赋给类上的成员变量。如果不指定这个规则,理论上,可能存在一个C#版本,其中纯值类型必须显式转换为它们的引用模拟,才能作为成员变量存储在类中。例如,想象一下,C#的一个版本,其中蓝色关键字类型(int,float,decimal)和浅蓝色类名(Int32,float,decimal)引用了两个非常不同的、只能显式转换的类型,而int,float,decimal等作为成员变量类型是不合法的,因为它们不是引用类型:

    public class MyClass
    {
      Int32 MyBoxedValueType; //using "int" not legal
    }
    
    ...
    
    MyClass myClass = new MyClass();
    int theInt = 2;
    
    myClass.MyBoxedValueType = (Int32)theInt; //explicit cast required
    

    我知道这听起来很傻,但在某种程度上,这些东西必须是已知的,在计算机中,你必须详细说明一切。找个时间读一下美国冰球规则手册;书中的第一条规则是比赛应该在冰面上进行。这是一个最终的“好duhs”,但也是一个游戏的基本真理,必须理解,以便任何其他规则的意义。

        4
  •  -1
  •   John Alexiou    14 年前

    可能是这样的,代码保证在像这样调用时通过 Convert.ChangeType(client, typeof(Client)) 不管是否 IConvertible 已实施。

    查查证据的来源 ChangeType mscorlib value

    还记得吗 = Client client_2 = client_1 不执行任何隐式转换。如果声明了标识隐式转换,则返回错误 CS0555 已发布。

    点不 手动尝试定义标识转换。

    推荐文章