11

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

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

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

(在 §6.1.6 隐式引用转换中)

隐式引用转换是:

  • [...]
  • 从任何引用类型引用类型 T,如果它具有到引用类型 T0的隐式标识或引用转换,并且T0具有到 的标识转换T

和:

(在 §6.1.7 装箱转换中)

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

最初它们似乎是多余的(同义词)。但他们一定是有目的的,所以他们为什么在那里?

您能否举一个T1T2两种类型的示例,如果不是上面引用的段落,则T1不会隐式转换为2吗?T

4

4 回答 4

6

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 的回答中指出的和之间存在的身份转换。objectdynamic

传递参考转换

假设您有两种类型,Mand 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存在拳击转换,从intto存在身份转换(即,并且IComparable<int>是同一类型);所以存在从到的拳击转换。IComparable<int>IComparable<System.Int32>IComparable<int>IComparable<System.Int32>intIComparable<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 节中定义的身份转换的定义(我在这里包括了整个部分,因为它很短):

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

因为objectand被认为是等价的,所以在 and 之间以及在替换所有出现的with时相同的构造类型dynamic之间存在身份转换。[强调我的]objectdynamicdynamicobject

(最后一部分也包含在第 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也存在隐式转换Nfrom to是正确的,就像在第一个示例中存在 from to的隐式转换是正确的,并且在第二个示例中存在从到的拳击转换。M<dynamic> NKintIComparable<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)的答案也可能只是:让阅读规范的任何人都清楚描述的行为

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

如果这一种常见的做法,我想你会发现很多文献——不仅仅是规范,还有研究论文、文章、教科书等——会更短、更密集、更难理解.

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

于 2010-09-17T18:07:10.793 回答
2

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

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

遗憾的是,该规范并不是一份完美的文件。

于 2010-09-20T04:46:11.353 回答
1

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

这表示在 C#-land 中,1==1; 锹是锹。这是将对象引用分配给相同类型的变量的基础;如果一个类型为 T1 的变量和一个类型为 T2 的变量实际上都是 Spade,则可以将一个分配给另一个,而不必将一个显式转换为 Spade。想象一个 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# 版本,其中必须将纯值类型显式转换为它们的引用模拟,以便将其存储为类的成员变量。例如,想象一个 C# 版本,其中蓝色关键字类型(int、float、decimal)和浅蓝色类名(Int32、Float、Decimal)指的是两个非常不同的、只能显式转换的类型,而 int 、float、decimal 等作为成员变量类型是不合法的,因为它们不是引用类型:

public class MyClass
{
  Int32 MyBoxedValueType; //using "int" not legal
}

...

MyClass myClass = new MyClass();
int theInt = 2;

myClass.MyBoxedValueType = (Int32)theInt; //explicit cast required

我知道这听起来很傻,但在某种程度上,这些事情必须是已知的,而在计算机中,你必须指定一切。有时阅读美国冰球规则手册了解冰球;书中的第一条规则是游戏必须在冰面上进行。这是终极的“好东西”之一,也是游戏的基本真理,必须理解才能使任何其他规则有意义。

于 2010-09-17T16:44:41.350 回答
-1

可能是这样,代码在调用时保证传递,Convert.ChangeType(client, typeof(Client))无论是否IConvertible实现。

使用 Reflector查看ChangeTypefrom的来源,并注意按原样返回的条件。mscorlibvalue

请记住,=运算符不是转换,只是参考集。所以类似代码Client client_2 = client_1不会执行任何隐式转换。如果声明了身份隐式转换,CS0555则会发出错误。

我猜规范说让 C# 编译器处理这种情况,因此不要手动尝试定义身份转换。

于 2010-09-19T05:24:29.110 回答