【问题标题】:Can a String[] hold System.Object inside it?String[] 可以在其中保存 System.Object 吗?
【发布时间】:2013-07-14 15:40:25
【问题描述】:

你觉得这个问题很奇怪吗?是的,发生的事情也很奇怪。让我解释一下。

我从这个Covariance and Contravariance with C# Arrays找到了一个sn-p

string[] strings = new string[1];
object[] objects = strings;
objects[0] = new object();

Jon skeet 解释说,上面的代码会抛出 ArrayTypeMismatchException,正如所说的那样。

我所做的是我在第 3 行设置了一个断点,使用 DebuggerVisualizer 我手动设置了 objects[0] = new object() 它不会抛出任何错误并且它可以工作。稍后检查 strings[0].GetType() 返回 System.Object。不仅System.Object任何类型都可以通过上述过程在string[]中设置。

我不知道这是怎么发生的

很想知道背后发生了什么。谁能解释一下。

Edit1 这甚至很有趣

重现上述行为后试试这个

int len = strings[0].Length;

如果您将鼠标放在属性长度上,则显示 strings[0].Length threw ArgumentException 并带有消息 Cannot find the method on the object instance 但实际上它不会引发异常并且代码运行会产生结果 len=0

【问题讨论】:

  • +1。我刚刚用 VS2008 对其进行了测试,并且可以确认这种行为。不过,这可能是调试器中的一个错误。
  • @varocarbas:我已经创建了一个屏幕截图,也许这可以解决问题:tinypic.com/r/4kh10z/5。注意立即窗口中执行的内容。
  • @Heinzi 我们不能说这是调试器中的错误。即使是运行时也不应该允许这样做。不是吗?
  • @SriramSakthivel:由于(据我们所知)只能通过使用调试器功能(即时窗口或自动/本地/...窗口)生成(错误)分配,因此它很可能是调试器中的赋值比编译代码的赋值更不安全。这就是为什么我怀疑调试器中存在错误,直到我们找到一种无需使用断点和 VS 调试功能即可重现此行为的方法。
  • 这太酷了。因此,如果您停止并调试,您可以将任何引用类型放入任何其他引用类型变量中。如果程序继续使用string s = strings[0]; Console.WriteLine(s.GetType()); 会发生什么?您可以分离调试器并让程序再次运行,在s 中使用非字符串。

标签: c# arrays covariance


【解决方案1】:

您的示例似乎回答了这个问题:是的,string 引用可以引用非字符串对象。然而,这不是故意的。

考虑一下您在调试器中发现的一个错误

正如 Jon Skeet 在 the answer you mention 中解释的那样,因为 .NET 数组具有这种“疯狂”的协变,即使数组不是只读的而是更像是读写,每次 写入 到数组引用的框架必须检查尝试写入数组的对象的类型,如果您要使用错误的类型,则抛出 ArrayTypeMismatchException,例如将 Cat 的实例分配给 @ 的数组987654325@s(运行时Dog[])已被“疯狂”协方差转换为Animal[]

您所演示的是,当我们使用 Visual Studio 调试器的即时窗口(或类似窗口)时,未完成所需的类型检查,因此这可能导致任何类型 Y(指针除外类型可能)被分配给任何引用类型X的引用类型变量。像这样:

X[] arrayOfX = new X[1];
object[] arrayCastByCrazyCovariance = arrayOfX;
Y badObject = new Y();  // or another constructor or method to get a Y

// Set breakpoint here.
// In Immediate window assign:  arrayCastByCrazyCovariance[0] = badObject
// Detach debugger again.

X anomalousReferenceVariable = arrayOfX[0];

anomalousReferenceVariable.MemberOfX();  // or other bad things

这可以使CatDog 一样吠叫,诸如此类。

在有关绕过类型保护的链接线程中,CodesInChaos 的回答显示了一种不相关的技术,您可以使用该技术将对“错误”且不相关类型的对象的引用放入引用变量中。

【讨论】:

  • 只是对您的陈述进行更正。我使用的调试器(即在代码通过变量后分析变量;在建议的示例中:进入这一行并收到一条弹出消息,指出发现错误)按预期工作。显然不能完美运行(或者,至少与正常执行代码时不完全一样)是即时窗口的这个特性,我个人以前从未使用过它(我做了很多,但很多调试)。我很想对此有更多的了解,但不会得出错误的结论:(空间不足)
  • 被视为实际执行代码的准时通知的调试器运行良好。它的一个特性(甚至不需要使用,并且可以理解为对代码执行后将做什么的估计)有一些错误,或者至少不考虑协方差情况(也许所需实现的复杂性太高,决定不考虑这一点,实际上这个限制是已知的 -> 不知道)。我希望您会同意对您的回答的这种“小”澄清。
  • @varocarbas 我可以确认“错误”仅涉及手动更改数组条目值的功能。如果您只是单步执行代码(在您启动应用程序之前编译好),则不会出现此问题。
  • 好的。那时一切都清楚了。我不是即时窗口用户,所以对我来说没什么大不了的 :)
【解决方案2】:

(我更喜欢重写我的答案,因为上一个更新太多,不够清楚)。

显然,在 VS 调试部分的其中一个工具(即时窗口)中发现了一个不太完美的行为。这种行为不会(完全)影响代码的正常执行,而且纯粹说,甚至不会影响调试过程。

我在上面最后一句中的意思是,当我调试代码时,我从不使用即时窗口,只需编写我想要的任何代码,执行它并查看调试器显示的内容。提到的问题不会影响这个过程(可以称为“调试实际执行的代码”;在建议的示例中,当您在objects[0] = new object(); 上时按 F11),这意味着 VS 中存在严重问题。因此,从我的角度(我所做的调试类型)和执行的角度来看,引用的错误根本没有影响。

此错误的唯一应用是在执行“即时窗口”功能时,这是调试器的一项功能,它在实际交付代码之前估计代码将交付的内容(可能称为“调试未执行的代码”或“估计未执行代码的预期输出”等;在建议的示例中,在线objects[0] = new object();,不按 F11,而是使用即时窗口输入值,让此功能告诉您预期会发生什么)。

总而言之,必须在正确的上下文中理解引用的问题,也就是说,它不是一个全面适用的问题,甚至不是整个调试器的问题(当您在调试器的引用行中按 F11 时) ,它输出一个错误,因此调试器完全理解这种情况是错误的),但只是在它的一个工具中。我什至不确定此工具是否可以接受这种行为(即,“即时窗口”提供的是可能不是 100% 正确的预测;如果您想确定会发生什么,请执行代码并让调试器会显示信息)。

  • 问题:String[] 可以在其中保存 System.Object 吗?
  • 回答:没有。
  • 澄清:协方差是一个复杂的现实,VS 中的某些辅助工具(例如, “立即窗口”),因此在某些情况下, 上述声明并不完全适用。但这是本地的 特定工具中的行为/错误,对实际没有影响 代码的执行。

【讨论】:

  • 在 vs2010 和 vs2012 中可以看到解释的行为
  • 是的,当然;到处。我没有正确理解您的观点;现在我变得更好了;请看看我更新的答案,让我知道你的想法。
  • if (objects[0] == new object()) 是什么意思?一个new object() 不等于另一个new object()。接受两个object== 的重载会进行引用相等测试。
  • 给我几分钟。
  • 好的。我包括的例子不是最好的;我只是想快速支持我的观点,快速做这种事情并不是最好的事情。我的陈述可以自己支持:在发生分配之前,没有任何错误,因此不会触发错误,调试器也不需要显示任何内容。如果有介入并且实际发生了分配,则会出现错误,因此一切都按预期进行。
猜你喜欢
  • 2011-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-30
  • 1970-01-01
  • 2011-08-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多