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

Tim*_*mwi 11 c# language-features language-specifications implicit-conversion

6.1节隐式转换因此定义了身份转换:

身份转换从任何类型转换为相同类型.存在这种转换,使得已经具有所需类型的实体可以说可转换为该类型.

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

(在§6.1.6隐式参考转换中)

隐式引用转换是:

  • [...]
  • 从任何引用类型引用类型, T如果它具有隐式标识或引用转换为引用类型 T0T0具有标识转换为T.

和:

(在§6.1.7拳击转换中)

  • 如果值类型具有I到接口类型I0的装箱转换并且I0具有到的标识转换,则值类型具有到接口类型的装箱转换I.

最初它们似乎是多余的(同义词).但他们必须出于某种目的,所以为什么他们在那里?

你能给两种类型的例子T1,T2,使得T1不会是隐式转换为T2,如果不是因为上面引述的段落?

Dan*_*Tao 6

2010年9月22日更新:

我怀疑除了蒂姆维之外还有人会读这个.即便如此,我想对这个答案进行一些修改,因为现在已经接受了一个新的答案,并且辩论仍在继续(至少在我想象的世界中)是否引用了规范的摘录在技​​术上是多余的.我并没有增加太多,但它太大了,不适合评论.大部分更新可以在下面的"涉及dynamic类型的转换"标题下找到.


2010年9月19日更新:

在你的评论中:

[T]他没有意义.

该死的,蒂姆维,你说的很多.但是好吧,那么; 你让我处于守势,所以来吧!

免责声明:我绝对没有像你一样仔细检查规格.基于你最近的一些问题,你好像最近一直在研究它.这自然会让你比SO上的大多数用户更熟悉很多细节; 所以,就像你从Eric Lippert以外的任何人那里收到的大多数答案一样,这可能不会让你满意.

不同的前提

首先,你的问题的前提是,如果突出显示的陈述是多余的,那么它们就没有用处.我的答案的前提是,如果多余的陈述澄清了对每个人都不明显的东西,那么多余的陈述并不一定没有目的.这些是相互矛盾的前提.如果我们不能就前提达成一致,我们就不能有一个直截了当的逻辑论证.我只是想让你重新思考你的前提.

然而,你的回答是重申你的前提:"如果句子真的是多余的,那么他们只会混淆读者而不澄清任何事情."

(顺便说一句,我喜欢你如何让自己成为所有规范读者的代表.)

我完全不能责怪你担任这个职位.我的意思是,这似乎显而易见.我的原始答案中没有给出任何具体的例子.所以下面我将尝试包含一些具体的例子.但首先,让我退后一步,提出我为什么这个奇怪的身份转换概念首先存在于规范中.

身份转换定义的目的

乍一看,这个定义似乎是多余的; 是不是只是说任何类型T的实例都可以转换为......好吧,到T?是的.但我假设*这个定义的目的是为规范提供适当的词汇,以便在讨论转换的过程中利用类型身份的概念.

这允许有关转换的陈述本质上是可传递的.您从规范中引用的第一点作为重言式声明的一个例子属于这一类.它说,如果隐式转换为某种类型定义(我将它称之为ķ)到另一个类型T 0和T 0 有一个标识转换为 T,则K隐式转换为T.通过定义身份转换给定的上面,"有一个身份转换为"真正意味着"是同一类型." 所以声明是多余的.

但同样重要的是:身份转换定义首先存在于为规范配备描述转换的形式语言,而不必说"如果T 0和T真的是同一类型".

好的,具体例子的时间.

对于某些开发人员而言,隐式转换的存在可能并不明显

注意:Eric Lippert在回答这个问题时提供了一个更好的例子.我把这前两个例子留作我的观点的一点点补充.我还补充说,该具体化之间存在身份转换的第三个例子,并在Eric的答复中指出.objectdynamic

传递参考转换

假设您有两种类型,M并且N,您有一个如下定义的隐式转换:

public static implicit operator M(N n);
Run Code Online (Sandbox Code Playgroud)

然后你可以写这样的代码:

N n = new N();
M m = n;
Run Code Online (Sandbox Code Playgroud)

现在让我们假设你有一个带有此using声明的文件:

using K = M;
Run Code Online (Sandbox Code Playgroud)

然后你在文件的后面有:

N n = new N();
K k = n;
Run Code Online (Sandbox Code Playgroud)

OK,我开始之前,我意识到,这是有目共睹的.但我的回答是,从一开始就是这样,对每个人来说可能并不明显,因此指明它 - 虽然多余 -仍然有目的.

这样做的目的是:为了清楚任何人抓挠他们的头,看看那个代码,这是合法的.存在从N到M 的隐式转换,并且存在从M到K 的身份转换(即,M和K是相同的类型); 因此存在从N到K的隐式转换.它不仅仅是逻辑的(尽管它可能合乎逻辑的); 它就在规范中.否则,人们可能会错误地认为需要以下内容:

K k = (M)n;
Run Code Online (Sandbox Code Playgroud)

显然,事实并非如此.

传递拳击转换

或者采取类型int.一个int可以装箱IComparable<int>,对吧?所以这是合法的:

int i = 10;
IComparable<int> x = i;
Run Code Online (Sandbox Code Playgroud)

现在考虑一下:

int i = 10;
IComparable<System.Int32> x = i;
Run Code Online (Sandbox Code Playgroud)

再次,是的,对于您,我和90%可能遇到过它的开发人员来说,这可能是显而易见的.但对于少数苗条谁没有看到它的时候了:一个装箱转换从存在intIComparable<int>,和身份的转换,从存在IComparable<int>IComparable<System.Int32>(即,IComparable<int>并且IComparable<System.Int32>是同一类型); 所以装箱转换从存在intIComparable<System.Int32>.

涉及dynamic类型的转换

我会从上面我引用转换例如借用,只是调整稍微说明之间的身份关系object,并dynamic在规范的4.0版本.

比方说,我们有种类M<T>N,和在什么地方定义了以下隐式转换:

public static implicit operator M<object>(N n);
Run Code Online (Sandbox Code Playgroud)

那么以下是合法的:

N n = new N();
M<dynamic> m = n;
Run Code Online (Sandbox Code Playgroud)

显然,上述内容远不如前两个例子明显.但这是一个百万美元的问题:即使问题中引用的规范摘录不存在,上述内容仍然合法吗?(为简洁起见,我打算将这些摘录称为Q.)如果答案是肯定的,那么Q实际上是多余的.如果不是,那就不是.

我相信答案是肯定的.

考虑6.1.1节中定义的身份转换的定义(我在这里包括整个部分,因为它很短):

身份转换从任何类型转换为相同类型.存在这种转换,使得已经具有所需类型的实体可以说可转换为该类型.

因为objectdynamic被认为等同之间存在的标识转换objectdynamic,以及构造类型替换的所有出现时是相同的间dynamicobject.[强调我的]

(最后一部分也包含在4.7节中,它定义了dynamic类型.)

现在让我们再看看代码.特别是我对这一行感兴趣:

M<dynamic> m = n;
Run Code Online (Sandbox Code Playgroud)

这句话的合法性(不考虑Q -记住,正在讨论的问题是上述声明的假设的合法性,如果 Q根本存在),因为M<T>N有自定义类型,取决于之间的用户自定义隐式转换的存在NM<dynamic>.

存在从一个隐式转换NM<object>.根据上面引用的规范部分,在M<object>和之间存在身份转换M<dynamic>.通过定义身份转换,M<object>而且M<dynamic> 是同一类型.

因此,正如前两个(更明显)的例子,我相信这是事实,隐式转换从存在NM<dynamic> 即使不考虑Q进去,只是因为它是真的,一个隐式转换,从存在NK第一个例子和该装箱转换从存在intIComparable<System.Int32>在第二个例子.

没有Q,这就不那么明显了(因此Q存在); 但是,这并不使假(即,Q是不是必要的被定义这种行为).它只是使它不那么明显.

结论

我在原来的答案中说,这是"明显的"解释,因为在我看来,你正在吠叫错误的树.你最初提出了这个挑战:

你能给出一个T 1,T 2这两种类型的例子,如果不是上面引用的段落,T 1就不能隐式转换为T 2吗?

没有人会遇到这个挑战,蒂姆维,因为这是不可能的.参考有关参考转换的第一段摘录.假设类型K可以隐式转换为类型T,如果它可以隐式转换为T 0并且T 0与T 相同.解构它,将它重新组合在一起,并且你留下了明显的重言式:K如果它可以隐式转换为T,则可以隐式转换为T.这会引入任何新的隐式转换吗?当然不是.

也许Ben Voigt的评论是正确的; 也许你所询问的这些要点会更好地放在脚注中,而不是放在文本正文中.在任何情况下,我清楚它们多余的,因此从这个前提开始它们不能是多余的,否则它们就不会有傻瓜的差事.愿意接受冗余的陈述可能仍然会对一个对每个人都不太明显的概念有所了解,并且接受这些陈述将变得更容易.

冗余?是.同义反复?是.无意义?在看来,没有.

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


原始答案

我认为你(或许有意)忽略了这里最明显的答案.

在你的问题中考虑这两句话:

(1)最初它们似乎是多余的(同义词).(2)但他们必须在某个目的,所以为什么他们在那里?

对我来说,这两个句子的含义是一个同义词的说法没有任何意义.但是,仅仅因为声明遵循既定的前提逻辑,这并不是每个人都明白的.换句话说,即使(1)为真,对(2)的答案可能只是:使阅读规范的任何人都清楚所描述的行为.

现在你可能会争辩说,即使某些东西不明显,如果提供冗余定义,它仍然不属于规范.对于这种可能的反对意见,我只能说:做到现实.在我看来,梳理一份文件,删除所有只是陈述可以从先前陈述中推断出的事实的陈述,这是不切实际的(在我看来).

如果这一种常见的做法,我认为你会发现很多文献 - 不只是规格,而是研究论文,文章,教科书等 - 会更短,更密集,更难理解.

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


Eri*_*ert 2

规范第 4.7 节指出,存在从Foo<dynamic>到 的身份转换Foo<object>,反之亦然。您引用的规范部分是为了确保处理这种情况而编写的。也就是说,如果存在从 T 到 的隐式引用转换C<object, object>,则还存在到C<object, dynamic>,C<dynamic, object>和的隐式引用转换C<dynamic, dynamic>

人们可能会合理地指出(1)这些短语的意图不明显 - 因此你的问题 - 并且令人困惑,并且(2)有关身份转换的部分应该交叉引用有关动态转换的部分,以及(3)短语规范中的这种情况使得规范的实现者很难将规范语言清楚地转换为实现。如何知道是否存在这种类型?规范不需要指定确切的算法,但如果它能提供更多指导那就太好了。

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