什么会导致此属性偶尔引发 NullReferenceException
本文关键字:偶尔 NullReferenceException 属性 什么 | 更新日期: 2023-09-27 18:32:23
我有一个 asp.net/C# 类,它调整图像大小以在服务器上缓存为文件,但是确定使用哪个编码器的代码部分似乎偶尔会抛出 NullReferenceException。
以下是初始化并传回编码器的代码:
public static class ImageUtilities{
private static Dictionary<string, ImageCodecInfo> encoders = null;
public static Dictionary<string, ImageCodecInfo> Encoders{
get{
if (encoders == null){
encoders = new Dictionary<string, ImageCodecInfo>();
}
//if there are no codecs, try loading them
if (encoders.Count == 0){
foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders()){
encoders.Add(codec.MimeType.ToLower(), codec);
}
}
return encoders;
}
}
...
这是引发异常的特定行:
encoders.Add(codec.MimeType.ToLower(), codec);
这是错误文本:
Object reference not set to an instance of an object.
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
这是调用 Encoders 属性的唯一位置(随后是堆栈跟踪中该属性下方的行):
if (Encoders.ContainsKey(lookupKey)){
foundCodec = Encoders[lookupKey];
}
即使 lookupKey 为 null,查找不应该只返回 null 而不是抛出异常吗?
您正在尝试使用"延迟加载的单例",但没有考虑并发性。 在不牺牲性能的情况下执行此操作的最简单方法是使用 Lazy<T>
:
private static Lazy<Dictionary<string, ImageCodecInfo>> _encoders =
new Lazy<Dictionary<string, ImageCodecInfo>>(() =>
ImageCodecInfo.GetImageEncoders().ToDictionary(x => x.MimeType.ToLower(), x => x));
public static Dictionary<string, ImageCodecInfo> Encoders
{
get { return _encoders.Value; }
}
这是 Jon Skeet 关于实现此模式的各种方法的优秀文章的模式 #6。
您还可以考虑使用只读词典,以防止任何调用方尝试向其添加内容。
private static Lazy<ReadOnlyDictionary<string, ImageCodecInfo>> _encoders =
new Lazy<ReadOnlyDictionary<string, ImageCodecInfo>>(() =>
new ReadOnlyDictionary<string, ImageCodecInfo>(
ImageCodecInfo.GetImageEncoders()
.ToDictionary(x => x.MimeType.ToLower(), x => x)));
public static IReadOnlyDictionary<string, ImageCodecInfo> Encoders
{
get { return _encoders.Value; }
}
您可能处理此问题的另一种方法是使用 ConcurrentDictionary
,但这似乎是矫枉过正,因为您不会经常添加项目。
由于此代码位于 ASP.NET 应用中,因此并发性可能存在一些问题。尝试将创建字典 int lock
语句:
private static object _lock = new object();
public static Dictionary<string, ImageCodecInfo> Encoders{
get{
lock(_lock) {
if (encoders == null){
encoders = new Dictionary<string, ImageCodecInfo>();
}
//if there are no codecs, try loading them
if (encoders.Count == 0){
foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders()){
encoders.Add(codec.MimeType.ToLower(), codec);
}
}
return encoders;
}
}
}
通常,Dictionary
不能有null
键(因为调用GetHashCode()
您放入的每个对象上)。但是因为你在 MimeType 上调用.ToLower()
- 它相当!= null
(否则异常会更早被抛出)。如果lock
不能解决您可能想要检查的问题,则使用调试器实际将哪些值放入字典中。
这可以简化,因为编码器不会在您每次调用时更改。 这是一个版本,它将编码器作为字典返回并将它们缓存在本地字典对象中
public static Dictionary<string, ImageCodecInfo> Encoders
{
get {
return encoders ??
(encoders = ImageCodecInfo.GetImageEncoders().ToDictionary(c => c.MimeType.ToLower()));
}
}