Skip to content

Commit 417532c

Browse files
committed
fix: value comparer usage, remove by key/value pair, indexer assignment
# Conflicts: # src/BidirectionalDictionary/BidirectionalDictionary.csproj
1 parent a3aa110 commit 417532c

4 files changed

Lines changed: 131 additions & 14 deletions

File tree

src/BidirectionalDictionary/BidirectionalDictionary.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public class BidirectionalDictionary<TKey, TValue> : IBidirectionalDictionary<TK
5555
/// <param name="key">The key of the value to get or set.</param>
5656
/// <returns>The value associated with the specified key. If the specified key is not found, a get operation throws a
5757
/// <see cref="KeyNotFoundException"/>, and a set operation creates a new element with the specified key.</returns>
58+
/// <remarks>The stored key instance is preserved; the inverse view mirrors it.</remarks>
5859
/// <exception cref="ArgumentNullException"></exception>
5960
/// <exception cref="ArgumentException"></exception>
6061
/// <exception cref="KeyNotFoundException"></exception>
@@ -71,20 +72,15 @@ public TValue this[TKey key]
7172

7273
if (TryGetValue(key, out var oldValue))
7374
{
74-
if (ValueComparer.Equals(oldValue, value))
75-
return;
76-
77-
if (ContainsValue(value))
75+
if (!ValueComparer.Equals(oldValue, value) && ContainsValue(value))
7876
{
7977
throw new ArgumentException("The value already exists.", nameof(value));
8078
}
81-
else
82-
{
83-
_baseDictionary[key] = value;
8479

85-
Inverse._baseDictionary.Remove(oldValue);
86-
Inverse._baseDictionary.Add(value, key);
87-
}
80+
var storedKey = Inverse._baseDictionary[oldValue];
81+
_baseDictionary[key] = value;
82+
Inverse._baseDictionary.Remove(oldValue);
83+
Inverse._baseDictionary.Add(value, storedKey);
8884
}
8985
else
9086
{
@@ -412,8 +408,12 @@ bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> i
412408
if (item.Value == null)
413409
throw new ArgumentNullException("The item value == null.", nameof(item));
414410

415-
return ((ICollection<KeyValuePair<TKey, TValue>>)_baseDictionary).Remove(item) &&
416-
Inverse._baseDictionary.Remove(item.Value);
411+
if (!_baseDictionary.TryGetValue(item.Key, out var value) || !ValueComparer.Equals(value, item.Value))
412+
{
413+
return false;
414+
}
415+
416+
return Remove(item.Key);
417417
}
418418

419419
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
@@ -424,7 +424,7 @@ bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue>
424424
if (item.Value == null)
425425
throw new ArgumentNullException("The item value == null.", nameof(item));
426426

427-
return ((ICollection<KeyValuePair<TKey, TValue>>)_baseDictionary).Contains(item);
427+
return _baseDictionary.TryGetValue(item.Key, out var value) && ValueComparer.Equals(value, item.Value);
428428
}
429429

430430
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) =>

src/BidirectionalDictionary/BidirectionalDictionary.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<PackageProjectUrl>https://github.com/iiKuzmychov/BidirectionalDictionary</PackageProjectUrl>
1616
<RepositoryUrl>https://github.com/iiKuzmychov/BidirectionalDictionary</RepositoryUrl>
1717
<PackageTags>bidirectional-dictionary;bidirectional;bidirect;bidictionary;bimap;two-way;dictionary;map;readonly;read-only</PackageTags>
18-
<Version>1.4.1</Version>
18+
<Version>1.4.2</Version>
1919
<PackageReadmeFile>README.md</PackageReadmeFile>
2020
</PropertyGroup>
2121

tests/BidirectionalDictionary.Tests/BidirectionalDictionaryTests.ICollection{KeyValuePair{TKey,TValue}}.cs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,4 +179,86 @@ public void ICollectionKeyValuePair_Contains_FilledBiDictionaryAndPairWithNullKe
179179

180180
Assert.Throws<ArgumentNullException>(() => ((ICollection<KeyValuePair<char?, int?>>)biDictionary).Contains(pair));
181181
}
182+
183+
[Fact]
184+
[Trait("Method", "ICollection<KeyValuePair<TKey, TValue>>")]
185+
public void ICollectionKeyValuePair_Contains_CustomValueComparer_UsesValueComparer()
186+
{
187+
var biDictionary = new BidirectionalDictionary<string, string>(
188+
keyComparer: null,
189+
valueComparer: StringComparer.OrdinalIgnoreCase)
190+
{
191+
{ "x", "abc" },
192+
};
193+
194+
var collection = (ICollection<KeyValuePair<string, string>>)biDictionary;
195+
196+
Assert.True(collection.Contains(new KeyValuePair<string, string>("x", "ABC")));
197+
Assert.True(collection.Contains(new KeyValuePair<string, string>("x", "abc")));
198+
Assert.False(collection.Contains(new KeyValuePair<string, string>("x", "xyz")));
199+
}
200+
201+
[Fact]
202+
[Trait("Method", "ICollection<KeyValuePair<TKey, TValue>>")]
203+
public void ICollectionKeyValuePair_Contains_ReferenceEqualityValueComparer_DistinguishesInstances()
204+
{
205+
var s1 = new string("abc".ToCharArray());
206+
var s2 = new string("abc".ToCharArray());
207+
208+
var biDictionary = new BidirectionalDictionary<int, string>(
209+
keyComparer: null,
210+
valueComparer: ReferenceEqualityComparer.Instance)
211+
{
212+
{ 1, s1 },
213+
};
214+
215+
var collection = (ICollection<KeyValuePair<int, string>>)biDictionary;
216+
217+
Assert.True(collection.Contains(new KeyValuePair<int, string>(1, s1)));
218+
Assert.False(collection.Contains(new KeyValuePair<int, string>(1, s2)));
219+
}
220+
221+
[Fact]
222+
[Trait("Method", "ICollection<KeyValuePair<TKey, TValue>>")]
223+
public void ICollectionKeyValuePair_Remove_ReferenceEqualityValueComparer_DoesNotDesyncMaps()
224+
{
225+
var s1 = new string("abc".ToCharArray());
226+
var s2 = new string("abc".ToCharArray());
227+
228+
var biDictionary = new BidirectionalDictionary<int, string>(
229+
keyComparer: null,
230+
valueComparer: ReferenceEqualityComparer.Instance)
231+
{
232+
{ 1, s1 },
233+
};
234+
235+
var collection = (ICollection<KeyValuePair<int, string>>)biDictionary;
236+
237+
var isRemoved = collection.Remove(new KeyValuePair<int, string>(1, s2));
238+
239+
Assert.False(isRemoved);
240+
Assert.Single(biDictionary, new KeyValuePair<int, string>(1, s1));
241+
Assert.Single(biDictionary.Inverse.Keys, s1);
242+
Assert.Equal(biDictionary.Count, biDictionary.Inverse.Count);
243+
}
244+
245+
[Fact]
246+
[Trait("Method", "ICollection<KeyValuePair<TKey, TValue>>")]
247+
public void ICollectionKeyValuePair_Remove_CustomValueComparerMatchingPair_RemovesFromBothMaps()
248+
{
249+
var biDictionary = new BidirectionalDictionary<string, string>(
250+
keyComparer: null,
251+
valueComparer: StringComparer.OrdinalIgnoreCase)
252+
{
253+
{ "x", "abc" },
254+
};
255+
256+
var collection = (ICollection<KeyValuePair<string, string>>)biDictionary;
257+
258+
var isRemoved = collection.Remove(new KeyValuePair<string, string>("x", "ABC"));
259+
260+
Assert.True(isRemoved);
261+
Assert.Empty(biDictionary);
262+
Assert.Empty(biDictionary.Inverse);
263+
}
182264
}

tests/BidirectionalDictionary.Tests/BidirectionalDictionaryTests.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,5 +699,40 @@ public void Indexer_Set_EmptyBiDictionaryAndMissingKeyAndNonDuplicateValue_Creat
699699
Assert.Single(biDictionary.Inverse.Values, 'a');
700700
}
701701

702+
[Fact]
703+
public void Indexer_Set_ExistingKeyAndComparerEqualValue_ReplacesStoredValueInstance()
704+
{
705+
var biDictionary = new BidirectionalDictionary<string, string>(
706+
keyComparer: null,
707+
valueComparer: StringComparer.OrdinalIgnoreCase)
708+
{
709+
{ "x", "abc" },
710+
};
711+
712+
biDictionary["x"] = "ABC";
713+
714+
Assert.Equal("ABC", biDictionary["x"]);
715+
Assert.Single(biDictionary.Values, "ABC");
716+
Assert.Single(biDictionary.Inverse.Keys, "ABC");
717+
Assert.Equal("x", biDictionary.Inverse["ABC"]);
718+
}
719+
720+
[Fact]
721+
public void Indexer_Set_ComparerEqualKey_PreservesStoredKeyInstanceOnBothSides()
722+
{
723+
var storedKey = "x";
724+
var biDictionary = new BidirectionalDictionary<string, int>(
725+
keyComparer: StringComparer.OrdinalIgnoreCase,
726+
valueComparer: null)
727+
{
728+
{ storedKey, 1 },
729+
};
730+
731+
biDictionary["X"] = 2;
732+
733+
Assert.Same(storedKey, biDictionary.Keys.Single());
734+
Assert.Same(storedKey, biDictionary.Inverse[2]);
735+
}
736+
702737
#endregion
703738
}

0 commit comments

Comments
 (0)