How is this Dictionary exception possible?

2020-03-13 04:14发布

问题:

Given the following stack trace:

MESSAGE: Value cannot be null.Parameter name: key  
SOURCE: mscorlib  
TARGETSITE: Void ThrowArgumentNullException(System.ExceptionArgument)  
STACKTRACE:  
   at System.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)  
   at System.Collections.Generic.Dictionary'2.FindEntry(TKey key)  
   at System.Collections.Generic.Dictionary'2.get_Item(TKey key)  
   at MyCompany.MAF.Agent.ServiceContracts.ConvertUtils.Convert(Dictionary'2 from) in D:\Development\MAF\Agent\MyCompany.MAF.Agent\ServiceContracts\ConvertUtils.cs:line 11

I conclude that somehow the following block of code has retrieved a null from the input Dictionary's Keys collection. However, the input dictionary is an instance of Dictionary<string, string>. The implementation of Dictionary<string, string> makes that condition impossible. Upon adding an item with a null key, an exception is thrown.

internal static KeyValuePair<string, string>[] Convert(IDictionary<string, string> from)
{
    List<KeyValuePair<string, string>> ret = new List<KeyValuePair<string, string>>();
    foreach (string key in from.Keys)
        ret.Add(new KeyValuePair<string, string>(key, from[key]));
    return ret.ToArray();
}

回答1:

I've had this problem happen frequently because I made the mistake of allowing multiple threads to access the same dictionary. Make sure that this is not the case, because Dictionary is not thread-safe.

(Incidentally, your method can be greatly simplified. Dictionary<K,V> is already an IEnumerable<KeyValuePair<K,V>>. You should be able to just do ToArray on one.



回答2:

It looks more like that your IDictionary argument has an item with a Key parameter which is null.

The parameter checking for the IDictionary will probably be happening somewhere internally in the framework.



回答3:

This exception happens if the dictionary key is null. As the built-in Dictionary class doesn't allow such keys, you might be using your own IDictionary-compatible class which allows null.



回答4:

Not sure about the null but why aren't you using:

internal static KeyValuePair<string, string>[] Convert(IDictionary<string, string> from)
{
    return from.ToArray();
}

Edit: As far as the null values are concerned. Do you have multiple threads accessing this IDictionary? Corruption is possible if you're not being thread safe. See this post for an example of corruption in the

Queue<T>

class. How can I modify a queue collection in a loop?



回答5:

Threading might well be the cause, but it was a red herring in our case. You can also seem to have this problem if you have a Release build where a function was inlined. Consider this repro:

class Program
{
    static string someKey = null;

    static void Main(string[] args)
    {
        try
        {
            object thing;

            if (SomeSeeminglyUnrelatedFunction(someKey, out thing)) Console.WriteLine("Found");

            things.TryGetValue(someKey, out thing);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.StackTrace.ToString());
            throw;
        }

        Console.ReadKey();
    }

    private static Dictionary<string, object> stuff = new Dictionary<string, object>();
    private static Dictionary<string, object> things = new Dictionary<string, object>();

    private static bool SomeSeeminglyUnrelatedFunction(string key, out object item)
    {
        return stuff.TryGetValue(key, out item);
    }
}

On my machine this gives the following Debug build stack trace:

at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
at MyApp.Program.SomeSeeminglyUnrelatedFunction(String key, Object& item)
at MyApp.Program.Main(String[] args)

But, it gives this stack trace in Release build:

at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
at MyApp.Program.Main(String[] args)

You'd be tempted to think with that last stack trace that it has to be things.TryGetValue that causes the exception, when in fact it was the SomeSeeminglyUnrelatedFunction function that got inlined in Main.

So if you land here, please consider if you have a similar issue. (I realize this may not be a straight up answer to OP's question, but wanted to share nonetheless as it might help a future visitor.)



回答6:

Could it possible be that another thread is affecting the dirctionary being passed into Convert?