-1

I am trying to adapt the following code, to also acceptable nullable type. Consider this:

public sealed class JsonDictionary
{
    private readonly IDictionary<string, object> _data;

    public static readonly Key<int> Foo = new Key<int> { Name = "FOO" };
    public static readonly Key<double> Bar = new Key<double> { Name = "BAR" };
    public static readonly Key<List<double>> Vec = new Key<List<double>> { Name = "VEC" };

    public JsonDictionary()
    {
        _data = new Dictionary<string, object>();
    }

    public void Set<T>(Key<T> key, T obj)
    {
        _data[key.Name] = obj;
    }    
    public T Get<T>(Key<T> key)
    {
        return (T)_data[key.Name];
    }
    public class Key<T>
    {
        public string Name { get; init; }
    }
}

This works quite well:

var d = new JsonDictionary();
d.Set(JsonDictionary.Foo, 42);
d.Set(JsonDictionary.Bar, 3.14);
d.Set(JsonDictionary.Vec, new List<double> { 1.0, 2.0, 3.0 });
Assert.Equal(42, d.Get(JsonDictionary.Foo));
Assert.Equal(3.14, d.Get(JsonDictionary.Bar));
Assert.Equal(new List<double> { 1.0, 2.0, 3.0 }, d.Get(JsonDictionary.Vec));

How can I change the Set/Get API to also accept nullable type ? For instance:

d.Set(JsonDictionary.Foo, null);
d.Set(JsonDictionary.Bar, null);
d.Set(JsonDictionary.Vec, null);

The following naive attempt did not work for me:

public void Set<T>(Key<T> key, T? obj)
{
    _data[key.Name] = obj;
}

Changing the base type as in:

public static readonly Key<int?> Foo = new Key<int?> { Name = "FOO" };
public static readonly Key<double?> Bar = new Key<double?> { Name = "BAR" };
public static readonly Key<List<double>?> Vec = new Key<List<double>?> { Name = "VEC" };

does work as expected, but requires a bit more typing ... any other solution directly at the Set function (with some constraints magic) ?


Another solution could be duplicating the code like this:

public void Set<T>(Key<T> key, T? obj) where T : struct
{
    _data[key.Name] = obj;
}
public void Set<T>(Key<T> key, T? obj) where T : class
{
    _data[key.Name] = obj;
}

which is also something I do not wish to do. Am I missing something fundamental in this strongly-typed structure ?

malat
  • 12,152
  • 13
  • 89
  • 158
  • 2
    I don't know if I have the right answer here, it's a guess:. change the base type? Like: `Key` – devlin carnate Sep 01 '21 at 15:47
  • To my mind, the best solution is the one given by @devlincarnate, keeping the same base type in the process is better. Your specifying the type in your Keys and in a way you're trying to modify it meanwhile. – T.Trassoudaine Sep 01 '21 at 16:31

1 Answers1

-1

The Solution you stated of changing the base Data types to nullable types seems to be the only option. If you try to pass a null into a non-nullable data type such as int you will receive an error (which your compiler should warn you about) at runtime. Generics can only perform the functions of the data type it is passed!


// will receive an error here v
// passing a null into int
Set(new Key<int> { Name = "FOO" }, null);
public void Set(Key<int> key, int obj)
{
    _data[key.Name] = obj;
}

this is essentially what your code was trying to do, the only way I can see it resolved is either by:

  1. Fixing the Data type

    Set<int?>(new Key<int?> { Name = "FOO" }, null);
    public void Set<T>(Key<T> key, T obj)
    {
        // Has no issue as you are setting a null to a nullable datatype
        _data[key.Name] = obj;
    }
    
  2. Discarding the null or reserving a value for null's

    Set<T>(new Key<int> { Name = "FOO" }, null);
    public void Set<T>(Key<T> key, object obj)
    {
        // check if obj is the same type as T if not sets the name to -1 in the key's type
        // be care full though if you try to pass an int into a float you will also get -1
        _data[key.Name] = (obj is T ? obj : (T)-1);
    }
    

Good luck and sorry I couldn't help much