Dictionary<String, Integer>

I’ve just upgraded from Elements 9 to 10.2275 and noticed that old stuff is not working any more.

What happened with the Dictionary<String, Integer>? Consider the code:

namespace TestDict;

interface

uses
  RemObjects.Elements.RTL;

type
  Program = class
  public
    class method Main(args: array of String): Int32;
  end;

implementation

class method Program.Main(args: array of String): Int32;
begin
  var D := new Dictionary<String, Integer>;
  D.Add('Y', 1);
  D.Add('X', 0);
  D.Add('Z', 2);
  D.Add('T', -1);
  D.Add('S', 0);
  D.Add('K', -2);
  for each k in D.Keys do
    writeLn(k + ' = ' + D[k]);
  readLn;
end;
end.

Under .NET, this gives:

Y = 1
Z = 2
T = -1
K = -2

Where are the zeros?

It seems there’s a bug/inconsistency in the .NET version of the RTL2 dictionary where when using value types (such as Integer), the default value (0 in this case) is treated as nil and setting nil will remove a value from the dictionary. We’ll investigate how to bets work around this, as this is a restriction of how Generics work, on .NET.

the only workaround for now would be to use non-value types for the dictionary’s values. :frowning:

Thanks, logged as bugs://80200

Seems odd, given that Integer should not accept nil (i.e. not even compile) unless marked optional. The whole point of nil/null (on modern platforms) is that is isn’t 0, no ?

I might have expected some odd edge cases on Island targets confusing 0 and null (a la good ol’ C/C++ and , dare I say, Delphi) but on .net I find this surprising. And it was O.K. in 9.x ? Maybe just put it back the way it was ? :face_with_hand_over_mouth: (I know it’s probably more complicated than that :slight_smile: )

Agreed. Now tell this to the CLR. :wink:

No. Its a design difference between the Dictionary on RTL2 vs Sugar. For RTL2, I designed the Dictionary to behave like Cocoa’s NSDictionary with regards to nil — reading a non-existing value returns nil, setting nil removes a value. Sugar’s Dictionary allowed nil as value in the dictionary, and threw an exception for unknown keys (which I hate, API wise, coz off makes for dozens of unnecessary If containsKey(x) then, and has bene the cause of many crashes in “naive” dictionary access code in the past).

If you look at RTL2’s implementation of Dictionary, you;'ll see it looks entirely fine, with regard to how it handles it in its logic — with one exception: anywhere my code checks for nil, if you instantiate a dictionary with a value type then “0” becomes nil, on there CLR :(.

I don’t know how to fix this, as reverting the design of the dictionary API to work like Sugar (or .NET’s Dictionary) works will break every single piece of code that sues this class, including all of Fire/Water. I;'m hoping we can find a workaround, compiler side. If not, the best/only option might be to constrain the RTL2 Dictionary class to “where T is class” and disallow using it for value types (on .NET).

:frowning:

I can confirm that it worked for me in Elements 9. The code broke after I upgraded to 10. I am pretty sure that I used the default configuration of 9.2181, but I can’t check any more, because the configuration is gone.

This seems too drastic to me. It would seriously hamper the .NET implementation and introduce additional differences between .NET and java.

I solved my particular problem by shifting Integers internally by +1, thus avoiding 0’s entirely. Ugly programming, but it works for me now.

not with Elements RTL. You probably used Sugar, which was a different library, with different implementation semantics for the dictionary, as i explained above.

Well. it silently losing 0 values is a rather worse platform discrepancy :wink:

Yeah, thats a hack, but not a long term solution for everyone.

The next build will have this constrained for Echoes and (possibly, couldn’t test that yet) Island, unless/until Carlo can find a compiler-side workaround (more likely for Island, less likely for .NET). On Java it seems to be fine (and Cocoa was already constrained to class types, as the underlying NSDictionary implementation is, too).

How is this going? I updated to RTL2 master and now Dictionary is restricted to classes, so I cannot use Dictionary<String,Boolean> unless I remove the constrain. Then I’ll face the missing 0s. A quick look at my code shows me I won’t have problems with the missing zeroes. I hope to remember this bug when I need them :wink:

Just asking, as I saw it was from May, this is not urgent for me. Don’t make Carlo hate me any more :wink:

haha I don’t hate you. This particular issue needs very careful implementation. We basically need a special type that can be nullable for both value and reference types, but is implicitly compatible with the actual type.

1 Like

bugs://80200 got closed with status fixed.