关于 C# 语言规范中隐式转换的问题
第 6.1 节隐式转换定义了身份转换:
身份转换可以从任何类型转换为相同类型。这种转换的存在使得已经具有所需类型的实体可以说可以转换为该类型。
现在,这些句子的目的是什么?
(在第 §6.1.6 隐式引用转换中)
隐式引用转换是:
- [...]
- 从任何引用类型到引用类型
T
(如果它具有到引用的隐式标识或引用转换) -typeT
0 和T
0 具有到T
T.
和:
(在 §6.1.7 拳击转换中)
- 如果值类型具有到接口类型
I
的装箱转换,则它具有到接口类型I
的装箱转换,并且 < code>I0 具有到I
的恒等转换。
最初它们看起来是多余的(同义反复)。但他们的存在一定是有目的的,那么他们为什么会存在呢?
您能否举出两种类型 T
1、T
2 的示例,使得 T如果不是上述情况,
1将无法隐式转换为T
2 -引用的段落?
Section 6.1 Implicit conversions defines an identity conversion thusly:
An identity conversion converts from any type to the same type. This conversion exists such that an entity that already has a required type can be said to be convertible to that type.
Now, what is the purpose of sentences such as these?
(In §6.1.6 Implicit reference conversions)
The implicit reference conversions are:
- [...]
- From any reference-type to a reference-type
T
if it has an implicit identity or reference conversion to a reference-typeT
0 andT
0 has an identity conversion toT
.
and:
(In §6.1.7 Boxing conversions)
- A value type has a boxing conversion to an interface type
I
if it has a boxing conversion to an interface typeI
0 andI
0 has an identity conversion toI
.
Initially they seem redundant (tautologous). But they must be there for a purpose, so why are they there?
Can you give an example of two types T
1, T
2 such that T
1 would not be implicitly convertible to T
2 if it weren’t for the above-quoted paragraphs?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
2010 年 9 月 22 日更新:
我怀疑除了 Timwi 之外还有人会读到这篇文章。即便如此,我还是想对这个答案进行一些编辑,因为新的答案现已被接受,并且关于规范引用的摘录是否正确的争论仍在继续(至少在我可能想象的世界中)在技术上是多余的。我没有添加太多内容,但内容太多,无法发表评论。大部分更新可以在下面的标题“涉及
动态
类型的转换”下找到。2010 年 9 月 19 日更新:
在您的评论中:
该死的,Timwi,你说了很多次。但好吧,那么;你让我处于守势,所以开始吧!
免责声明:我绝对没有像您一样仔细地检查了该规范。根据您最近提出的一些问题,您似乎最近对此进行了相当多的研究。这自然会让你比 SO 上的大多数用户更熟悉很多细节;因此,就像您可能从埃里克·利珀特以外的任何人那里收到的大多数答案一样,这可能不会令您满意。
不同的前提
首先,您问题的前提是,如果突出显示的陈述是多余的 em>,那么它们就没有任何目的。我的回答的前提是,如果冗余陈述澄清了一些并非每个人都显而易见的内容,那么它们不一定是没有目的的。这些都是矛盾的前提。如果我们不能在前提上达成一致,我们就无法进行简单的逻辑论证。我只是要求你重新考虑你的前提。
然而,你的回应是重申你的前提:“如果这些句子确实是多余的,那么它们只会让读者感到困惑,而不会澄清任何事情。”
(顺便说一句,我喜欢你将自己设置为规范所有读者的代表。)
我不能责怪你担任这个职位。我的意思是,这确实看起来是显而易见的。而且我在原来的回答中没有给出任何具体的例子。所以下面我将尝试包括一些具体的例子。但首先,让我退后一步,谈谈为什么规范中首先存在这种奇怪的身份转换概念。
身份转换定义的目的
乍一看,这个定义似乎相当多余;事实上,这个定义似乎是多余的。这不是说任何类型 T 的实例都可以转换为……好吧,转换为 T 吗?是的。但我假设*该定义的目的是为规范提供适当的词汇,以便在讨论转换的背景下利用类型标识的概念。
这允许有关本质上具有传递性的转换的声明。您从规范中引用的第一点作为同义反复语句的示例就属于此类。它表示,如果为某种类型(我称之为 K)定义了到另一种类型 T0 的隐式转换,并且 T0 具有以下身份转换: T,那么 K 可以隐式转换为 T。根据上面给出的身份转换的定义,“具有身份转换”实际上意味着“与...是相同的类型”。所以这个语句是多余的。
但再说一次:身份转换定义的存在首先是为了给规范配备一种正式的语言来描述转换,而不必说“if T0”之类的东西 和 T 确实是同一类型。”
好的,是时候看具体例子了。
对于某些开发人员来说,隐式转换的存在可能并不明显
注意:Eric Lippert 提供了一个更好示例他对问题的回答 。我留下前两个例子只是对我的观点的次要强化。我还添加了第三个示例,具体化了
object
和dynamic
之间存在的身份转换,如 Eric 的答案中指出的。传递引用转换
假设您有两种类型,
M
和N
,并且您已经定义了如下隐式转换:然后您可以编写如下代码:
现在假设您有一个文件上面有这个
using
语句:然后在文件后面:
好的,在我继续之前,我意识到这对你来说是显而易见的,并且 <强>我。但我的答案是,并且从一开始就是如此,这可能不是对每个人来说都是显而易见的,因此指定它——虽然多余——仍然有目的。
这个目的是:让任何摸不着头脑、查看该代码的人都明白,它是合法的。从N到M存在隐式转换,从M到K存在身份转换(即M和K是同一类型);因此存在从 N 到 K 的隐式转换。这不仅仅是逻辑上的(尽管它可能是逻辑上的); 它就在规范中。否则,人们可能会错误地认为类似以下内容是必要的:
显然,事实并非如此。
传递装箱转换
或者采用
int
类型。int
可以装箱为IComparable
,对吧?所以这是合法的:现在考虑一下:
再次,是的,对于你、我以及 90% 可能遇到过它的开发人员来说,这可能是显而易见的。但对于极少数没有立即看到这一点的人来说:存在从
int
到IComparable
的装箱转换,以及 < em>存在从IComparable
到IComparable
的身份转换(即IComparable
和IComparable
是相同类型);因此存在从int
到IComparable
的装箱转换。涉及
dynamic
类型的转换我将借用上面的引用转换示例,并稍微调整它以说明
object
和dynamic 在规范的 4.0 版本中。
假设我们有类型
M
和N
,并且在某处定义了以下隐式转换:那么以下是合法的:
显然,上面的远不及 <比前面两个例子明显。但这是一个价值百万美元的问题:即使问题中引用的规范摘录不存在,上述仍然是否合法?(我将致电为了简洁起见,摘录了Q。)如果答案是肯定的,那么Q实际上是多余的。如果不是,那就不是。
我相信答案是肯定的。
考虑第 6.1.1 节中定义的身份转换的定义(我将整个部分放在这里,因为它很短):
(最后一部分也包含在第 4.7 节中,它定义了
dynamic
类型。)现在让我们再次看一下代码。我特别对这一行感兴趣:
该声明的合法性(忽略Q - 请记住,正在讨论的问题是上述声明的假设合法性if Q不存在),因为
M
和N
是自定义类型,取决于是否存在N
和M
之间的用户定义隐式转换。存在从
N
到M
因此,就像前两个(更明显的)示例一样,我相信即使没有,也确实存在从
N
到M
的隐式转换。考虑到Q,就像第一个示例中确实存在从N
到K
的隐式转换一样,并且在第二个示例中,存在从int
到IComparable
的装箱转换。如果没有Q,这一点就不太明显(因此Q的存在); 但这并不意味着它是错误的(即,Q 对于定义此行为来说不是必需的)。它只是让它变得不那么明显。
结论
我在最初的回答中说过,这是“显而易见的”解释,因为在我看来,你找错了对象。您最初提出了这个挑战:
蒂姆维,没有人会迎接这个挑战,因为这是不可能的。摘录有关引用转换的第一个摘录。也就是说,如果类型 K 可以隐式转换为 T0 并且 T0 与 T 相同,则类型 K 可以隐式转换为类型 T。解构这个,将其放入回到一起,你会得到一个明显的同义反复:如果 K 可以隐式转换为 T,那么它也可以隐式转换为 T。这会引入任何新的隐式转换吗?当然不是。
所以也许本·沃伊特的评论是正确的;也许您所询问的这些要点最好放在脚注中,而不是放在正文中。无论如何,我很清楚它们是多余的,因此从前提开始它们不能是多余的,否则它们就不会在那里就是开始执行一项愚蠢的任务。愿意接受这样一个事实:冗余的陈述可能仍然可以阐明一个对每个人来说可能并不明显的概念,并且更容易接受这些陈述的本来面目。
多余的?是的。同义反复?是的。无意义? 我认为,不。
*显然,我没有参与编写 C# 语言规范。如果我这样做的话,这个答案就会更加权威。事实上,它只是代表一个人试图理解一份相当复杂的文档的无力尝试。
原始答案
我认为你(也许是故意的)忽略了这里最明显的答案。
考虑你的问题中的这两句话:
对我来说,这两句话放在一起的含义是同义反复的陈述没有任何意义。但仅仅因为一个陈述是从既定前提逻辑上得出的,并不会让每个人都明白这一点。换句话说,即使(1)为真,(2)的答案可能只是:让阅读规范的任何人都清楚所描述的行为< /em>.
现在您可能会争辩说,即使某些内容不是显而易见的,但如果它提供了冗余定义,它仍然不属于规范。对于这种潜在的反对意见,我只能说:现实一点。 (在我看来)梳理一份文件并剔除所有陈述是不太实际的,这些陈述只是陈述可以从先前陈述中推断出的事实。
如果这是一种常见的做法,我想你会发现很多文献——不仅仅是规格,还有研究论文、文章、教科书等——会短很多,更密集,更难理解。
所以:是的,也许它们是多余的。但这并不能否定他们的目的。
Update on 22-Sep-2010:
I doubt anybody is going to read this besides Timwi. Even so, I wanted to make a few edits to this answer in light of the fact that a new answer has now been accepted and the debate still continues (at least in my perhaps imaginary world) on whether or not the quoted excerpts of the spec are technically redundant. I am not adding much, but it's too substantial to fit in a comment. The bulk of the update can be found under the heading "Conversion involving the
dynamic
type" below.Update on 19-Sep-2010:
In your comment:
Damn, Timwi, you say that a lot. But all right, then; you've put me on the defensive, so here goes!
Disclaimer: I have definitely not examined the spec as closely as you have. Based on some of your recent questions it seems like you've been looking into it quite a bit lately. This is naturally going to make you more familiar with a lot of details than most users on SO; so this, like most answers you're likely to receive from anyone other than Eric Lippert, may not satisfy you.
Different premises
Firstly, the premise of your question is that if the statements highlighted are redundant, then they serve no purpose. My answer's premise is that redundant statements are not necessarily without purpose if they clarify something that isn't obvious to everyone. These are contradictory premises. And if we can't agree on premises, we can't have a straightforward logical argument. I was simply asking you to rethink your premise.
Your response, however, was to reiterate your premise: "If the sentences are truly redundant, then they only confuse the reader and don't clarify anything."
(I like how you set yourself up as the representative for all readers of the spec there, by the way.)
I can't blame you for holding this position, exactly. I mean, it does seem obvious. And I didn't give any concrete examples in my original answer. So below I will try to include some concrete examples. But first, let me take a step back and offer my take on why this weird identity conversion concept exists in the spec in the first place.
The purpose of the identity conversion definition
Upon first glance, this definition seems rather superfluous; isn't it just saying that an instance of any type T is convertible to ... well, to T? Yes, it is. But I hypothesize* that the purpose of this definition is to provide the spec with the proper vocabulary to utilize the concept of type identity in the context of discussing conversions.
This allows for statements about conversions which are essentially transitive in nature. The first point you quoted from the spec as an example of a tautological statement falls into this category. It says that if an implicit conversion is defined for some type (I'll call it K) to another type T0 and T0 has an identity conversion to T, then K is implicitly convertible to T. By the definition of identity conversion given above, "has an identity conversion to" really means "is the same type as." So the statement is redundant.
But again: the identity conversion definition exists in the first place to equip the spec with a formal language for describing conversions without having to say things like "if T0 and T are really the same type."
OK, time for concrete examples.
Where the existence of an implicit conversion might not be obvious to some developers
Note: A much better example has been provided by Eric Lippert in his answer to the question. I leave these first two examples as only minor reinforcements of my point. I have also added a third example that concretizes the identity conversion that exists between
object
anddynamic
as pointed out in Eric's answer.Transitive reference conversion
Let's say you have two types,
M
andN
, and you've got an implicit conversion defined like this:Then you can write code like this:
Now let's say you've got a file with this
using
statement up top:And then you have, later in the file:
OK, before I proceed, I realize that this is obvious to you and me. But my answer is, and has been from the beginning, that it might not be obvious to everyone, and therefore specifying it--while redundant--still has a purpose.
That purpose is: to make clear to anyone scratching his or her head, looking at that code, it is legal. An implicit conversion exists from N to M, and an identity conversion exists from M to K (i.e., M and K are the same type); so an implicit conversion exists from N to K. It isn't just logical (though it may be logical); it's right there in the spec. Otherwise one might mistakenly believe that something like the following would be necessary:
Clearly, it isn't.
Transitive boxing conversion
Or take the type
int
. Anint
can be boxed as anIComparable<int>
, right? So this is legal:Now consider this:
Again, yes, it may be obvious to you, me, and 90% of all developers who might ever come across it. But for that slim minority who don't see it right away: a boxing conversion exists from
int
toIComparable<int>
, and an identity conversion exists fromIComparable<int>
toIComparable<System.Int32>
(i.e.,IComparable<int>
andIComparable<System.Int32>
are the same type); so a boxing conversion exists fromint
toIComparable<System.Int32>
.Conversion involving the
dynamic
typeI'm going to borrow from my reference conversion example above and just tweak it slightly to illustrate the identity relation between
object
anddynamic
in version 4.0 of the spec.Let's say we have the types
M<T>
andN
, and have defined somewhere the following implicit conversion:Then the following is legal:
Clearly, the above is far less obvious than the two previous examples. But here's the million-dollar question: would the above still be legal even if the excerpts from the spec quoted in the question did not exist? (I'm going to call these excerpts Q for brevity.) If the answer is yes, then Q is in fact redundant. If no, then it is not.
I believe the answer is yes.
Consider the definition of identity conversion, defined in section 6.1.1 (I am including the entire section here as it is quite short):
(This last part is also included in section 4.7, which defines the
dynamic
type.)Now let's look at the code again. In particular I'm interested in this one line:
The legality of this statement (disregarding Q -- remember, the issue being discussed is the hypothetical legality of the above statement if Q did not exist), since
M<T>
andN
are custom types, depends on the existence of a user-defined implicit conversion betweenN
andM<dynamic>
.There exists an implicit conversion from
N
toM<object>
. By the section of the spec quoted above, there is an identity conversion betweenM<object>
andM<dynamic>
. By the definition of identity conversion,M<object>
andM<dynamic>
are the same type.So, just as in the first two (more obvious) examples, I believe it is true that an implicit conversion exists from
N
toM<dynamic>
even without taking Q into account, just as it is true that an implicit conversion exists fromN
toK
in the first example and that a boxing conversion exists fromint
toIComparable<System.Int32>
in the second example.Without Q, this is much less obvious (hence Q's existence); but that does not make it false (i.e., Q is not necessary for this behavior to be defined). It just makes it less obvious.
Conclusion
I said in my original answer that this is the "obvious" explanation, because it seemed to me you were barking up the wrong tree. You initially posed this challenge:
No one's going to meet this challenge, Timwi, because it's impossible. Take the first excerpt about reference conversions. It is saying that a type K is implicitly convertible to a type T if it is implicitly convertible to T0 and T0 is the same as T. Deconstruct this, put it back together, and you're left with an obvious tautology: K is implicitly convertible to T if it's implicitly convertible to T. Does this introduce any new implicit conversions? Of course not.
So maybe Ben Voigt's comment was correct; maybe these points that you're asking about would've been better placed in footnotes, rather than in the body of the text. In any case, it's clear to me that they are redundant, and so to start with the premise they cannot be redundant, or else they wouldn't be there is to embark on a fool's errand. Be willing to accept that a redundant statement may still shed some light on a concept that may not be obvious to everyone, and it will become easier to accept these statements for what they are.
Redundant? Yes. Tautologous? Yes. Pointless? In my opinion, no.
*Obviously, I did not have any part in writing the C# language specification. If I did, this answer would be a lot more authoritative. As it is, it simply represents one guy's feeble attempt to make sense of a rather complex document.
Original answer
I think you're (perhaps intentionally) overlooking the most obvious answer here.
Consider these two sentences in your question:
To me, the implication of these two sentences together is that a tautologous statement serves no purpose. But just because a statement follows logically from established premises, that does not make it obvious to everyone. In other words even if (1) is true, the answer to (2) may simply be: to make the described behavior clear to anyone reading the spec.
Now you might argue that even if something is not obvious, it still does not belong in a specification if it is providing a redundant definition. To this potential objection, I can only say: be realistic. It's not really practical (in my opinion) to comb through a document stripping out all statements which are simply stating facts that could have been deduced from prior statements.
If this were a common practice, I think you'd find a lot of literature out there -- not just specs, but research papers, articles, textbooks, etc. -- would be a lot shorter, denser, and more difficult to understand.
So: yes, perhaps they are redundant. But that does not negate their purpose.
规范第 4.7 节指出,存在从
Foo
到Foo的身份转换,反之亦然。您引用的规范部分是为了确保处理这种情况而编写的。也就是说,如果存在从 T 到
C
的隐式引用转换,则还存在到C
, < code>C<动态,对象> 和C<动态,动态>
。人们可能会合理地指出(1)这些短语的意图不明显 - 因此你的问题 - 并且令人困惑,并且(2)有关身份转换的部分应该交叉引用有关动态转换的部分,以及(3)短语规范中的这种情况使得规范的实现者很难将规范语言清楚地转换为实现。如何知道是否存在这种类型?规范不需要指定确切的算法,但如果它能提供更多指导那就太好了。
遗憾的是,该规范并不是一个完美的文档。
Section 4.7 of the specification notes that there is an identity conversion from
Foo<dynamic>
toFoo<object>
and vice versa. The portion of the spec you quoted is written to ensure that this case is handled. That is, if there is an implicit reference conversion from T toC<object, object>
then there is also an implicit reference conversion toC<object, dynamic>
,C<dynamic, object>
andC<dynamic, dynamic>
.One might reasonably point out that (1) the intention of these phrases is unobvious - hence your question - and confusing, and (2) that the section on identity conversions ought to cross-reference the section on dynamic conversions, and (3) phrases like this in the spec make it difficult for an implementor of the specification to clearly translate the spec language into an implementation. How is one to know if any such type exists? The spec need not specify exact algorithms, but it would be nice if it gave more guidance.
The spec is, sadly, not a perfect document.
这表示在 C# 领域,1==1;黑桃就是黑桃。这是将对象引用分配给相同类型的变量的基础;如果一个类型为 T1 的变量和一个类型为 T2 的变量实际上都是 Spade,则可以将一个变量分配给另一个变量,而无需将其中一个显式转换为 Spade。想象一个 C# 变体,其中赋值必须如下所示:
此外,数学中的“恒等式”指出,在给定一组输入的情况下求出结果的表达式相当于在给定相同输入的情况下生成相同结果的另一个表达式。在编程中,这意味着计算结果为类型实例的表达式或函数与该类型等效,无需显式转换。如果这不成立,则需要以下代码:
第二条规则基本上说,如果成员变量(根据定义是装箱类型,因为其容器是引用类型)被声明为相同类型。如果未指定此规则,理论上,可能存在一个 C# 版本,其中纯值类型必须显式转换为其引用模拟,以便作为成员变量存储在类中。例如,想象一下 C# 的一个版本,其中蓝色关键字类型(int、float、decimal)和浅蓝色类名(Int32、Float、Decimal)引用两种截然不同的、只能显式转换的类型,而 int 、 float、decimal 等作为成员变量类型是不合法的,因为它们不是引用类型:
我知道这听起来很愚蠢,但在某种程度上,这些东西必须是已知的,在计算机中,你必须指定一切。有时阅读美国冰球协会的冰球规则手册;书中的第一条规则是比赛必须在冰面上进行。这是终极的“好吧”之一,但也是游戏的基本真理,必须理解它才能使任何其他规则有意义。
This says that in C#-land, 1==1; a Spade is a Spade. This is the basis of assigning an object reference to a variable of the same type; if a variable typed T1 and one typed T2 are in reality both Spades, it is possible to assign one to the other without having to explicitly cast one as a Spade. Imagine a C# variant where assignments had to look like this:
Also, an "identity" in mathematics states that an expression that evaluates to a result given a set of inputs is equivalent to another expression that produces the same result given the same inputs. In programming, this means that an expression or function that evaluates to an instance of a type is equivalent to that type, without explicit conversion. If that didn't hold, the following code would be required:
The second rule basically says that a value type can be assigned to a member variable on a class if, in part, the member variable (a boxed type by definition, as its container is a reference type) is declared to be the same type. If this rule were not specified, theoretically, a version of C# could exist in which pure value types would have to be explicitly converted to their reference analog in order to be stored as a member variable on a class. Imagine, for example, a version of C# in which the blue keyword types (int, float, decimal) and the light blue class names (Int32, Float, Decimal) referred to two very different, only-explicitly-convertible types, and int, float, decimal etc. were not legal as member variable types because they were not reference types:
I know it sounds silly, but at some level, these things must be known, and in computers, you have to specify EVERYTHING. Read the USA Hockey rulebook for ice hockey sometime; the very first rule in the book is that the game shall be played on an ice surface. It's one of the ultimate "well duhs", but also a fundamental truth of the game that must be understood in order for any other rule to make sense.
愿代码在像
Convert.ChangeType(client, typeof(Client))
那样调用时保证传递,无论是否实现了IConvertible
。使用 Reflector 查看
mscorlib
中ChangeType
的源代码,并注意按原样返回value
的条件。请记住,
=
运算符不是转换,只是一个引用集。因此,像Client client_2 = client_1
这样的代码不会执行任何隐式转换。如果声明了身份隐式转换,则会出现错误CS0555
已发布。我猜规范说让 C# 编译器处理此类情况,因此不要手动尝试定义身份转换。
May it is such that the code guarantees pass-through when called like
Convert.ChangeType(client, typeof(Client))
regardless ifIConvertible
is implemented.Look into the source of
ChangeType
frommscorlib
with Reflector and notice the conditions at whichvalue
is returned as-is.Remember a
=
operator is not a conversion, just a reference set. So code likeClient client_2 = client_1
does not perform any implicit conversions. If an identity implicit conversion is declared then errorCS0555
is issued.I guess the spec says let the C# compiler handle such cases, and thus dot not manually try to define identity conversions.