我有这段代码:
static class Global
{
public static readonly IChannelsData Channels = new ChannelsData();
public static readonly IMessagesData Messages = new MessagesData();
}
我的理解是,因为这个类是静态的,所以Global.Channels
或Global.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);**
}
...
}
我在NullReferenceException
上获得Global.Channels
,我已在即时窗口中确认。让我感到困惑的是,我可以在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 和所讨论的属性是在一个单独的静态类中,而不是在同一个类中。我也无法使用在那里发布的解决方案的解决方法。
完整堆栈跟踪:
在C:\\ Vert \ Slack \ Channel.cs中的Vert.Slack.Channel.Init(StreamingContext上下文):第48行
错误:
对象引用未设置为对象的实例。
答案 0 :(得分:6)
我已经能够通过以下方式重现它:
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关于静态场初始化:
类声明的静态字段变量初始值设定项 对应于在中执行的一系列赋值 它们出现在类声明中的文本顺序。如果一个 静态构造函数(第17.11节)存在于类中,执行 静态字段初始化程序在执行之前立即发生 静态构造函数。否则,静态字段初始值设定项为 在第一次使用之前的执行相关时间执行 该类的静态字段
这是根本原因。有关循环参考
的更多上下文,请参阅注释这实际上意味着我们在初始化程序有机会完成设置之前调用Deserialize
并点击Global.Channels.DoIt(this);
。据我所知,这是静态字段在使用之前无法初始化的唯一方式 - 经过一些测试后,即使使用运行时调度({{1} }),reflection 和 dynamic
(对于后者,初始化是在第一次方法调用时完成的)..
虽然您的代码可能不太明显(例如,链是否被另一个静态类引用启动)。例如,这将导致相同的问题,但不是立即明确:
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();
}
中还有其他内容,则应对其进行调查(如果为简洁起见而将其排除在外)。将Globals
声明移到类的顶部可能很简单。 Channels
任何静态引用,然后按照源代码进行操作。 ChannelsData
中设置断点应该会为堆栈跟踪返回静态初始值设定项。如果不,请尝试通过调用通常被反序列化的DoSomething
来复制该问题。