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