Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Restricting value types in a C# Dictionary:

What I want is a Dictionary<string, T> where T is restricted to one of a few types.

If C# had Typescript union types it would look something like Dictionary<string, bool | string | int | List<string> | List<int>>

What I did is write a wrapper class around Dictionary which has a bunch of overloads of Add() for individual supported types, and methods like GetBool() and GetString() for retrieval. See http://pastebin.com/av7nE5az

This works, but it's verbose and ugly and tedious to extend and I think there must be a better way. Anybody got one?

Edit: Some questions about why I want this. Basically I want Dictionary<string, object>, with an instance perhaps containing values of multiple types, but I expect to use it in a constrained way (in a game) so the list of possible value types is limited. I could get by with just using Dictionary<string, object> and casting every retrieval, but I have the idea that it will be nicer if I can build more type awareness into the data structure itself.

like image 442
Matt Avatar asked Mar 02 '26 23:03

Matt


1 Answers

If your goal is to have simpler code and compile-time type checks, you can use this method, although it may slightly impact performance.

  1. Define your dictionary as Dictionary<string, MyEntry>

  2. Write a new class MyEntry that is capable of storing all of the data types of interest.

  3. Write implicit conversion operators between MyEntry and the data types of interest. It might look something like this example, which can handle bool and int (you can extend this for other data types as needed):

    class MyEntry
    {
        private Type _type = typeof(object);
        private bool _boolValue = false;
        private int _intValue = 0;
    
        public MyEntry(bool b)
        {
            _type = typeof(bool);
            _boolValue = b;
        }
    
        public MyEntry(int i)
        {
            _type = typeof(int);
            _intValue = i;
        }
    
        public static implicit operator bool(MyEntry e)
        {
            if (e._type != typeof(bool)) throw new InvalidCastException();
            return e._boolValue;
        }
        public static implicit operator MyEntry(bool b)
        {
            return new MyEntry(b);
        }
        public static implicit operator int(MyEntry e)
        {
            if (e._type != typeof(int)) throw new InvalidCastException();
            return e._intValue;
        }
        public static implicit operator MyEntry(int i)
        {
            return new MyEntry(i);
        }
    }
    
  4. You can then write concise code to read and write from this dictionary without explicit casts. You can control which types are allowed by restricting the implicit casts that are implemented for MyEntry.

    var d = new Dictionary<string, MyEntry>();
    d.Add("IntegerOne", 1);
    d.Add("BooleanTrue", true);
    d.Add("FloatThree", 3.0f);  //Will not compile
    
    bool b = d["BooleanTrue"];
    int i = d["IntegerOne"];
    float f = d["FloatThree"]; //Will not compile
    
    Console.WriteLine(string.Format("BooleanTrue = '{0}'", b));
    Console.WriteLine(string.Format("IntegerOne = '{0}'", i));
    
like image 116
John Wu Avatar answered Mar 04 '26 13:03

John Wu