2010 年 9 月 22 日更新:
我怀疑除了 Timwi 之外还有其他人会阅读这篇文章。即便如此,我还是想对这个答案进行一些编辑,因为现在已经接受了一个新的答案,并且关于是否引用的规范摘录的辩论仍在继续(至少在我可能想象的世界中)在技术上是多余的。我没有添加太多,但它太重要了,无法放入评论中。大部分更新可以在下面的标题“涉及dynamic 类型的转换”下找到。
2010 年 9 月 19 日更新:
在您的评论中:
[T]这没有意义。
该死的,Timwi,你说很多。但是,好吧,那么;你让我处于守势,所以来吧!
免责声明:我绝对没有像您一样仔细检查了规范。根据您最近的一些问题,您似乎最近一直在研究它。这自然会让你比 SO 上的大多数用户更熟悉很多细节;因此,就像您可能从 Eric Lippert 以外的任何人那里收到的大多数答案一样,这可能不会让您满意。
不同的前提
首先,您的问题的前提是,如果突出显示的语句是冗余,那么它们没有目的。我的回答的前提是,如果冗余陈述澄清了对每个人都不是很明显的事情,那么它们不一定没有目的。这些都是矛盾的前提。如果我们不能在前提上达成一致,我们就不能有一个直截了当的逻辑论证。我只是要求你重新考虑你的前提。
然而,你的回答是重申你的前提:“如果这些句子真的是多余的,那么它们只会让读者感到困惑,而不会澄清任何事情。”
(顺便说一句,我喜欢你将自己设置为所有规范读者的代表。)
确切地说,我不能责怪你担任这个职位。我的意思是,它确实似乎很明显。而且我在最初的答案中没有给出任何具体的例子。所以下面我将尝试包括一些具体的例子。但首先,让我退后一步,谈谈为什么这个奇怪的身份转换概念首先存在于规范中。
身份转换的目的定义
乍一看,这个定义似乎是多余的;不是说任何类型 T 的实例都可以转换为……嗯,转换为 T 吗?是的。但我假设*此定义的目的是为规范提供适当的词汇表,以便在讨论 conversions 的上下文中利用 type identity 的概念。
这允许关于本质上具有传递性的转换的陈述。您从规范中引用的第一点作为重言式陈述的示例属于这一类。它表示,如果为某种类型(我称之为 K)定义了隐式转换为另一种类型 T0 并且 T0 具有到的身份转换 T,则 K 可以隐式转换为 T。根据上面给出的恒等转换的定义,“具有恒等转换”实际上意味着“与类型相同”。所以这个语句是冗余的。
但同样:身份转换定义的存在首先为规范配备了一种描述转换的正式语言,而不必说诸如“如果T0 和 T 真的是同一类型。”
好的,具体例子的时间到了。
隐式转换的存在可能对一些开发人员来说并不明显
注意:Eric Lippert 在his answer to the question 中提供了一个更好的示例。我将前两个例子作为对我观点的次要强化。我还添加了第三个示例,具体化了 object 和 dynamic 之间存在的身份转换,正如 Eric 的回答中所指出的那样。
传递引用转换
假设您有两种类型,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% 可能遇到过它的所有开发人员来说,这可能是显而易见的。但是对于那些没有立即看到它的少数人来说:boxing 转换从 int 到 IComparable<int>,身份转换从 IComparable<int>到IComparable<System.Int32>(即IComparable<int>和IComparable<System.Int32>是同一类型);所以存在从int 到IComparable<System.Int32> 的拳击转换。
涉及dynamic 类型的转换
我将从上面的参考转换示例中借用并稍微调整一下,以说明规范 4.0 版本中 object 和 dynamic 之间的身份关系。
假设我们有 M<T> 和 N 类型,并在某处定义了以下隐式转换:
public static implicit operator M<object>(N n);
那么以下是合法的:
N n = new N();
M<dynamic> m = n;
很明显,上面的例子远没有前面两个例子那么明显。但这是一个价值百万美元的问题:即使问题中引用的规范摘录不存在,上述仍然是否合法?(我将致电这些摘录 Q 为简洁起见。)如果答案是肯定的,那么 Q 实际上是多余的。如果不是,则不是。
我相信答案是肯定的。
考虑在第 6.1.1 节中定义的身份转换的定义(我在这里包括了整个部分,因为它很短):
身份转换从任何类型转换为相同类型。这种转换的存在使得已经具有所需类型的实体可以说是可转换为该类型。
因为object 和dynamic 被认为是等效的,所以object 和dynamic 之间以及构造类型之间存在身份转换,当用@987654358 替换所有出现的dynamic 时相同的构造类型之间@。 [强调我的]
(最后一部分也包含在第 4.7 节中,它定义了dynamic 类型。)
现在让我们再看一遍代码。特别是我对这一行感兴趣:
M<dynamic> m = n;
此声明的合法性(忽略 Q -- 请记住,正在讨论的问题是上述声明的假设合法性 if Q 不存在),因为M<T>和N是自定义类型,取决于N和M<dynamic>之间是否存在用户定义的隐式转换。
存在从N 到M<object> 的隐式转换。根据上面引用的规范部分,M<object> 和 M<dynamic> 之间存在身份转换。根据身份转换的定义,M<object>和M<dynamic>属于同一类型。
因此,就像前两个(更明显的)示例一样,我相信确实存在从 N 到 M<dynamic> 的隐式转换即使没有考虑 Q帐户,正如在第一个示例中确实存在从 N 到 K 的隐式转换,并且在第二个示例中存在从 int 到 IComparable<System.Int32> 的装箱转换。
没有Q,这将不那么明显(因此Q的存在); 但这并不意味着它是假的(即,Q 不是必要来定义此行为)。它只是让它变得不那么明显。
结论
我在最初的回答中说这是“显而易见的”解释,因为在我看来,你在找错树了。您最初提出了这个挑战:
您能否举一个 T1、T2 两种类型的示例,使得 T1 不会隐式转换为 T2 如果不是上面引用的段落?
蒂姆维,没有人会迎接这个挑战,因为这是不可能的。取第一个关于参考转换的摘录。就是说如果类型 K 可以隐式转换为 T0 并且 T0 与 T 相同,则类型 K 可以隐式转换为类型 T。解构 this,把它回到一起,你会留下一个明显的重言式:如果 K 可以隐式转换为 T,则 K 可以隐式转换为 T。这会引入任何新的隐式转换吗?当然不是。
所以也许 Ben Voigt 的评论是正确的;也许您要询问的这些要点最好放在脚注中,而不是放在正文中。无论如何,我很清楚它们 是多余的,因此从前提开始它们不能是多余的,否则它们就不会在那里做傻事。愿意接受多余的陈述可能仍会阐明对每个人来说可能并不明显的概念,并且更容易接受这些陈述的本来面目。
冗余?是的。重言式?是的。无意义?在我的看来,没有。
*显然,我没有参与编写 C# 语言规范。如果我这样做了,这个答案将更具权威性。事实上,它只是代表一个人试图理解一份相当复杂的文件的微弱尝试。
原答案
我认为您(也许是故意)忽略了这里最明显的答案。
在你的问题中考虑这两个句子:
(1) 最初它们似乎是多余的
(同义词)。 (2) 但他们必须在那里
出于某种目的,那么他们为什么会在那里?
对我来说,这两个句子的含义是重复的陈述没有任何意义。但是仅仅因为一个陈述在逻辑上是从既定的前提中得出的,这并不能让每个人都明白这一点。换句话说,即使 (1) 为真,(2) 的答案可能只是:让阅读规范的任何人都清楚描述的行为。
现在您可能会争辩说,即使某些东西显而易见,如果它提供了冗余定义,它仍然不属于规范。对于这种潜在的反对意见,我只能说:现实一点。梳理一份文件,剔除所有陈述,这些陈述只是陈述可能从先前陈述中推导出来的事实,这并不实际(在我看来)。
如果这是一种常见的做法,我想你会发现很多文献——不仅仅是规范,还有研究论文、文章、教科书等——将是一个更短、更密集、更难理解。
所以:是的,也许它们是多余的。但这并不能否定他们的目的。