草庐IT

c# - 具有循环依赖的静态字段的反射 GetValue 返回 null

coder 2024-05-30 原文

注意:以下代码实际上工作正常,但显示了我自己的解决方案中失败的场景。有关详细信息,请参阅本文底部。

有了这些类:

public class MainType {
   public static readonly MainType One = new MainType();
   public static readonly MainType Two = SubType.Two;
}

public sealed class SubType : MainType {
   public new static readonly SubType Two = new SubType();
}

获取字段 OneTwo:

List<FieldInfo> fieldInfos = typeof(MainType)
   .GetFields(BindingFlags.Static | BindingFlags.Public)
   .Where(f => typeof(MainType).IsAssignableFrom(f.FieldType))
   .ToList();

最后,获取它们的值:

List<MainType> publicMainTypes = fieldInfos
   .Select(f => (MainType) f.GetValue(null))
   .ToList();

在 LinqPad 或带有上述代码的简单单元测试类中,一切正常。但是在我的解决方案中,我有一些单元测试想要使用这些字段的所有实例,GetValue 可以很好地返回父类型的字段,但是父字段应该有实例子类型,他们总是给出 null! (如果发生这种情况,最终列表将是 { One, null } 而不是 { One, Two } 。)测试类与两个不同的项目类型(每个都在自己的文件中),但我暂时将一切公开。我在其中放置了一个断点并检查了所有我可以检查的内容,并在 Watch 表达式中完成了与 fieldInfos[1].GetValue(null) 等效的操作,它实际上返回了 null,尽管事实上,我的主类中有一行与上面 MainType 中的第二行完全相同。

怎么了?如何获取子类型字段的所有值?他们怎么可能在没有错误的情况下返回 null?

根据理论,也许由于某种原因,由于通过反射访问,子类型的类没有被静态构造,我尝试了

System.Runtime.CompilerServices.RuntimeHelpers
  .RunClassConstructor(typeof(SubType).TypeHandle);

在开始之前位于顶部,但它没有帮助(其中 SubType 是我项目中的实际子类型类)。

我会继续努力尝试在一个简单的案例中重现这一点,但我暂时没有想法。

附加信息

经过一堆摆弄,代码开始工作了。现在它不再工作了。我正在努力重现触发代码开始工作的内容。

注意:在 Visual Studio 2015 中使用 C# 6.0 以 .Net 4.6.1 为目标。

问题重现可用

你可以通过下载这个 somewhat minimal working example of the problem at github 来玩我的场景的工作(失败)精简版本.

调试单元测试。出现异常时,单步执行到GlossaryHelper.cs的第20行,可以在Locals标签中看到GetGlossaryMembers的返回值。您可以看到索引 3 到 12 为空。

最佳答案

问题

这个问题与反射无关,而是两个静态字段初始化器之间的循环依赖及其执行顺序。

考虑以下片段:

var b = MainType.Two;
var a = SubType.Two;
Debug.Assert(a == b); // Success

现在让我们交换前两行:

var a = SubType.Two;
var b = MainType.Two;
Debug.Assert(a == b); // Fail! b == null

那么这里发生了什么?让我们看看:

  1. 代码第一次尝试访问 SubType.Two 静态字段。
  2. 静态初始化程序触发并执行SubType 的构造函数。
  3. 由于SubType继承自MainTypeMainType构造函数也会执行并触发MainType静态初始化。
  4. MainType.Two 字段静态初始值设定项正在尝试访问 SubType.Two。由于静态初始化程序只执行一次,而 SubType.Two 的初始化程序已经执行(好吧,不是真的,它当前正在执行,但被认为是),它只是返回当前字段值( null),然后存储在 MainType.Two 中,并将由对该字段的进一步访问请求返回。

简而言之,这种设计的正确工作实际上取决于外部访问字段的顺序,所以它有时有效,有时无效也就不足为奇了。不幸的是,这是您无法控制的。

如何修复

如果可能,避免这种静态字段依赖。请改用 static readonly properties。它们为您提供完全控制,还允许您消除字段重复(目前您有 2 个不同的字段,它们包含一个相同的值)。

这是没有此类问题的等效设计(使用 C#6.0):

public class MainType
{
    public static MainType One { get; } = new MainType();
    public static MainType Two => SubType.Two;
}

public sealed class SubType : MainType
{
    public new static SubType Two { get; } = new SubType();
}

当然,这需要更改您的反射代码以使用 GetProperties 而不是 GetFields,但我认为这是值得的。

更新:另一种解决问题的方法是将静态字段移动到嵌套的抽象容器类中:

public class MainType
{
    public abstract class Fields
    {
        public static readonly MainType One = new MainType();
        public static readonly MainType Two = SubType.Fields.Two;
    }
}

public sealed class SubType : MainType
{
    public new abstract class Fields : MainType.Fields
    {
        public new static readonly SubType Two = new SubType();
    }
}

现在两个测试都成功完成:

var a = SubType.Fields.Two;
var b = MainType.Fields.Two;
Debug.Assert(a == b); // Success

var b = MainType.Fields.Two;
var a = SubType.Fields.Two;
Debug.Assert(a == b); // Success

这是因为容器类除了被包含在之外,与静态字段类型没有关系,因此它们的静态初始化是独立的。此外,虽然它们使用继承,但它们永远不会被实例化(因为是抽象的),因此没有由基本构造函数调用引起的副作用。

关于c# - 具有循环依赖的静态字段的反射 GetValue 返回 null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40182648/

有关c# - 具有循环依赖的静态字段的反射 GetValue 返回 null的更多相关文章

  1. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  2. ruby - 树顶语法无限循环 - 2

    我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

  3. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  4. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  5. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  6. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  7. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

    我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

  8. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  9. ruby-on-rails - 在 Rails 和 ActiveRecord 中查询时忽略某些字段 - 2

    我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr

  10. ruby - 检查字符串是否包含散列中的任何键并返回它包含的键的值 - 2

    我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案

随机推荐