Odin breaks derieved generic containers

Issue #212 resolved
Andreas Podgurski created an issue

Hi, I have the following setup. I derieve a class from dictionary, where the value type is fixed (A float) and the key value is a generic:

public class Distribution<T> : Dictionary<T, float> where T: IComparable { }

The class has two own methods, which simply pick items from the collection, but don't do any modification. The class worked perfect before bringing Odin into the Project.

Now, if I try to add an entry, either in Unity or even by code, I get the following exception:

NullReferenceException: Object reference not set to an instance of an object
System.Collections.Generic.Dictionary`2[SpotTypes,System.Single].Add (SpotTypes key, Single value) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:746)
Spot.Start () (at Assets/RealmsOfQu/Scripts/Spot.cs:23)

in code or this one in the editor:

NullReferenceException: Object reference not set to an instance of an object
System.Collections.Generic.Dictionary`2[SpotTypes,System.Single].set_Item (SpotTypes key, Single value) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/List.cs:244)
Sirenix.OdinInspector.Editor.DictionaryHandler`3[Distribution`1[SpotTypes],SpotTypes,System.Single].ApplyChanges () (at C:/Sirenix/Sirenix Solution/Sirenix.OdinInspector.Editor/Core/Value Entries/PropertyValueEntryAlias.cs:340)
Sirenix.OdinInspector.Editor.PropertyValueEntry`2[Spot,Distribution`1[SpotTypes]].ApplyChanges ()
Sirenix.OdinInspector.Editor.PropertyTree.ApplyChanges () (at C:/Sirenix/Sirenix Solution/Sirenix.OdinInspector.Editor/Drawers/Attribute Drawers/DelayedPropertyAttributeDrawer.cs:83)
Sirenix.OdinInspector.Editor.InspectorUtilities.EndDrawPropertyTree (Sirenix.OdinInspector.Editor.PropertyTree tree) (at C:/Sirenix/Sirenix Solution/Sirenix.OdinInspector.Editor/Core/Infos/InspectorPropertyInfo.cs:510)
Sirenix.OdinInspector.Editor.PropertyTree.Draw (Boolean applyUndo) (at C:/Sirenix/Sirenix Solution/Sirenix.OdinInspector.Editor/Utilities/Persistent/PersistentContextCache.cs:183)
Sirenix.OdinInspector.Editor.OdinEditor.OnInspectorGUI () (at C:/Sirenix/Sirenix Solution/Sirenix.OdinInspector.Editor/Misc/DrawerPriority.cs:380)
UnityEditor.InspectorWindow.DrawEditor (UnityEditor.Editor[] editors, Int32 editorIndex, Boolean rebuildOptimizedGUIBlock, System.Boolean& showImportedObjectBarNext, UnityEngine.Rect& importedObjectBarRect) (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:1240)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

Working with the same type constellation with an underieved dictionary works as expected, so the derivation seems to be the major issue here. I tried to fix the generic type of the derieved dictionary to a specific type, but that didn't change a thing.

Please note, that the dictionary DOES work, if I add the containing behaviour fresh, but the Content is not serialized and trying to add it in the editor afterwards throws the given exception.

Sadly, the line numbers don't match with those from the provided source archive, so those seem to be not in sync, making any pre analyzation be me impossible.

This is a very seriouse bug to me. I used FullInspector before, but as it is dicontinued, I needed to move over to Odin. This issue appeared only after adding Odin to the Project and blocks my work at the moment.

Comments (4)

  1. Tor Esa Vestergaard
    • changed status to open

    I'm looking into this now, but am having trouble reproducing your error. Can you provide a bit more context around your usage of the Distribution<T> class, and perhaps a code sample that reproduces the problem?

  2. Andreas Podgurski reporter

    The dictionary is used within my Spot-class, but is ment to be used in multiple places later, thus the generic:

    public class Spot : SerializedMonoBehaviour {
    public Distribution<SpotTypes> Distribution = new Distribution<SpotTypes>();
    }
    

    SpotTypes is simply an enum, which specifies the type of decoration to be placed potentionally at this spot. There is not much more to say about this - I call the method Random within the derived dictionary (Returns a random entry, based on the distribution value stored in the float) and that's it. I'll look, if I can narrow it down to a test Project, but that may take a few hours.

    For testing purposes, I tried to bake the generic class into a typed one:

    public class SpotTypesDistribution : Distribution<SpotTypes> { }
    

    But the exception remains. Maybe it is simply a Problem with the types themselves, either the enum or the float?

  3. Tor Esa Vestergaard

    Alright, I've managed to reproduce the issue, and am working to resolve it. I'm currently looking at the decompiled source of Dictionary<TKey, TValue> in Unity's version of mscorlib.dll, since the error for some strange reason occurs in there. I'll keep you updated :)

  4. Tor Esa Vestergaard

    Okay, I've figured this one out. Essentially, our serialization didn't quite support serializing types that were derived from Dictionary - it would fail to detect that our dictionary formatter could handle the type, and would then fall back onto the SerializableFormatter, which would itself not work quite right for various reasons in this specific case (this has also been fixed). The exception was weird because the internal dictionary arrays had been messed up and were null, due to the deserialization not having happened correctly, so the exception happened inside the key setter, despite all the arguments to the setter being perfectly correct.

    I've added a temporary, hardcoded hotfix for this case to the FormatterLocator class, and made a note to revamp the formatter location system to the same standards as the drawer location system (with generic constraint parsing and generic parameter inference) for the next major patch, so it can dynamically handle this sort of case in the future.

    Meanwhile, I have a build ready for you. If you send me a mail with your Odin invoice ID to tor@sirenix.net, I'll send you the fixed build right away.

  5. Log in to comment