关于 C# 语言规范中隐式转换的问题
-
03-10-2019 - |
题
第 6.1 节 隐式转换 定义一个 身份转换 因此:
身份转换从任何类型转换为相同类型。这种转换的存在使得已经具有所需类型的实体可以说可以转换为该类型。
现在,这些句子的目的是什么?
(在第 §6.1.6 隐式引用转换中)
隐式引用转换为:
- [...]
- 从任何 参考类型 到一个 参考类型
T
如果它具有隐式标识或引用转换为 参考类型T
0 和T
0 身份转换为T
.
和:
(在§6.1.7 拳击转换中)
- 值类型具有到接口类型的装箱转换
I
如果它具有到接口类型的装箱转换I
0 和I
0 身份转换为I
.
最初它们看起来是多余的(同义反复)。但他们的存在一定是有目的的,那么他们为什么会存在呢?
你能举出两种类型的例子吗 T
1, T
2 这样 T
1 会 不是 可以隐式转换为 T
2 如果不是上面引用的段落呢?
解决方案
规范的第4.7节指出,从 Foo<dynamic>
至 Foo<object>
反之亦然。您引用的规格部分是为了确保处理此情况的。也就是说,如果有隐式参考转换从t到 C<object, object>
然后还有一个隐式的参考转换为 C<object, dynamic>
, C<dynamic, object>
和 C<dynamic, dynamic>
.
有人可能会合理地指出,(1)这些短语的意图是毫无意义的 - 因此,您的问题 - 并令人困惑,以及(2)有关身份转换的部分应该交叉引用动态转换的部分,以及(3)短语像这样的规格使规格实现者很难将规格语言清楚地转化为实现。怎么知道是否存在这种类型?规格不必指定确切的算法,但是如果提供更多指导,那将是很好的。
可悲的是,规格不是一个完美的文档。
其他提示
2010 年 9 月 22 日更新:
我怀疑除了 Timwi 之外还有人会读到这篇文章。即便如此,我还是想对这个答案进行一些编辑,因为新的答案现已被接受,并且关于规范引用的摘录是否正确的争论仍在继续(至少在我可能想象的世界中)在技术上是多余的。我没有添加太多内容,但内容太多,无法发表评论。大部分更新可以在标题下找到 “转换涉及 dynamic
类型” 以下。
2010 年 9 月 19 日更新:
在您的评论中:
[T]这没有道理。
该死的,蒂姆维,你这么说 很多. 。但好吧,那么;你让我处于守势,所以开始吧!
免责声明:我有 当然不 尽可能仔细地检查规范。根据您最近提出的一些问题,您似乎最近对此进行了相当多的研究。这自然会让你比 SO 上的大多数用户更熟悉很多细节;因此,就像您可能从埃里克·利珀特以外的任何人那里收到的大多数答案一样,这可能不会令您满意。
不同场所
首先,你的问题的前提是,如果突出显示的陈述是 多余的, ,那么他们不服务 目的. 。我的回答的前提是,如果多余的陈述澄清了一些事情,那么它们并不一定是没有目的的。 对每个人来说并不明显. 。这些都是矛盾的前提。如果我们不能在前提上达成一致,我们就无法进行简单的逻辑论证。我只是要求你重新考虑你的前提。
然而,你的回应是 重申 你的前提:“如果这些句子确实是多余的,那么它们只会让读者感到困惑,而不会澄清任何事情。”
(顺便说一句,我喜欢你如何将自己设置为该规范的所有读者的代表。)
确切地说,我不能责怪你担任这个职位。我的意思是,确实如此 似乎 明显的。而且我在原来的回答中没有给出任何具体的例子。所以下面我将尝试包括一些具体的例子。但首先,让我退后一步,提出我的看法,解释为什么会出现这种奇怪的现象 身份转换 概念首先存在于规范中。
目的 身份转换 定义
乍一看,这个定义似乎相当多余。这不是说任何类型 T 的实例都可以转换为...好吧,到T?是的。但我假设*该定义的目的是为规范提供适当的词汇来利用以下概念: 类型标识 在讨论的背景下 转换.
这允许有关本质上具有传递性的转换的声明。您从规范中引用的第一点作为同义反复语句的示例就属于此类。它表示如果为某种类型(我称之为 K)定义了到另一种类型 T 的隐式转换0 和T0 身份转换为 T,则 K 可隐式转换为 T。根据定义 身份转换 如上所述,"与......有身份转换 "实际上是指 "与......类型相同"。 所以声明是 多余的.
但再说一遍:这 身份转换 定义首先是为了给规范配备一种正式的描述语言 转换 不必说“如果T0 和T确实是同一类型。”
好的,是时候看具体例子了。
存在隐式转换的地方 可能 不明显 一些 开发商
笔记:A 好多了 Eric Lippert 在中提供了示例 他对问题的回答. 。我留下前两个例子只是对我的观点的次要强化。我还添加了第三个示例,具体化了之间存在的身份转换 object
和 dynamic
正如埃里克的回答所指出的。
传递引用转换
假设你有两种类型, 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;
好的,在我继续之前,我意识到 这很明显 你 和 我. 但我的答案是,而且从一开始就是这样,它可能 不是 显而易见 每个人, ,因此指定它--同时 多余的——还有一个 目的.
那 目的 是:向那些摸不着头脑、查看该代码的人表明,这是合法的。一个 隐式转换 存在从 N 到 M,并且 身份转换 从 M 到 K 存在(即 M 和 K 是同一类型);因此存在从 N 到 K 的隐式转换。它不是 只是 合乎逻辑(尽管可能 是 逻辑); 它就在规范中. 。否则,人们可能会错误地认为像下面这样的东西是必要的:
K k = (M)n;
显然,事实并非如此。
传递拳击转换
或者采取类型 int
. 。一个 int
可以装箱为 IComparable<int>
, , 正确的?所以这是合法的:
int i = 10;
IComparable<int> x = i;
现在考虑一下:
int i = 10;
IComparable<System.Int32> x = i;
再次, 是的, ,可能是 明显的 献给你、我以及 90% 可能遇到过它的开发者。但对于那些没有立即看到这一点的极少数人来说:A 拳击转换 存在于 int
到 IComparable<int>
, , 和 身份转换 存在于 IComparable<int>
到 IComparable<System.Int32>
(IE。, IComparable<int>
和 IComparable<System.Int32>
是同一类型);所以拳击转换存在于 int
到 IComparable<System.Int32>
.
转换涉及 dynamic
类型
我将借用上面的引用转换示例,并稍微调整它以说明之间的恒等关系 object
和 dynamic
在规范的 4.0 版本中。
假设我们有以下类型 M<T>
和 N
, ,并在某处定义了以下隐式转换:
public static implicit operator M<object>(N n);
那么以下是合法的:
N n = new N();
M<dynamic> m = n;
显然,上面的内容要少得多 明显的 比前两个例子。但这是一个价值百万美元的问题: 上面的会 仍然 即使问题中引用的规范摘录不存在,也是合法的吗? (我将把这些摘录称为 问 为简洁起见。)如果答案是肯定的,那么 问 事实上是多余的。如果不是,那就不是。
我相信答案是肯定的。
考虑以下的定义 身份转换, ,在第 6.1.1 节中定义(我在这里包含整个部分,因为它很短):
身份转换从任何类型转换为相同类型。这种转换的存在使得已经具有所需类型的实体可以说可以转换为该类型。
因为
object
和dynamic
被认为是等价的,之间存在身份转换object
和dynamic
, 以及在替换所有出现的情况下相同的构造类型之间dynamic
和object
. [强调我的]
(最后一部分也包含在第 4.7 节中,它定义了 dynamic
类型。)
现在让我们再看一下代码。我特别对这一行感兴趣:
M<dynamic> m = n;
本声明的合法性(不考虑 问 -- 请记住,正在讨论的问题是上述声明的假设合法性 如果 问 做过 不是 存在),因为 M<T>
和 N
是自定义类型,取决于用户定义的隐式转换之间是否存在 N
和 M<dynamic>
.
存在隐式转换 N
到 M<object>
. 。根据上面引用的规范部分,之间存在身份转换 M<object>
和 M<dynamic>
. 。根据定义 身份转换, M<object>
和 M<dynamic>
是同一类型.
因此,就像前两个(更明显的)示例一样,我相信确实存在隐式转换 N
到 M<dynamic>
即使不采取 问 考虑到, ,正如确实存在隐式转换一样 N
到 K
在第一个示例中,并且存在装箱转换 int
到 IComparable<System.Int32>
在第二个例子中。
没有 问, ,这不太明显(因此 问的存在); 但这并不意味着它是错误的 (IE。, 问 不是 必要的 来定义此行为)。它只是让它变得不那么明显。
结论
我在最初的回答中说过,这是“显而易见的”解释,因为在我看来,你找错了树。您最初提出了这个挑战:
你能举出两种类型T的例子吗1, T2 使得 T1 不会隐式转换为 T2 如果不是上面引用的段落呢?
没有人会迎接这个挑战,蒂姆维,因为这是不可能的。摘录有关引用转换的第一个摘录。也就是说,如果类型 K 可以隐式转换为 T,则它也可以隐式转换为类型 T0 和T0 与T相同。解构这个,把它重新组合起来,你就会得到一个明显的同义反复:如果 K 可隐式转换为 T,则 K 也可隐式转换为 T。这是否引入了任何新的隐式转换?当然不是。
所以也许本·沃伊特的评论是正确的;也许您所询问的这些要点最好放在脚注中,而不是放在正文中。无论如何,我很清楚他们 是 多余的,所以从前提开始 他们不能是多余的,否则他们就不会在那里 就是去做一件愚蠢的事。愿意接受这样一个事实:冗余的陈述可能仍然可以阐明一个对每个人来说可能并不明显的概念,并且更容易接受这些陈述的本来面目。
多余的?是的。同义反复?是的。无意义?在 我的 意见,没有。
*显然,我没有参与编写 C# 语言规范。如果我这样做的话,这个答案就会更加权威。事实上,它只是代表了一个人试图理解一份相当复杂的文档的无力尝试。
原答案
我认为你(也许是故意的)忽略了这里最明显的答案。
考虑你的问题中的这两句话:
(1) 最初,它们似乎是多余的 (同义反复)。 (2) 但它们必须在那里 为什么它们会在那里?
对我来说,这两句话放在一起的含义是同义反复的陈述没有任何意义。但仅仅因为一个陈述是从既定前提逻辑上得出的,并不会让每个人都明白这一点。换句话说,即使 (1) 是真的,答案是 (2) 可能只是: 使阅读规范的任何人都清楚所描述的行为.
现在你可能会争辩说,即使有些东西不是 明显的, ,如果它提供了冗余定义,它仍然不属于规范。对于这种潜在的反对意见,我只能说:现实点。(在我看来)梳理一份文件并剔除所有陈述是不太实际的,这些陈述只是陈述可以从先前陈述中推断出的事实。
如果这 是 这是一种常见的做法,我想你会发现很多文献——不仅仅是规格,还有研究论文、文章、教科书等。——会更短、更密集、更难理解。
所以:是的,也许它们是多余的。但这并不能否定他们的目的。
身份转换转换 从任何类型到同一类型. 。这种转换存在于 一个已经具有必要类型的实体 可以说可以转换为该类型。
这说明在c#land中,1 == 1;黑桃是一只黑桃。这是将对象引用分配给同一类型变量的基础;如果可变键入的T1和一个键入的T2实际上是两个黑桃,则可以将一个分配给另一个黑桃,而无需显式将其作为黑桃施加。想象一个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#的版本,其中必须将纯值类型明确转换为其参考模拟,以存储为类中的成员变量。想象一下,例如,蓝色关键字类型(int,float,Decimal)和浅蓝色类名称(int32,float,Decimal)的C#版本,提到了两种截然不同的,仅唯一的说明类型,而INT类型,浮点,十进制等不合法作为成员变量类型,因为它们不是参考类型:
public class MyClass
{
Int32 MyBoxedValueType; //using "int" not legal
}
...
MyClass myClass = new MyClass();
int theInt = 2;
myClass.MyBoxedValueType = (Int32)theInt; //explicit cast required
我知道这听起来很愚蠢,但是在某种程度上,这些事情必须知道,在计算机中,您必须指定所有内容。在某个时候阅读《冰曲棍球》的美国曲棍球规则手册;本书中的第一条规则是游戏应在冰面上玩。这是最终的“井”之一,但也是游戏的基本真理,必须理解才能使任何其他规则变得有道理。
可以这样的代码在称为时可以保证通行证 Convert.ChangeType(client, typeof(Client))
不管是否 IConvertible
已实施。
看 ChangeType
从 mscorlib
带有反射器并注意到的条件 value
被返回。
记住一个 =
操作员不是转换,而只是参考集。所以代码喜欢 Client client_2 = client_1
不执行任何隐式转换。如果声明了身份隐式转换,则错误 CS0555
发行。
我想规格说让C#编译器处理此类情况,从而 点没有 手动尝试定义身份转换。