Skip to content

Commit 46bc292

Browse files
committed
Initial commit - DictionaryWithDefaultValue
1 parent 531f23b commit 46bc292

File tree

5 files changed

+226
-0
lines changed

5 files changed

+226
-0
lines changed

CodingMilitia.MoarCollections.sln

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.26430.6
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{AD399DC0-AECC-4797-A8FF-951615A58437}"
7+
EndProject
8+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{8485BE98-F392-4E80-B192-72DEE9C76927}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{0B17044C-A6D3-48F7-987A-BAB751AC9EE6}"
11+
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodingMilitia.MoarCollections", "src\CodingMilitia.MoarCollections\CodingMilitia.MoarCollections.csproj", "{22F10D5F-20AB-486B-9365-AB5D1E18B794}"
13+
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodingMilitia.MoarCollections.Tests", "tests\CodingMilitia.MoarCollections.Tests\CodingMilitia.MoarCollections.Tests.csproj", "{427303DA-D22D-46F4-91E2-2F864C20A5BF}"
15+
EndProject
16+
Global
17+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
18+
Debug|Any CPU = Debug|Any CPU
19+
Release|Any CPU = Release|Any CPU
20+
EndGlobalSection
21+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
22+
{22F10D5F-20AB-486B-9365-AB5D1E18B794}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23+
{22F10D5F-20AB-486B-9365-AB5D1E18B794}.Debug|Any CPU.Build.0 = Debug|Any CPU
24+
{22F10D5F-20AB-486B-9365-AB5D1E18B794}.Release|Any CPU.ActiveCfg = Release|Any CPU
25+
{22F10D5F-20AB-486B-9365-AB5D1E18B794}.Release|Any CPU.Build.0 = Release|Any CPU
26+
{427303DA-D22D-46F4-91E2-2F864C20A5BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27+
{427303DA-D22D-46F4-91E2-2F864C20A5BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
28+
{427303DA-D22D-46F4-91E2-2F864C20A5BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
29+
{427303DA-D22D-46F4-91E2-2F864C20A5BF}.Release|Any CPU.Build.0 = Release|Any CPU
30+
EndGlobalSection
31+
GlobalSection(SolutionProperties) = preSolution
32+
HideSolutionNode = FALSE
33+
EndGlobalSection
34+
GlobalSection(NestedProjects) = preSolution
35+
{22F10D5F-20AB-486B-9365-AB5D1E18B794} = {AD399DC0-AECC-4797-A8FF-951615A58437}
36+
{427303DA-D22D-46F4-91E2-2F864C20A5BF} = {8485BE98-F392-4E80-B192-72DEE9C76927}
37+
EndGlobalSection
38+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard1.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
5+
namespace CodingMilitia.MoarCollections.Dictionaries
6+
{
7+
/// <summary>
8+
/// <para>
9+
/// Acts as a wrapper over a <see cref="Dictionary{TKey, TValue}"/> with the ability of returning a default value (static or using a <see cref="Func{TKey, TValue}"/>)
10+
/// when the requested key is not present.
11+
/// </para>
12+
/// <para>
13+
/// Most of the times, using <see cref="IDictionary{TKey, TValue}.TryGetValue(TKey, out TValue)"/> on a regular Dictionary will be good enough,
14+
/// this should only be useful when you just want to index and forget.
15+
/// </para>
16+
/// <para>The <see cref="Func{TKey, TValue}"/> can be also useful in cases we want to throw a specific exception when the key is not found.</para>
17+
/// </summary>
18+
/// <typeparam name="TKey"></typeparam>
19+
/// <typeparam name="TValue"></typeparam>
20+
public class DictionaryWithDefaultValue<TKey, TValue> : IDictionary<TKey, TValue>
21+
{
22+
private readonly Dictionary<TKey, TValue> _backingDictionary;
23+
private readonly TValue _defaultValue;
24+
private readonly Func<TKey, TValue> _defaultValueFunc;
25+
26+
public DictionaryWithDefaultValue(TValue defaultValue)
27+
{
28+
_backingDictionary = new Dictionary<TKey, TValue>();
29+
_defaultValue = defaultValue;
30+
_defaultValueFunc = null;
31+
}
32+
33+
public DictionaryWithDefaultValue(IDictionary<TKey, TValue> sourceDictionary, TValue defaultValue)
34+
{
35+
_backingDictionary = sourceDictionary != null ? new Dictionary<TKey, TValue>(sourceDictionary) : throw new ArgumentNullException(nameof(sourceDictionary));
36+
_defaultValue = defaultValue;
37+
_defaultValueFunc = null;
38+
}
39+
40+
public DictionaryWithDefaultValue(Func<TKey, TValue> defaultValueFunc)
41+
{
42+
_backingDictionary = new Dictionary<TKey, TValue>();
43+
_defaultValue = default(TValue);
44+
_defaultValueFunc = defaultValueFunc ?? throw new ArgumentNullException(nameof(defaultValueFunc));
45+
}
46+
47+
public DictionaryWithDefaultValue(IDictionary<TKey, TValue> sourceDictionary, Func<TKey, TValue> defaultValueFunc)
48+
{
49+
_backingDictionary = sourceDictionary != null ? new Dictionary<TKey, TValue>(sourceDictionary) : throw new ArgumentNullException(nameof(sourceDictionary));
50+
_defaultValue = default(TValue);
51+
_defaultValueFunc = defaultValueFunc ?? throw new ArgumentNullException(nameof(defaultValueFunc));
52+
}
53+
54+
public TValue this[TKey key]
55+
{
56+
get => _backingDictionary.TryGetValue(key, out var value) ? value : _defaultValueFunc != null ? _defaultValueFunc(key) : _defaultValue;
57+
set => _backingDictionary[key] = value;
58+
}
59+
60+
public ICollection<TKey> Keys => _backingDictionary.Keys;
61+
62+
public ICollection<TValue> Values => _backingDictionary.Values;
63+
64+
public int Count => _backingDictionary.Count;
65+
66+
public bool IsReadOnly => ((IDictionary<TKey, TValue>)_backingDictionary).IsReadOnly;
67+
68+
public void Add(TKey key, TValue value) => _backingDictionary.Add(key, value);
69+
70+
public void Add(KeyValuePair<TKey, TValue> item) => ((IDictionary<TKey, TValue>)_backingDictionary).Add(item);
71+
72+
public void Clear() => _backingDictionary.Clear();
73+
74+
public bool Contains(KeyValuePair<TKey, TValue> item) => ((IDictionary<TKey, TValue>)_backingDictionary).Contains(item);
75+
76+
public bool ContainsKey(TKey key) => _backingDictionary.ContainsKey(key);
77+
78+
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) => ((IDictionary<TKey, TValue>)_backingDictionary).CopyTo(array, arrayIndex);
79+
80+
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => _backingDictionary.GetEnumerator();
81+
82+
public bool Remove(TKey key) => _backingDictionary.Remove(key);
83+
84+
public bool Remove(KeyValuePair<TKey, TValue> item) => ((IDictionary<TKey, TValue>)_backingDictionary).Remove(item);
85+
86+
public bool TryGetValue(TKey key, out TValue value) => _backingDictionary.TryGetValue(key, out value);
87+
88+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
89+
}
90+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp1.1</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
9+
<PackageReference Include="xunit" Version="2.2.0" />
10+
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<ProjectReference Include="..\..\src\CodingMilitia.MoarCollections\CodingMilitia.MoarCollections.csproj" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
19+
</ItemGroup>
20+
21+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using CodingMilitia.MoarCollections.Dictionaries;
2+
using System;
3+
using System.Collections.Generic;
4+
using Xunit;
5+
6+
namespace CodingMilitia.MoarCollections.Tests.Dictionaries
7+
{
8+
public class DictionaryWithDefaultValueTests
9+
{
10+
private const int DefaultValue = 666;
11+
private static readonly Func<int, int> DefaultValueFunc = (key) => DefaultValue;
12+
private static IDictionary<int, int> SampleDictionary = new Dictionary<int, int>
13+
{
14+
[0] = 0,
15+
[1] = 111
16+
};
17+
18+
[Fact]
19+
public void WhenKeyIsInDictionaryItIsReturnedRegardlessOfDefaultValue()
20+
{
21+
var dictionary = new DictionaryWithDefaultValue<int, int>(SampleDictionary, DefaultValue);
22+
Assert.Equal(111, dictionary[1]);
23+
Assert.NotEqual(DefaultValue, dictionary[1]);
24+
}
25+
26+
[Fact]
27+
public void WhenKeyIsInDictionaryItIsReturnedRegardlessOfDefaultValueFunc()
28+
{
29+
var dictionary = new DictionaryWithDefaultValue<int, int>(SampleDictionary, DefaultValueFunc);
30+
31+
Assert.Equal(111, dictionary[1]);
32+
Assert.NotEqual(DefaultValue, dictionary[1]);
33+
}
34+
35+
[Fact]
36+
public void WhenKeyIsNotInDictionaryItReturnsTheDefaultValue()
37+
{
38+
var dictionary = new DictionaryWithDefaultValue<int, int>(SampleDictionary, DefaultValue);
39+
Assert.Equal(DefaultValue, dictionary[6]);
40+
}
41+
42+
[Fact]
43+
public void WhenKeyIsNotInDictionaryItReturnsTheDefaultValueFuncResult()
44+
{
45+
var dictionary = new DictionaryWithDefaultValue<int, int>(SampleDictionary, DefaultValueFunc);
46+
Assert.Equal(DefaultValue, dictionary[6]);
47+
}
48+
49+
[Fact]
50+
public void WhenKeyIsNotInDictionaryAndDefaultValueFuncThrowsItIsPropagated()
51+
{
52+
var dictionary = new DictionaryWithDefaultValue<int, int>(SampleDictionary, (key) => throw new InvalidOperationException("Test"));
53+
Assert.Throws<InvalidOperationException>(() => dictionary[6]);
54+
}
55+
56+
[Fact]
57+
public void WhenNullSourceDictionaryIsProvidedAnArgumentNullExceptionIsThrown()
58+
{
59+
Assert.Throws<ArgumentNullException>("sourceDictionary", () => new DictionaryWithDefaultValue<int, int>(null, 0));
60+
Assert.Throws<ArgumentNullException>("sourceDictionary", () => new DictionaryWithDefaultValue<int, int>(null, (key) => 0));
61+
}
62+
63+
[Fact]
64+
public void WhenNullDefaultValueFuncIsProvidedAnArgumentNullExceptionIsThrown()
65+
{
66+
Assert.Throws<ArgumentNullException>("defaultValueFunc", () => new DictionaryWithDefaultValue<int, int>((Func<int, int>)null));
67+
Assert.Throws<ArgumentNullException>("defaultValueFunc", () => new DictionaryWithDefaultValue<int, int>(SampleDictionary, (Func<int, int>)null));
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)