C#中的双向/双向字典?

Posted

技术标签:

【中文标题】C#中的双向/双向字典?【英文标题】:Two-way / bidirectional Dictionary in C#? 【发布时间】:2012-06-13 13:04:56 【问题描述】:

我想通过以下方式将单词存储在字典中:

我可以逐字获取代码:dict["SomeWord"] -> 123 并逐字获取代码:dict[123] -> "SomeWord"

这是真的吗?当然,一种方法是使用两个字典:Dictionary<string,int>Dictionary<int,string>,但还有其他方法吗?

【问题讨论】:

没有提供 O(1) 两种方式访问​​的标准(从 .NET 4 开始)数据类型...... AFAIK :) 也不是双向映射(关键字?)施加额外的限制,除非多双向映射... ***.com/questions/1227683/bi-directional-dictionary , ***.com/questions/268321/… 【参考方案1】:

我写了几个快速的类,可以让你做你想做的事。您可能需要使用更多功能对其进行扩展,但这是一个很好的起点。

使用代码如下:

var map = new Map<int, string>();

map.Add(42, "Hello");

Console.WriteLine(map.Forward[42]);
// Outputs "Hello"

Console.WriteLine(map.Reverse["Hello"]);
//Outputs 42

定义如下:

public class Map<T1, T2>

    private Dictionary<T1, T2> _forward = new Dictionary<T1, T2>();
    private Dictionary<T2, T1> _reverse = new Dictionary<T2, T1>();

    public Map()
    
        this.Forward = new Indexer<T1, T2>(_forward);
        this.Reverse = new Indexer<T2, T1>(_reverse);
    

    public class Indexer<T3, T4>
    
        private Dictionary<T3, T4> _dictionary;
        public Indexer(Dictionary<T3, T4> dictionary)
        
            _dictionary = dictionary;
        
        public T4 this[T3 index]
        
            get  return _dictionary[index]; 
            set  _dictionary[index] = value; 
        
    

    public void Add(T1 t1, T2 t2)
    
        _forward.Add(t1, t2);
        _reverse.Add(t2, t1);
    

    public Indexer<T1, T2> Forward  get; private set; 
    public Indexer<T2, T1> Reverse  get; private set; 

【讨论】:

@Pedro77 - 我只是在厚颜无耻地暗示我的班级是新的“地图”解决方案。 这不会维护异常的类不变量。 _forward.Add 可能会成功,_reverse.Add 可能会失败,从而为您留下部分添加的对。 @hvd - 就像我说的 - 这是一个快速组合的课程。 您可能不希望在 Indexer 类中使用 setter,这会破坏正向/反向查找。 @AaA 它没有修改它自己的Forward 字典属性(它具有private set;),但它正在通过传递它的 Indexer 类的 Indexer 属性修改该字典上的值到字典。 public T4 this[T3 index] get return _dictionary[index]; set _dictionary[index] = value; 这打破了正向/反向查找。【参考方案2】:

很遗憾,您需要两本字典,每个方向一本。但是,您可以使用 LINQ 轻松获取逆字典:

Dictionary<T1, T2> dict = new Dictionary<T1, T2>();
Dictionary<T2, T1> dictInverse = dict.ToDictionary((i) => i.Value, (i) => i.Key);

【讨论】:

虽然方便,但如果你经常访问逆字典,它的表现就不会很好。每次需要时即时创建新词典的成本很高。【参考方案3】:

通过添加初始化和包含方法扩展了 Enigmativity 代码。

public class Map<T1, T2> : IEnumerable<KeyValuePair<T1, T2>>

    private readonly Dictionary<T1, T2> _forward = new Dictionary<T1, T2>();
    private readonly Dictionary<T2, T1> _reverse = new Dictionary<T2, T1>();

    public Map()
    
        Forward = new Indexer<T1, T2>(_forward);
        Reverse = new Indexer<T2, T1>(_reverse);
    

    public Indexer<T1, T2> Forward  get; private set; 
    public Indexer<T2, T1> Reverse  get; private set; 

    public void Add(T1 t1, T2 t2)
    
        _forward.Add(t1, t2);
        _reverse.Add(t2, t1);
    

    public void Remove(T1 t1)
    
        T2 revKey = Forward[t1];
        _forward.Remove(t1);
        _reverse.Remove(revKey);
    
    
    public void Remove(T2 t2)
    
        T1 forwardKey = Reverse[t2];
        _reverse.Remove(t2);
        _forward.Remove(forwardKey);
    

    IEnumerator IEnumerable.GetEnumerator()
    
        return GetEnumerator();
    

    public IEnumerator<KeyValuePair<T1, T2>> GetEnumerator()
    
        return _forward.GetEnumerator();
    

    public class Indexer<T3, T4>
    
        private readonly Dictionary<T3, T4> _dictionary;

        public Indexer(Dictionary<T3, T4> dictionary)
        
            _dictionary = dictionary;
        

        public T4 this[T3 index]
        
            get  return _dictionary[index]; 
            set  _dictionary[index] = value; 
        

        public bool Contains(T3 key)
        
            return _dictionary.ContainsKey(key);
        
    

这是一个用例,检查有效括号

public static class ValidParenthesisExt

    private static readonly Map<char, char>
        _parenthesis = new Map<char, char>
        
            '(', ')',
            '', '',
            '[', ']'
        ;

    public static bool IsValidParenthesis(this string input)
    
        var stack = new Stack<char>();
        foreach (var c in input)
        
            if (_parenthesis.Forward.Contains(c))
                stack.Push(c);
            else
            
                if (stack.Count == 0) return false;
                if (_parenthesis.Reverse[c] != stack.Pop())
                    return false;
            
        
        return stack.Count == 0;
    

【讨论】:

public T4 this[T3 index] get return _dictionary[index]; set _dictionary[index] = value; 由于这段代码,字典可以从对中指出值 ex:``` var parent = parentMap.Reverse[pair.Key]; parentMap.Forward[parent] = ;```【参考方案4】:

什么鬼,我会把我的版本混在一起:

public class BijectiveDictionary<TKey, TValue> 

    private EqualityComparer<TKey> _keyComparer;
    private Dictionary<TKey, ISet<TValue>> _forwardLookup;
    private EqualityComparer<TValue> _valueComparer;
    private Dictionary<TValue, ISet<TKey>> _reverseLookup;             

    public BijectiveDictionary()
        : this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
    
    

    public BijectiveDictionary(EqualityComparer<TKey> keyComparer, EqualityComparer<TValue> valueComparer)
        : this(0, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
    
    

    public BijectiveDictionary(int capacity, EqualityComparer<TKey> keyComparer, EqualityComparer<TValue> valueComparer)
    
        _keyComparer = keyComparer;
        _forwardLookup = new Dictionary<TKey, ISet<TValue>>(capacity, keyComparer);            
        _valueComparer = valueComparer;
        _reverseLookup = new Dictionary<TValue, ISet<TKey>>(capacity, valueComparer);            
    

    public void Add(TKey key, TValue value)
    
        AddForward(key, value);
        AddReverse(key, value);
    

    public void AddForward(TKey key, TValue value)
    
        ISet<TValue> values;
        if (!_forwardLookup.TryGetValue(key, out values))
        
            values = new HashSet<TValue>(_valueComparer);
            _forwardLookup.Add(key, values);
        
        values.Add(value);
    

    public void AddReverse(TKey key, TValue value) 
    
        ISet<TKey> keys;
        if (!_reverseLookup.TryGetValue(value, out keys))
        
            keys = new HashSet<TKey>(_keyComparer);
            _reverseLookup.Add(value, keys);
        
        keys.Add(key);
    

    public bool TryGetReverse(TValue value, out ISet<TKey> keys)
    
        return _reverseLookup.TryGetValue(value, out keys);
    

    public ISet<TKey> GetReverse(TValue value)
    
        ISet<TKey> keys;
        TryGetReverse(value, out keys);
        return keys;
    

    public bool ContainsForward(TKey key)
    
        return _forwardLookup.ContainsKey(key);
    

    public bool TryGetForward(TKey key, out ISet<TValue> values)
    
        return _forwardLookup.TryGetValue(key, out values);
    

    public ISet<TValue> GetForward(TKey key)
    
        ISet<TValue> values;
        TryGetForward(key, out values);
        return values;
    

    public bool ContainsReverse(TValue value)
    
        return _reverseLookup.ContainsKey(value);
    

    public void Clear()
    
        _forwardLookup.Clear();
        _reverseLookup.Clear();
    

向其中添加一些数据:

var lookup = new BijectiveDictionary<int, int>();

lookup.Add(1, 2);
lookup.Add(1, 3);
lookup.Add(1, 4);
lookup.Add(1, 5);

lookup.Add(6, 2);
lookup.Add(6, 8);
lookup.Add(6, 9);
lookup.Add(6, 10);

然后进行查找:

lookup[2] --> 1, 6
lookup[3] --> 1
lookup[8] --> 6

【讨论】:

我喜欢这个支持1:N @Sebastian,你可以添加 IEnumerable>。【参考方案5】:

正如其他人所说,您可以使用两个字典,但还要注意,如果 TKeyTValue 属于同一类型(并且已知它们的运行时值域是不相交的),那么您可以只使用通过为每个键/值对创建两个条目来创建相同的字典:

dict["SomeWord"]= "123"dict["123"]="SomeWord"

这样一个字典就可以用于任何一种类型的查找。

【讨论】:

是的,这个方法在问题中得到了承认:) 这忽略了“键”和“值”中存在相同值的可能性。那么它会在这个解决方案中发生冲突。 @user1028741 同意,尽管从示例中可以看出它们的意思是“不同类型”而不是“相同类型” 这可能会导致将来出现意外结果,然后代码会进行重构。例如。然后左右两侧开始重叠。它几乎没有增加性能。【参考方案6】:

您可以使用此扩展方法,尽管它使用枚举,因此对于大型数据集可能没有性能。如果您担心效率,那么您需要两个字典。如果您想将两个字典包装到一个类中,请参阅此问题的公认答案:Bidirectional 1 to 1 Dictionary in C#

public static class IDictionaryExtensions

    public static TKey FindKeyByValue<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TValue value)
    
        if (dictionary == null)
            throw new ArgumentNullException("dictionary");

        foreach (KeyValuePair<TKey, TValue> pair in dictionary)
            if (value.Equals(pair.Value)) return pair.Key;

        throw new Exception("the value is not found in the dictionary");
    

【讨论】:

虽然这是一个双向字典,但获取值是一个 O(n) 操作,而它应该是一个 O(1) 操作。这对于小型数据集可能无关紧要,但在处理大型数据集时可能会导致性能问题。空间性能的最佳答案是使用两个具有反向数据的字典。 @TomA 我完全同意 Tom,唯一需要真正双向字典的情况是当您有 100K、1M+ 条目时,只要不扫描它,实际上就是 NOOP。 我喜欢这个解决方案适合我的情况(小字典大小),因为我仍然可以使用集合初始化器。我认为接受的答案中的 Map 不能用于集合初始化程序。 @ChrisMarisic,这似乎是一件奇怪的事情。我敢打赌,如果这个查找是在一个紧密的循环中调用的,即使条目少于 500 个,你也会感到痛苦。它还取决于比较测试的成本。我不认为像你的评论这样笼统的陈述是有帮助的。 @LeeCampbell 我的概括性陈述是基于在实际现实中的经验,如在可测量和描述的现实中。如果您想使用某些复杂类型作为字典的键,那是您的问题,不是我的问题。【参考方案7】:

Enigmativity 的答案有一个扩展版本,可作为 nuget 包使用 https://www.nuget.org/packages/BidirectionalMap/

它是开源的here

【讨论】:

【参考方案8】:

Xavier John 答案的修改版本,带有一个额外的构造函数来进行正向和反向比较器。例如,这将支持不区分大小写的键。如果需要,可以添加更多构造函数,以将更多参数传递给正向和反向 Dictionary 构造函数。

public class Map<T1, T2> : IEnumerable<KeyValuePair<T1, T2>>

    private readonly Dictionary<T1, T2> _forward;
    private readonly Dictionary<T2, T1> _reverse;

    /// <summary>
    /// Constructor that uses the default comparers for the keys in each direction.
    /// </summary>
    public Map()
        : this(null, null)
    
    

    /// <summary>
    /// Constructor that defines the comparers to use when comparing keys in each direction.
    /// </summary>
    /// <param name="t1Comparer">Comparer for the keys of type T1.</param>
    /// <param name="t2Comparer">Comparer for the keys of type T2.</param>
    /// <remarks>Pass null to use the default comparer.</remarks>
    public Map(IEqualityComparer<T1> t1Comparer, IEqualityComparer<T2> t2Comparer)
    
        _forward = new Dictionary<T1, T2>(t1Comparer);
        _reverse = new Dictionary<T2, T1>(t2Comparer);
        Forward = new Indexer<T1, T2>(_forward);
        Reverse = new Indexer<T2, T1>(_reverse);
    

    // Remainder is the same as Xavier John's answer:
    // https://***.com/a/41907561/216440
    ...

使用示例,带有不区分大小写的键:

Map<int, string> categories = 
new Map<int, string>(null, StringComparer.CurrentCultureIgnoreCase)

     1, "Bedroom Furniture" ,
     2, "Dining Furniture" ,
     3, "Outdoor Furniture" , 
     4, "Kitchen Appliances" 
;

int categoryId = 3;
Console.WriteLine("Description for category ID 0: '1'", 
    categoryId, categories.Forward[categoryId]);

string categoryDescription = "DINING FURNITURE";
Console.WriteLine("Category ID for description '0': 1", 
    categoryDescription, categories.Reverse[categoryDescription]);

categoryDescription = "outdoor furniture";
Console.WriteLine("Category ID for description '0': 1", 
    categoryDescription, categories.Reverse[categoryDescription]);

// Results:
/*
Description for category ID 3: 'Outdoor Furniture'
Category ID for description 'DINING FURNITURE': 2
Category ID for description 'outdoor furniture': 3
*/

【讨论】:

【参考方案9】:

字典

这是我在每个答案中喜欢的内容的混合。它实现了IEnumerable,因此它可以使用集合初始化器,如您在示例中所见。

使用限制:

您正在使用不同的数据类型。 (即,T1T2

代码:

using System;
using System.Collections.Generic;
using System.Linq;

public class Program

    public static void Main()
    
        Bictionary<string, int> bictionary = 
            new Bictionary<string,int>() 
                 "a",1 , 
                 "b",2 , 
                 "c",3  
            ;

        // test forward lookup
        Console.WriteLine(bictionary["b"]);
        // test forward lookup error
        //Console.WriteLine(bictionary["d"]);
        // test reverse lookup
        Console.WriteLine(bictionary[3]); 
        // test reverse lookup error (throws same error as forward lookup does)
        Console.WriteLine(bictionary[4]); 
    


public class Bictionary<T1, T2> : Dictionary<T1, T2>

    public T1 this[T2 index]
    
        get
        
            if(!this.Any(x => x.Value.Equals(index)))
               throw new System.Collections.Generic.KeyNotFoundException();
            return this.First(x => x.Value.Equals(index)).Key;
        
    

小提琴:

https://dotnetfiddle.net/mTNEuw

【讨论】:

非常优雅的解决方案!你能不能进一步解释一下它的胆量?我说得对吗,即使所有字符串都是唯一的,您也无法创建 Bictionary&lt;string, string&gt; @Merlin2001,没错。更准确地说,你不能用它进行正向查找。我将不得不考虑如何克服这一点。它可以编译,但总是在T1 == T2 时首先找到反向索引器,因此正向查找失败。此外,我不能覆盖默认索引器,因为这样查找调用将是模棱两可的。我添加了这个约束并删除了前一个约束,因为T1 的值可以与T2 的值重叠。 反过来有一个相当严重的性能问题;字典被搜索两次,性能下降 O(n);使用第二个字典会快得多,并且会删除类型约束。 @SteveCooper,也许我可以通过将其包装在 try 中并将异常转换为 KeyNotFoundExceptions 来消除性能损失。 @toddmo 您可以通过这种方式将速度提高一倍。更大的问题是 .First 和 .Any 一次搜索一个项目,测试每个项目。因此,测试 1,000,000 项列表的搜索时间是 1 元素列表的 1,000,000 倍。字典要快得多,并且不会随着您添加更多项目而减慢速度,因此第二个逆向字典将在大型列表中节省大量时间。它可能无关紧要,但在测试期间使用少量数据就可以解决问题,然后它会在具有严重数据的真实服务器上扼杀性能。【参考方案10】:

这是一个老问题,但我想添加两个扩展方法,以防有人发现它有用。第二个没那么有用,但如果需要支持一对一字典,它提供了一个起点。

    public static Dictionary<VALUE,KEY> Inverse<KEY,VALUE>(this Dictionary<KEY,VALUE> dictionary)
    
        if (dictionary==null || dictionary.Count == 0)  return null; 

        var result = new Dictionary<VALUE, KEY>(dictionary.Count);

        foreach(KeyValuePair<KEY,VALUE> entry in dictionary)
        
            result.Add(entry.Value, entry.Key);
        

        return result;
    

    public static Dictionary<VALUE, KEY> SafeInverse<KEY, VALUE>(this Dictionary<KEY, VALUE> dictionary)
    
        if (dictionary == null || dictionary.Count == 0)  return null; 

        var result = new Dictionary<VALUE, KEY>(dictionary.Count);

        foreach (KeyValuePair<KEY, VALUE> entry in dictionary)
        
            if (result.ContainsKey(entry.Value))  continue; 

            result.Add(entry.Value, entry.Key);
        

        return result;
    

【讨论】:

【参考方案11】:

这是我的代码。除了种子构造函数之外,一切都是 O(1)。

using System.Collections.Generic;
using System.Linq;

public class TwoWayDictionary<T1, T2>

    Dictionary<T1, T2> _Forwards = new Dictionary<T1, T2>();
    Dictionary<T2, T1> _Backwards = new Dictionary<T2, T1>();

    public IReadOnlyDictionary<T1, T2> Forwards => _Forwards;
    public IReadOnlyDictionary<T2, T1> Backwards => _Backwards;

    public IEnumerable<T1> Set1 => Forwards.Keys;
    public IEnumerable<T2> Set2 => Backwards.Keys;


    public TwoWayDictionary()
    
        _Forwards = new Dictionary<T1, T2>();
        _Backwards = new Dictionary<T2, T1>();
    

    public TwoWayDictionary(int capacity)
    
        _Forwards = new Dictionary<T1, T2>(capacity);
        _Backwards = new Dictionary<T2, T1>(capacity);
    

    public TwoWayDictionary(Dictionary<T1, T2> initial)
    
        _Forwards = initial;
        _Backwards = initial.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
    

    public TwoWayDictionary(Dictionary<T2, T1> initial)
    
        _Backwards = initial;
        _Forwards = initial.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
    


    public T1 this[T2 index]
    
        get => _Backwards[index];
        set
        
            if (_Backwards.TryGetValue(index, out var removeThis))
                _Forwards.Remove(removeThis);

            _Backwards[index] = value;
            _Forwards[value] = index;
        
    

    public T2 this[T1 index]
    
        get => _Forwards[index];
        set
        
            if (_Forwards.TryGetValue(index, out var removeThis))
                _Backwards.Remove(removeThis);

            _Forwards[index] = value;
            _Backwards[value] = index;
        
    

    public int Count => _Forwards.Count;

    public bool Contains(T1 item) => _Forwards.ContainsKey(item);
    public bool Contains(T2 item) => _Backwards.ContainsKey(item);

    public bool Remove(T1 item)
    
        if (!this.Contains(item))
            return false;

        var t2 = _Forwards[item];

        _Backwards.Remove(t2);
        _Forwards.Remove(item);

        return true;
    

    public bool Remove(T2 item)
    
        if (!this.Contains(item))
            return false;

        var t1 = _Backwards[item];

        _Forwards.Remove(t1);
        _Backwards.Remove(item);

        return true;
    

    public void Clear()
    
        _Forwards.Clear();
        _Backwards.Clear();
    

【讨论】:

我想知道如果你传递一个键和值类型相同的现有字典,构造函数会如何表现。它将如何解决是使用后向还是前向? error CS0121: The call is ambiguous between the following methods or properties: 'TwoWayDictionary&lt;T1, T2&gt;.TwoWayDictionary(Dictionary&lt;T1, T2&gt;)' and 'TwoWayDictionary&lt;T1, T2&gt;.TwoWayDictionary(Dictionary&lt;T2, T1&gt;)'【参考方案12】:

以下是建议的替代解决方案。移除了内部类并保证了添加/移除项目时的一致性

using System.Collections;
using System.Collections.Generic;

public class Map<E, F> : IEnumerable<KeyValuePair<E, F>>

    private readonly Dictionary<E, F> _left = new Dictionary<E, F>();
    public IReadOnlyDictionary<E, F> left => this._left;
    private readonly Dictionary<F, E> _right = new Dictionary<F, E>();
    public IReadOnlyDictionary<F, E> right => this._right;

    public void RemoveLeft(E e)
    
        if (!this.left.ContainsKey(e)) return;
        this._right.Remove(this.left[e]);
        this._left.Remove(e);
    

    public void RemoveRight(F f)
    
        if (!this.right.ContainsKey(f)) return;
        this._left.Remove(this.right[f]);
        this._right.Remove(f);
    

    public int Count()
    
        return this.left.Count;
    

    public void Set(E left, F right)
    
        if (this.left.ContainsKey(left))
        
            this.RemoveLeft(left);
        
        if (this.right.ContainsKey(right))
        
            this.RemoveRight(right);
        
        this._left.Add(left, right);
        this._right.Add(right, left);
    


    public IEnumerator<KeyValuePair<E, F>> GetEnumerator()
    
        return this.left.GetEnumerator();
    

    IEnumerator IEnumerable.GetEnumerator()
    
        return this.left.GetEnumerator();
    


【讨论】:

【参考方案13】:

以下封装类在 1 个字典实例上使用 linq(IEnumerable Extensions)。

public class TwoWayDictionary<TKey, TValue>

    readonly IDictionary<TKey, TValue> dict;
    readonly Func<TKey, TValue> GetValueWhereKey;
    readonly Func<TValue, TKey> GetKeyWhereValue;
    readonly bool _mustValueBeUnique = true;

    public TwoWayDictionary()
    
        this.dict = new Dictionary<TKey, TValue>();
        this.GetValueWhereKey = (strValue) => dict.Where(kvp => Object.Equals(kvp.Key, strValue)).Select(kvp => kvp.Value).FirstOrDefault();
        this.GetKeyWhereValue = (intValue) => dict.Where(kvp => Object.Equals(kvp.Value, intValue)).Select(kvp => kvp.Key).FirstOrDefault();
    

    public TwoWayDictionary(KeyValuePair<TKey, TValue>[] kvps)
        : this()
    
        this.AddRange(kvps);
    

    public void AddRange(KeyValuePair<TKey, TValue>[] kvps)
    
        kvps.ToList().ForEach( kvp =>         
            if (!_mustValueBeUnique || !this.dict.Any(item => Object.Equals(item.Value, kvp.Value)))
            
                dict.Add(kvp.Key, kvp.Value);
             else 
                throw new InvalidOperationException("Value must be unique");
            
        );
    

    public TValue this[TKey key]
    
        get  return GetValueWhereKey(key); 
    

    public TKey this[TValue value]
    
        get  return GetKeyWhereValue(value); 
    


class Program

    static void Main(string[] args)
    
        var dict = new TwoWayDictionary<string, int>(new KeyValuePair<string, int>[] 
            new KeyValuePair<string, int>(".jpeg",100),
            new KeyValuePair<string, int>(".jpg",101),
            new KeyValuePair<string, int>(".txt",102),
            new KeyValuePair<string, int>(".zip",103)
        );


        var r1 = dict[100];
        var r2 = dict[".jpg"];

    


【讨论】:

【参考方案14】:

这使用索引器进行反向查找。 反向查找是 O(n) 但它也不使用两个字典

public sealed class DictionaryDoubleKeyed : Dictionary<UInt32, string>
   // used UInt32 as the key as it has a perfect hash
    // if most of the lookup is by word then swap
    public void Add(UInt32 ID, string Word)
    
        if (this.ContainsValue(Word)) throw new ArgumentException();
        base.Add(ID, Word);
    
    public UInt32 this[string Word]
       // this will be O(n)
        get
        
            return this.FirstOrDefault(x => x.Value == Word).Key;
        
     

【讨论】:

例如:this[string Word] 中可能的 NRE。其他问题是变量名不符合惯例,注释与代码不一致(UInt16 vs UInt32 - 这就是为什么:不要使用 cmets!),解决方案不通用,...【参考方案15】:

在这个开源仓库中有一个BijectionDictionary 类型可用:

https://github.com/ColmBhandal/CsharpExtras.

这与给出的其他答案在质量上没有太大区别。它使用两个字典,就像大多数答案一样。

我认为,这本词典与迄今为止的其他答案相比,新颖之处在于,它不像一个双向词典,而是像一个单向、熟悉的词典,然后动态地允许您翻转使用 Reverse 属性的字典。翻转的对象引用很浅,因此它仍然可以修改与原始引用相同的核心对象。所以你可以有两个对同一个对象的引用,除了其中一个被翻转。

这本字典的另一个可能独特之处是,在该 repo 下的测试项目中为它编写了一些测试。我们已经在实践中使用它,并且到目前为止非常稳定。

【讨论】:

以上是关于C#中的双向/双向字典?的主要内容,如果未能解决你的问题,请参考以下文章

C#中的Unicode双向文本算法?

如何在 C# 中进行双向 tls 身份验证

DataGridView如何绑定字典集合数据

字典到 ListView 双向绑定 - 可能吗?

c#命名管道双向通信

pythoncollections模块(有序字典,计数器,双向队列)