【问题标题】:Strange generic method call with explicit generic type behaviour具有显式泛型类型行为的奇怪泛型方法调用
【发布时间】:2014-12-30 12:06:45
【问题描述】:

我有一个带有泛型方法的非泛型类。此方法的泛型类型定义了输出类型,无法从使用中推断出来,因此我必须显式提供泛型类型。有时这种类型是从泛型调用者的方法类型参数传递的,但在一种情况下,我必须自己显式提供它。

问题是当我调用我显式提供的泛型类型方法调用时,它似乎没有被执行并返回一个完全不相关的类型。我无法调试此调用并获得无效结果。但它并没有破坏特别奇怪的执行。当从调用者的泛型方法类型传递泛型类型的其他地方调用相同的方法时,一切似乎都按照定义工作。

我完全不知道发生了什么。

我在接口中的方法定义(并在后面的类中实现):

TRecord Update<TRecord>(int recordId, int? categoryId, string categoryName, string title)
    where TRecord : Record;

我的Record 类是非抽象的,并且只有一种类型继承自它:

public class Record : ProtectedEntity
{
    ...
}

public class RelatedRecord<T> : Record
{
    public IList<T> Related { get; private set; }
    ...
}

我正在调用我的方法:

var record = myRepo.Update<Record>(...);

当执行到这一行时,我按 F11 进行调试,但执行只是跳转到下一句。当我检查我的record 变量时,它不是Record 类型,而是System.String,具有参数categoryName 的值。这意味着某些东西确实被执行了,但它绝对不是我的通用方法的主体。

奇怪的是,在其他任何地方,相同的调用都按预期工作。

如何解释,我做错了什么?

【问题讨论】:

  • 看起来很好奇。你能把你的一些代码提取到 MCVE(最小的完整可验证示例)吗?
  • 在编译时记录的类型是什么?当您尝试在运行时使用record 访问Recordstring 的成员时会发生什么?
  • record.GetType().FullName 返回 System.String 吗?
  • @MarcGravell:是的,它确实返回了System.String。 :S
  • @Robert k;你也可以检查typeof(Record).FullName吗?那是什么?

标签: c# generics generic-method


【解决方案1】:

根据你所说的,包括评论:

如果我尝试在运行时访问任何成员,它当然会引发关于不存在成员的异常。

这听起来像是编译器错误或 JIT 错误。这意味着要诊断它,我们需要更多地了解您正在使用的编译器(确切版本)和/或您正在使用的 JIT。在编译器错误的情况下,可能只要你能简单地显示已经生成的 IL 就足够了,如果你熟悉的话。

请注意,最新的 VS 预览版包含一个全新的 JIT (RyuJIT),并且默认在系统范围内启用它,所以如果您安装了 VS 预览版,那将是我的猜测。如果是这样,您可以简单地禁用它来检查。


注意这里的另一个选项类似于using 别名,将Record 别名为System.String,或将var 别名为System.String(加上一些隐式转换运算符等)。这些不太可能,但我已经看到它发生了;p

编辑:您的评论排除了上述情况:

执行 typeof(Record).FullName 正确返回我的完整类型名称,包括应有的命名空间。

所以我们留下了编译器错误或 JIT 错误。


如果这是一个 RyuJIT 错误,以下是禁用它的选项(我建议先使用配置,因为它最容易做到):

  1. 作为环境变量:设置 COMPLUS_useLegacyJit=1

  2. 在注册表中:设置 HKLM 或 HKCU,Software\Microsoft.NETFramework。键名:useLegacyJit。类型:REG_DWORD。值:1

  3. 在 app.exe.config 文件中:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <runtime>
        <useLegacyJit enabled="1" />
      </runtime>
    </configuration>
    

(这些来自我之前与 MS 的单独讨论)

【讨论】:

  • 我正在使用 Visual Studio Premium 2013 Update 4。如果我打开 VS2012 命令提示符(虽然我没有安装 VS 2012 并且没有匹配 2013 的命令提示符)并输入 csc 它说:Microsoft (R) Visual C# Compiler version 4.0.30319.33440 for Microsoft (R) .NET Framework 4.5
  • @RobertKoritnik 你能把它简化成最小的可重现代码吗?我很想深入了解一下,但这需要重新制作
  • @RobertKoritnik 另一个有趣的尝试:使项目显式 x86(不是 AnyCPU)并尝试,然后使其显式 x64 并尝试;两者都失败了吗?还是只有一个?
  • @RobertKoritnik 同样,您实际上已经回答了这个问题:x64 与 x86 测试几乎旨在帮助澄清这一点;它强烈表明该错误存在于 x64 JIT 中;无论哪种方式,IL 都应该相同。如果两者都失败,那么可能编译器(虽然它可能是两个 JIT 共有的错误,但这种情况很少见)
  • @RobertKoritnik 如果这是一个 RyuJIT 问题(可能来自 azure 工具),我已经包含(参见编辑)禁用它的说明
【解决方案2】:

如果您没有收到运行时类型异常,我可能会怀疑我在 Visual Studio 调试器中遇到了错误。如果在同一个方法中有两个同名的局部作用域变量,调试器会混淆它们:

private void DoSomething(int x)
{
    {
        var s = "STRING!";
        Console.WriteLine(s);
    }
    {
        var s = 5;
        Console.WriteLine(s);
    }
}

我不知道上面的示例是否真的会重现该问题,但我已经看到调试器的行为,您将鼠标悬停在第二个 s 上,它显示为“STRING!”,而不是 5。那就是我要检查的第一件事是,在同一个名为“record”的方法中没有另一个局部变量。

【讨论】:

  • 不幸的是,在执行方法的作用域中只有一个同名的变量。
猜你喜欢
  • 1970-01-01
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
  • 2015-12-28
  • 2019-05-25
  • 1970-01-01
  • 1970-01-01
  • 2018-08-28
相关资源
最近更新 更多