首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >C#中的"is“运算符返回不一致的结果

C#中的"is“运算符返回不一致的结果
EN

Stack Overflow用户
提问于 2015-03-13 18:16:19
回答 2查看 397关注 0票数 16

我想在C#中使用"is“运算符来检查对象实例的运行时类型。但它似乎并不像我预期的那样工作。

假设我们有三个程序集A1、A2和A3,它们都只包含一个类。

A1:

代码语言:javascript
复制
public class C1
{
    public static void Main()
    {
        C2 c2 = new C2();

        bool res1 = (c2.c3) is C3;
        bool res2 = ((object)c2.c3) is C3;
    }
}

A2:

代码语言:javascript
复制
public class C2
{
    public C3 c3 = new C3();
}

A3:

代码语言:javascript
复制
public class C3
{
}

A1需要引用A2和A3。

A2需要引用A3。

运行Main()之后,res1和res2如预期的那样设置为true。当我开始将A3作为强名称程序集进行版本控制,并使A1引用一个版本,使A2引用另一个版本的A3 ( A3的源代码保持不变)时,就会出现问题。顺便说一句。只有当A2引用的A3版本低于或等于A1引用的A3版本时,编译器才允许这样做。这个程序的结果现在不同了(res1 = true,res2 = false)。

这种行为正确吗?它们不应该都是假的(或者可能是真的)吗?

根据C# 5.0规范(第7.10.10章),res1和res2应该以相同的值结束。"is“运算符应该始终考虑实例的运行时类型。

在IL代码中,我可以看到对于res1,编译器判断来自不同A3程序集的两个C3类是相等的,并发出没有isinst指令的代码,只检查null。对于res2编译器增加了isinst指令,推迟了运行时的决定。看起来C#编译器在如何解决这个问题上有不同于CLR运行时的规则。

代码语言:javascript
复制
.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       36 (0x24)
  .maxstack  2
  .locals init ([0] class [A2]C2 c2,
           [1] bool res1,
           [2] bool res2)
  IL_0000:  nop
  IL_0001:  newobj     instance void [A2]C2::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldfld      class [A3]C3 [A2]C2::c3
  IL_000d:  ldnull
  IL_000e:  ceq
  IL_0010:  ldc.i4.0
  IL_0011:  ceq
  IL_0013:  stloc.1
  IL_0014:  ldloc.0
  IL_0015:  ldfld      class [A3]C3 [A2]C2::c3
  IL_001a:  isinst     [A3_3]C3
  IL_001f:  ldnull
  IL_0020:  cgt.un
  IL_0022:  stloc.2
  IL_0023:  ret
} // end of method C1::Main

这是否仅仅是为了在不使用isinst的情况下实现更快和优化的实现而进行的权衡(考虑到编译器警告)?

解决这个问题的可能选择是绑定重定向(正如警告所建议的),但我不能使用它,因为版本可能并不总是向后兼容的(尽管C3类总是向后兼容)。在A2中更改引用也不是我的选择。

编辑:因为似乎最简单的解决办法是始终强制转换为object以获得正确的结果。

无论如何,尽管它仍然可以生成正确的IL代码,但知道它是C#编译器中的错误(并可能将其报告给MS)还是本身不是错误(因为编译器会发现问题并报告警告),这仍然是一件很有趣的事情。

EN

回答 2

Stack Overflow用户

发布于 2015-03-14 05:22:23

不幸的是,我不知道为什么第一个结果是真的。然而,如果规范说is应该基于运行时类型,那么Panagiotis是正确的;类型是不同的,并且两者都应该返回false。GetType()和typeof的行为与is应该一样。

代码语言:javascript
复制
var res3 = c2.c3.GetType() == typeof(C3);              // is false
var res4 = ((object)c2.c3).GetType() == typeof(C3);    // is false

var localC3 = new C3();
var res5 = localC3 is C3;                              // is true
var res6 = ((object)localC3).GetType() == typeof(C3);  // is true

我的下意识反应是去掉对象强制转换,因为它看起来像你想要的那样工作。

然而,如果is是固定的,这种情况可能会改变。您可以求助于以下方法。由于您的代码是针对签名的程序集进行编译的,因此人们将无法替换假的程序集。

代码语言:javascript
复制
var res7 = c3.GetType().FullName == typeof(C3).FullName

希望这能对你有所帮助。

票数 2
EN

Stack Overflow用户

发布于 2015-03-14 05:40:32

你的问题是res1的等式被C#编译器编译成了true (如IL所示)。然而,res2在运行时执行了正确的分析(任何时候你转换成object,它都会迫使C#退回到运行时操作)。

因此,编译器似乎假定这些类型是相同的(可能不会验证组成DLL的版本)。

唯一容易想到的解决方案是更改其中一个C3的别名,看看对您正在谈论的哪个are进行限定是否有帮助。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29029676

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档