什么会导致此 属性 偶尔抛出 NullReferenceException?
What would cause this property to occasionally throw a NullReferenceException?
我有一个 asp.net/C# class 可以调整图像大小以便在服务器上缓存为文件,但是确定使用哪个编码器的代码部分似乎偶尔会抛出 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)
这是唯一调用编码器 属性 的地方(随后是堆栈跟踪中该编码器下方的行):
if (Encoders.ContainsKey(lookupKey)){
foundCodec = Encoders[lookupKey];
}
即使 lookupKey 为 null,查找不应该只是 return null 而不是抛出异常吗?
由于此代码在 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
没有解决您可能想要检查的问题,您实际使用调试器将什么值放入字典中。
这可以简化,因为每次调用时编码器都不会改变。这是一个将 return 编码器作为字典并将它们缓存在本地字典对象中的版本
public static Dictionary<string, ImageCodecInfo> Encoders
{
get {
return encoders ??
(encoders = ImageCodecInfo.GetImageEncoders().ToDictionary(c => c.MimeType.ToLower()));
}
}
您正在尝试使用 "lazy loaded singleton",但您没有考虑并发性。在不牺牲性能的情况下执行此操作的最简单方法是 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's excellent article on the various ways you can implement this pattern 的模式 #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/C# class 可以调整图像大小以便在服务器上缓存为文件,但是确定使用哪个编码器的代码部分似乎偶尔会抛出 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)
这是唯一调用编码器 属性 的地方(随后是堆栈跟踪中该编码器下方的行):
if (Encoders.ContainsKey(lookupKey)){
foundCodec = Encoders[lookupKey];
}
即使 lookupKey 为 null,查找不应该只是 return null 而不是抛出异常吗?
由于此代码在 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
没有解决您可能想要检查的问题,您实际使用调试器将什么值放入字典中。
这可以简化,因为每次调用时编码器都不会改变。这是一个将 return 编码器作为字典并将它们缓存在本地字典对象中的版本
public static Dictionary<string, ImageCodecInfo> Encoders
{
get {
return encoders ??
(encoders = ImageCodecInfo.GetImageEncoders().ToDictionary(c => c.MimeType.ToLower()));
}
}
您正在尝试使用 "lazy loaded singleton",但您没有考虑并发性。在不牺牲性能的情况下执行此操作的最简单方法是 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's excellent article on the various ways you can implement this pattern 的模式 #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
,但这似乎有些矫枉过正,因为您不会经常添加项目。