As @JanDotNet explains in their [answer](https://codereview.stackexchange.com/a/166379/50704), your `DataTable<TRow, TCol, TCell>` does not handle hash collisions at all.  If a two two-item keys happen to have the same hash, your dictionary will overwrite the value for the first with the value for the second in random, hard-to-reproduce situations.

Since `Dictionary<TKey, TValue>` is already [designed to handle hash collisions](https://stackoverflow.com/a/2975784), I would recommend that, before implementing your own replacement from scratch, you ask: *is there any way I can use the existing `Dictionary<TKey, TValue>` type with two symmetric keys*?  And, in fact, this can be done, because a .Net dictionary can be constructed with a [custom `IEqualityComparer`](https://msdn.microsoft.com/en-us/library/ms132072(v=vs.110).aspx).  Given that, your problem now simplifies to: how can I create an equality comparer between pairs of keys that is symmetric?

Assuming you have access to the [`Tuple`](https://msdn.microsoft.com/en-us/library/system.tuple(v=vs.110).aspx) or [`ValueTuple`](https://msdn.microsoft.com/en-us/library/system.valuetuple(v=vs.110).aspx) types, you can create the following `IEqualityComparer` and [extension methods](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods):

    public class SymmetricTupleComparer<T1> : IEqualityComparer<Tuple<T1, T1>>
    {
        IEqualityComparer<T1> comparer = EqualityComparer<T1>.Default;

        #region IEqualityComparer<Tuple<T1,T1>> Members

        public bool Equals(Tuple<T1, T1> x, Tuple<T1, T1> y)
        {
            if (object.ReferenceEquals(x, y))
                return true;
            else if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null))
                return false;

            if (comparer.Equals(x.Item1, y.Item1) && comparer.Equals(x.Item2, y.Item2))
                return true;
            if (comparer.Equals(x.Item1, y.Item2) && comparer.Equals(x.Item2, y.Item1))
                return true;
            return false;
        }

        public int GetHashCode(Tuple<T1, T1> obj)
        {
            if (obj == null)
                return 0;
            // Here we use the fact that the XOR operator is symmetric
            return comparer.GetHashCode(obj.Item1) ^ comparer.GetHashCode(obj.Item2);
        }

        #endregion
    }

    public static partial class DictionaryExtensions
    {
        // Extension methods for dictionaries of symmetric pairs.
        // https://unity3d.com/learn/tutorials/topics/scripting/extension-methods

        public static void Add<TKey, TValue>(this IDictionary<Tuple<TKey, TKey>, TValue> dictionary, TKey item1, TKey item2, TValue value)
        {
            if (dictionary == null)
                throw new ArgumentNullException();
            dictionary.Add(Tuple.Create(item1, item2), value);
        }

        public static void Set<TKey, TValue>(this IDictionary<Tuple<TKey, TKey>, TValue> dictionary, TKey item1, TKey item2, TValue value)
        {
            if (dictionary == null)
                throw new ArgumentNullException();
            dictionary[Tuple.Create(item1, item2)] = value;
        }

        public static bool Remove<TKey, TValue>(this IDictionary<Tuple<TKey, TKey>, TValue> dictionary, TKey item1, TKey item2)
        {
            if (dictionary == null)
                throw new ArgumentNullException();
            return dictionary.Remove(Tuple.Create(item1, item2));
        }

        public static bool TryGetValue<TKey, TValue>(this IDictionary<Tuple<TKey, TKey>, TValue> dictionary, TKey item1, TKey item2, out TValue value)
        {
            if (dictionary == null)
                throw new ArgumentNullException();
            return dictionary.TryGetValue(Tuple.Create(item1, item2), out value);
        }
    }

Notice that:

 - `GetHashCode()` XOR's the hash codes of the items.  Since XOR is symmetric, two tuples with flipped items will have the same hash code.

 - `Equals()` checks whether the items are identical or identical when flipped.

And now you can use the comparer and extension methods as follows:

    var dictionary = new Dictionary<Tuple<int, int>, string>(new SymmetricTupleComparer<int>());
    dictionary.Set(1, 0, "hello");
    Console.WriteLine(dictionary.Get(0, 1));

Sample [fiddle](https://dotnetfiddle.net/up2NId) with basic tests working.

If you don't have access to `Tuple` or `ValueTuple` you can create your own generic symmetric pair quite easily:

    public struct SymmetricPair<TKey> : IEquatable<SymmetricPair<TKey>>
    {
        readonly TKey item1;
        readonly TKey item2;

        public SymmetricPair(TKey item1, TKey item2)
        {
            this.item1 = item1;
            this.item2 = item2;
        }

        public TKey Item1 { get { return item1; } }
        public TKey Item2 { get { return item2; } }

        #region IEquatable<SymmetricPair<TKey>> Members

        public bool Equals(SymmetricPair<TKey> other)
        {
            var comparer = EqualityComparer<TKey>.Default;
            if (comparer.Equals(Item1, other.Item1) && comparer.Equals(Item2, other.Item2))
                return true;
            if (comparer.Equals(Item2, other.Item1) && comparer.Equals(Item1, other.Item2))
                return true;
            return false;
        }

        #endregion

        public override bool Equals(object obj)
        {
            if (!(obj is SymmetricPair<TKey>))
                return false;
            return Equals((SymmetricPair<TKey>)obj);
        }

        public override int GetHashCode()
        {
            var comparer = EqualityComparer<TKey>.Default;
            // Note that the XOR operator is symmetric
            return comparer.GetHashCode(Item1) ^ comparer.GetHashCode(Item2);
        }

        public override string ToString()
        {
            return string.Format("SymmetricPair: ({0} {1})", Item1, Item2);
        }
    }

    public static partial class DictionaryExtensions
    {
        // Extension methods for dictionaries of symmetric pairs.
        // https://unity3d.com/learn/tutorials/topics/scripting/extension-methods

        public static void Add<TKey, TValue>(this IDictionary<SymmetricPair<TKey>, TValue> dictionary, TKey item1, TKey item2, TValue value)
        {
            if (dictionary == null)
                throw new ArgumentNullException();
            dictionary.Add(new SymmetricPair<TKey>(item1, item2), value);
        }

        public static void Set<TKey, TValue>(this IDictionary<SymmetricPair<TKey>, TValue> dictionary, TKey item1, TKey item2, TValue value)
        {
            if (dictionary == null)
                throw new ArgumentNullException();
            dictionary[new SymmetricPair<TKey>(item1, item2)] = value;
        }

        public static bool Remove<TKey, TValue>(this IDictionary<SymmetricPair<TKey>, TValue> dictionary, TKey item1, TKey item2)
        {
            if (dictionary == null)
                throw new ArgumentNullException();
            return dictionary.Remove(new SymmetricPair<TKey>(item1, item2));
        }

        public static TValue Get<TKey, TValue>(this IDictionary<SymmetricPair<TKey>, TValue> dictionary, TKey item1, TKey item2)
        {
            if (dictionary == null)
                throw new ArgumentNullException();
            return dictionary[new SymmetricPair<TKey>(item1, item2)];
        }

        public static bool TryGetValue<TKey, TValue>(this IDictionary<SymmetricPair<TKey>, TValue> dictionary, TKey item1, TKey item2, out TValue value)
        {
            if (dictionary == null)
                throw new ArgumentNullException();
            return dictionary.TryGetValue(new SymmetricPair<TKey>(item1, item2), out value);
        }
    }

And do:

    var dictionary = new Dictionary<SymmetricPair<int>, string>();
    dictionary.Set(1, 0, "hello");
    Console.WriteLine(dictionary.Get(0, 1));

Sample [fiddle #2](https://dotnetfiddle.net/rd1UPn).

In both cases your testing is now much simpler: you no longer need to validate your own dictionary implementation, you only need to validate your implementations of `GetHashCode()` and `Equals()`.