草庐IT

c# - 静态属性赋值后为空

coder 2024-05-20 原文

我有这个代码:

static class Global
{
    public static readonly IChannelsData Channels = new ChannelsData();
    public static readonly IMessagesData Messages = new MessagesData();
}

我的理解是,因为这个类是静态的,所以 Global.ChannelsGlobal.Messages 不可能为空,因为它们已经被赋予了一个实例.

但是,我尝试访问该属性

public class Channel : IComparable
{

    ...

    private SortedList<string, Message> _messages;

    [JsonConstructor]
    public Channel()
    {
        _messages = new SortedList<string, Message>();
    }

    [OnDeserialized]
    private void Init(StreamingContext context)
    {
        **Global.Channels.RegisterChannel(this);**
    }  

    ...

}

我在 Global.Channels 上收到了一个 NullReferenceException,我已经在即时窗口中确认了这一点。让我更加困惑的是,我可以在 new ChannelData() 处设置断点,因此我知道静态成员在某个时间点被成功填充。

更多上下文,评论请求:

    private Hashtable _channels;

    public ChannelsData()
    {
        _channels = new Hashtable();

        foreach(Channel channel in SlackApi.ChannelList())
        {
            _channels.Add(channel.GetHashCode(), channel);
        }
    }

感觉和问题类似here .但是,在我的情况下,我使用 JSON.NET 而不是 WCF 进行反序列化,所讨论的属性在一个单独的静态类中,而不是在同一个类中。我也不能使用那里发布的解决方案的解决方法。

完整的堆栈跟踪:

at Vert.Slack.Channel.Init(StreamingContext context) in C:\\Vert\Slack\Channel.cs:line 48

错误:

Object reference not set to an instance of an object.

最佳答案

我已经能够通过以下方式重现它:

class Program
{
    static void Main(string[] args)
    {
        var m = Global.Messages;
    }
}
[Serializable]
public class Blah
{
    [OnDeserialized]
    public void DoSomething(StreamingContext context)
    {
        Global.Channels.DoIt(this);
    }
}
static class Global
{
    private static Blah _b = Deserialize();

    public static readonly IChannelsData Channels = new ChannelsData();
    public static readonly IMessagesData Messages = new MessagesData();

    public static Blah Deserialize()
    {
        var b = new Blah();
        b.DoSomething(default(StreamingContext));
        return b;
    }
}

本质上,执行顺序是:

var m = Global.Messages; 导致静态初始化程序为 Global 运行。

根据关于静态字段初始化的 ECMA-334:

The static field variable initializers of a class declaration correspond to a sequence of assignments that are executed in the textual order in which they appear in the class declaration. If a static constructor (§17.11) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class

这是根本原因。有关循环引用的更多上下文,请参阅评论

这实际上意味着我们正在调用 Deserialize 并在初始化器有机会完成设置之前点击 Global.Channels.DoIt(this);。据我所知,这是静态字段在使用前无法初始化的唯一方式 - 经过一些测试,即使在使用运行时分派(dispatch)(动态)、反射 GetUninitializedObject(然而,对于后者,初始化是在第一次方法调用时完成的)。

虽然您的代码可能不太容易诊断(例如,如果链被另一个静态类引用启动)。例如,这会导致同样的问题,但不是立即清楚:

class Program
{
   static void Main(string[] args)
   {
       var t = Global.Channels;
   }
}
[Serializable]
public class Blah
{
   [OnDeserialized]
   public void DoSomething(StreamingContext context)
   {
       Global.Channels.DoIt();
   }
}

public interface IChannelsData { void DoIt(); }
class ChannelsData : IChannelsData
{
    public static Blah _b = Deserialize();
    public static Blah Deserialize()
    {
        var b = new Blah();
        b.DoSomething(default(StreamingContext));
        return b;
    }
    public void DoIt() 
    {
        Console.WriteLine("Done it");
    }
}

static class Global
{
    public static readonly IChannelsData Channels = new ChannelsData();
    public static readonly IMessagesData Messages = new MessagesData();  
}

所以:

  1. 如果您在 Globals 中的这些字段之前有任何其他内容,您应该调查它们(如果为简洁起见将它们遗漏)。将 Channels 声明移动到类的顶部可能很简单。
  2. 检查ChannelsData 是否有任何 静态引用,并按照这些引用找到源。
  3. DoSomething 中设置一个断点应该会给您一个返回到静态初始值设定项的堆栈跟踪。如果它没有,请尝试通过调用 new Blah(default(StreamingContext)) 来重现该问题,它通常会被反序列化。

关于c# - 静态属性赋值后为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34259304/

有关c# - 静态属性赋值后为空的更多相关文章

  1. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  2. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  3. ruby - 多个属性的 update_column 方法 - 2

    我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2

  4. ruby - Nokogiri 剥离所有属性 - 2

    我有这个html标记:我想得到这个:我如何使用Nokogiri做到这一点? 最佳答案 require'nokogiri'doc=Nokogiri::HTML('')您可以通过xpath删除所有属性:doc.xpath('//@*').remove或者,如果您需要做一些更复杂的事情,有时使用以下方法遍历所有元素会更容易:doc.traversedo|node|node.keys.eachdo|attribute|node.deleteattributeendend 关于ruby-Nokog

  5. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

    对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

  6. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  7. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  8. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  9. ruby - Chef Ruby 遍历 .erb 模板文件中的属性 - 2

    所以这可能有点令人困惑,但请耐心等待。简而言之,我想遍历具有特定键值的所有属性,然后如果值不为空,则将它们插入到模板中。这是我的代码:属性:#===DefaultfileConfigurations#default['elasticsearch']['default']['ES_USER']=''default['elasticsearch']['default']['ES_GROUP']=''default['elasticsearch']['default']['ES_HEAP_SIZE']=''default['elasticsearch']['default']['MAX_OP

  10. ruby - 获取数组中的值并最小化某个类属性的最优雅的方法是什么? - 2

    假设我有以下类(class):classPersondefinitialize(name,age)@name=name@age=ageenddefget_agereturn@ageendend我有一组Person对象。是否有一种简洁的、类似于Ruby的方法来获取最小(或最大)年龄的人?如何根据它对它们进行排序? 最佳答案 这样做会:people_array.min_by(&:get_age)people_array.max_by(&:get_age)people_array.sort_by(&:get_age)

随机推荐