Implement Keys and Values as views
This commit is contained in:
parent
e58cdbd214
commit
3f45d7c11e
@ -477,6 +477,77 @@ namespace Google.Protobuf.Collections
|
||||
Assert.IsTrue(new MapField<int, int?>(true).AllowsNullValues);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void KeysReturnsLiveView()
|
||||
{
|
||||
var map = new MapField<string, string>();
|
||||
var keys = map.Keys;
|
||||
CollectionAssert.AreEqual(new string[0], keys);
|
||||
map["foo"] = "bar";
|
||||
map["x"] = "y";
|
||||
CollectionAssert.AreEqual(new[] { "foo", "x" }, keys);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValuesReturnsLiveView()
|
||||
{
|
||||
var map = new MapField<string, string>();
|
||||
var values = map.Values;
|
||||
CollectionAssert.AreEqual(new string[0], values);
|
||||
map["foo"] = "bar";
|
||||
map["x"] = "y";
|
||||
CollectionAssert.AreEqual(new[] { "bar", "y" }, values);
|
||||
}
|
||||
|
||||
// Just test keys - we know the implementation is the same for values
|
||||
[Test]
|
||||
public void ViewsAreReadOnly()
|
||||
{
|
||||
var map = new MapField<string, string>();
|
||||
var keys = map.Keys;
|
||||
Assert.IsTrue(keys.IsReadOnly);
|
||||
Assert.Throws<NotSupportedException>(() => keys.Clear());
|
||||
Assert.Throws<NotSupportedException>(() => keys.Remove("a"));
|
||||
Assert.Throws<NotSupportedException>(() => keys.Add("a"));
|
||||
}
|
||||
|
||||
// Just test keys - we know the implementation is the same for values
|
||||
[Test]
|
||||
public void ViewCopyTo()
|
||||
{
|
||||
var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } };
|
||||
var keys = map.Keys;
|
||||
var array = new string[4];
|
||||
Assert.Throws<ArgumentException>(() => keys.CopyTo(array, 3));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => keys.CopyTo(array, -1));
|
||||
keys.CopyTo(array, 1);
|
||||
CollectionAssert.AreEqual(new[] { null, "foo", "x", null }, array);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void KeysContains()
|
||||
{
|
||||
var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } };
|
||||
var keys = map.Keys;
|
||||
Assert.IsTrue(keys.Contains("foo"));
|
||||
Assert.IsFalse(keys.Contains("bar")); // It's a value!
|
||||
Assert.IsFalse(keys.Contains("1"));
|
||||
// Keys can't be null, so we should prevent contains check
|
||||
Assert.Throws<ArgumentNullException>(() => keys.Contains(null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValuesContains()
|
||||
{
|
||||
var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } };
|
||||
var values = map.Values;
|
||||
Assert.IsTrue(values.Contains("bar"));
|
||||
Assert.IsFalse(values.Contains("foo")); // It's a key!
|
||||
Assert.IsFalse(values.Contains("1"));
|
||||
// Values can be null, so this makes sense
|
||||
Assert.IsFalse(values.Contains(null));
|
||||
}
|
||||
|
||||
private static KeyValuePair<TKey, TValue> NewKeyValuePair<TKey, TValue>(TKey key, TValue value)
|
||||
{
|
||||
return new KeyValuePair<TKey, TValue>(key, value);
|
||||
|
@ -136,6 +136,12 @@ namespace Google.Protobuf.Collections
|
||||
return map.ContainsKey(key);
|
||||
}
|
||||
|
||||
private bool ContainsValue(TValue value)
|
||||
{
|
||||
var comparer = EqualityComparer<TValue>.Default;
|
||||
return list.Any(pair => comparer.Equals(pair.Value, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the entry identified by the given key from the map.
|
||||
/// </summary>
|
||||
@ -221,17 +227,15 @@ namespace Google.Protobuf.Collections
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make these views?
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection containing the keys in the map.
|
||||
/// </summary>
|
||||
public ICollection<TKey> Keys { get { return list.Select(t => t.Key).ToList(); } }
|
||||
public ICollection<TKey> Keys { get { return new MapView<TKey>(this, pair => pair.Key, ContainsKey); } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection containing the values in the map.
|
||||
/// </summary>
|
||||
public ICollection<TValue> Values { get { return list.Select(t => t.Value).ToList(); } }
|
||||
public ICollection<TValue> Values { get { return new MapView<TValue>(this, pair => pair.Value, ContainsValue); } }
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified entries to the map.
|
||||
@ -658,5 +662,81 @@ namespace Google.Protobuf.Collections
|
||||
MessageDescriptor IMessage.Descriptor { get { return null; } }
|
||||
}
|
||||
}
|
||||
|
||||
private class MapView<T> : ICollection<T>, ICollection
|
||||
{
|
||||
private readonly MapField<TKey, TValue> parent;
|
||||
private readonly Func<KeyValuePair<TKey, TValue>, T> projection;
|
||||
private readonly Func<T, bool> containsCheck;
|
||||
|
||||
internal MapView(
|
||||
MapField<TKey, TValue> parent,
|
||||
Func<KeyValuePair<TKey, TValue>, T> projection,
|
||||
Func<T, bool> containsCheck)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.projection = projection;
|
||||
this.containsCheck = containsCheck;
|
||||
}
|
||||
|
||||
public int Count { get { return parent.Count; } }
|
||||
|
||||
public bool IsReadOnly { get { return true; } }
|
||||
|
||||
public bool IsSynchronized { get { return false; } }
|
||||
|
||||
public object SyncRoot { get { return parent; } }
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return containsCheck(item);
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
if (arrayIndex < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("arrayIndex");
|
||||
}
|
||||
if (arrayIndex + Count >= array.Length)
|
||||
{
|
||||
throw new ArgumentException("Not enough space in the array", "array");
|
||||
}
|
||||
foreach (var item in this)
|
||||
{
|
||||
array[arrayIndex++] = item;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return parent.list.Select(projection).GetEnumerator();
|
||||
}
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user