什么会导致此属性偶尔抛出 NullReferenceException?

Jan*_*nda 2 c# asp.net iis-8.5

我有一个 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;
        }
    }
    ...
Run Code Online (Sandbox Code Playgroud)

这是抛出异常的特定行:

encoders.Add(codec.MimeType.ToLower(), codec);
Run Code Online (Sandbox Code Playgroud)

这是错误文本:

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)
Run Code Online (Sandbox Code Playgroud)

这是调用 Encoders 属性的唯一地方(随后是堆栈跟踪中该行下方的行):

if (Encoders.ContainsKey(lookupKey)){
    foundCodec = Encoders[lookupKey];
}
Run Code Online (Sandbox Code Playgroud)

即使lookupKey 为空,查找不应该只返回空值而不是抛出异常吗?

Mat*_*int 5

您正在尝试使用“延迟加载的单例”,但您没有考虑并发性。在不牺牲性能的情况下执行此操作的最简单方法是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; }
}
Run Code Online (Sandbox Code Playgroud)

这是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; }
}
Run Code Online (Sandbox Code Playgroud)

您可能会处理此问题的另一种方法是使用ConcurrentDictionary,但这似乎有点矫枉过正,因为您不会经常添加项目。