Add Remove(string key, out object value) overload to RouteValueDictionary (#858)
* Add Remove(string key, out object value) overload to RouteValueDictionary. * Consistently use _count field instead of Count property in Remove overloads. Added comment on EnsureCapacity call. Added test for removing first/middle/last entry.
This commit is contained in:
parent
e8b2c9337e
commit
5c31f1f455
|
|
@ -388,7 +388,9 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureCapacity(Count);
|
// Ensure property storage is converted to array storage as we'll be
|
||||||
|
// applying the lookup and removal on the array
|
||||||
|
EnsureCapacity(_count);
|
||||||
|
|
||||||
var index = FindIndex(key);
|
var index = FindIndex(key);
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
|
|
@ -404,6 +406,48 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to remove and return the value that has the specified key from the <see cref="RouteValueDictionary"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the element to remove and return.</param>
|
||||||
|
/// <param name="value">When this method returns, contains the object removed from the <see cref="RouteValueDictionary"/>, or <c>null</c> if key does not exist.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the object was removed successfully; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public bool Remove(string key, out object value)
|
||||||
|
{
|
||||||
|
if (key == null)
|
||||||
|
{
|
||||||
|
ThrowArgumentNullExceptionForKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_count == 0)
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure property storage is converted to array storage as we'll be
|
||||||
|
// applying the lookup and removal on the array
|
||||||
|
EnsureCapacity(_count);
|
||||||
|
|
||||||
|
var index = FindIndex(key);
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
_count--;
|
||||||
|
var array = _arrayStorage;
|
||||||
|
value = array[index].Value;
|
||||||
|
Array.Copy(array, index + 1, array, index, _count - index);
|
||||||
|
array[_count] = default;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to the add the provided <paramref name="key"/> and <paramref name="value"/> to the dictionary.
|
/// Attempts to the add the provided <paramref name="key"/> and <paramref name="value"/> to the dictionary.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -471,11 +471,7 @@ namespace Microsoft.AspNetCore.Routing.Template
|
||||||
else if (part is RoutePatternParameterPart parameterPart)
|
else if (part is RoutePatternParameterPart parameterPart)
|
||||||
{
|
{
|
||||||
// If it's a parameter, get its value
|
// If it's a parameter, get its value
|
||||||
var hasValue = acceptedValues.TryGetValue(parameterPart.Name, out var value);
|
acceptedValues.Remove(parameterPart.Name, out var value);
|
||||||
if (hasValue)
|
|
||||||
{
|
|
||||||
acceptedValues.Remove(parameterPart.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
var isSameAsDefault = false;
|
var isSameAsDefault = false;
|
||||||
if (_defaults != null &&
|
if (_defaults != null &&
|
||||||
|
|
|
||||||
|
|
@ -1393,6 +1393,235 @@ namespace Microsoft.AspNetCore.Routing.Tests
|
||||||
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
|
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Remove_KeyAndOutValue_EmptyStorage()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var dict = new RouteValueDictionary();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = dict.Remove("key", out var removedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Null(removedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Remove_KeyAndOutValue_EmptyStringIsAllowed()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var dict = new RouteValueDictionary();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = dict.Remove("", out var removedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Null(removedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Remove_KeyAndOutValue_PropertyStorage_Empty()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var dict = new RouteValueDictionary(new { });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = dict.Remove("other", out var removedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Null(removedValue);
|
||||||
|
Assert.Empty(dict);
|
||||||
|
Assert.NotNull(dict._propertyStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Remove_KeyAndOutValue_PropertyStorage_False()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var dict = new RouteValueDictionary(new { key = "value" });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = dict.Remove("other", out var removedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Null(removedValue);
|
||||||
|
Assert.Collection(dict, kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
|
||||||
|
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Remove_KeyAndOutValue_PropertyStorage_True()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
object value = "value";
|
||||||
|
var dict = new RouteValueDictionary(new { key = value });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = dict.Remove("key", out var removedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Same(value, removedValue);
|
||||||
|
Assert.Empty(dict);
|
||||||
|
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Remove_KeyAndOutValue_PropertyStorage_True_CaseInsensitive()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
object value = "value";
|
||||||
|
var dict = new RouteValueDictionary(new { key = value });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = dict.Remove("kEy", out var removedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Same(value, removedValue);
|
||||||
|
Assert.Empty(dict);
|
||||||
|
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Remove_KeyAndOutValue_ListStorage_False()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var dict = new RouteValueDictionary()
|
||||||
|
{
|
||||||
|
{ "key", "value" },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = dict.Remove("other", out var removedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
Assert.Null(removedValue);
|
||||||
|
Assert.Collection(dict, kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
|
||||||
|
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Remove_KeyAndOutValue_ListStorage_True()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
object value = "value";
|
||||||
|
var dict = new RouteValueDictionary()
|
||||||
|
{
|
||||||
|
{ "key", value }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = dict.Remove("key", out var removedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Same(value, removedValue);
|
||||||
|
Assert.Empty(dict);
|
||||||
|
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Remove_KeyAndOutValue_ListStorage_True_CaseInsensitive()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
object value = "value";
|
||||||
|
var dict = new RouteValueDictionary()
|
||||||
|
{
|
||||||
|
{ "key", value }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = dict.Remove("kEy", out var removedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Same(value, removedValue);
|
||||||
|
Assert.Empty(dict);
|
||||||
|
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Remove_KeyAndOutValue_ListStorage_KeyExists_First()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
object value = "value";
|
||||||
|
var dict = new RouteValueDictionary()
|
||||||
|
{
|
||||||
|
{ "key", value },
|
||||||
|
{ "other", 5 },
|
||||||
|
{ "dotnet", "rocks" }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = dict.Remove("key", out var removedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Same(value, removedValue);
|
||||||
|
Assert.Equal(2, dict.Count);
|
||||||
|
Assert.False(dict.ContainsKey("key"));
|
||||||
|
Assert.True(dict.ContainsKey("other"));
|
||||||
|
Assert.True(dict.ContainsKey("dotnet"));
|
||||||
|
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Remove_KeyAndOutValue_ListStorage_KeyExists_Middle()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
object value = "value";
|
||||||
|
var dict = new RouteValueDictionary()
|
||||||
|
{
|
||||||
|
{ "other", 5 },
|
||||||
|
{ "key", value },
|
||||||
|
{ "dotnet", "rocks" }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = dict.Remove("key", out var removedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Same(value, removedValue);
|
||||||
|
Assert.Equal(2, dict.Count);
|
||||||
|
Assert.False(dict.ContainsKey("key"));
|
||||||
|
Assert.True(dict.ContainsKey("other"));
|
||||||
|
Assert.True(dict.ContainsKey("dotnet"));
|
||||||
|
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Remove_KeyAndOutValue_ListStorage_KeyExists_Last()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
object value = "value";
|
||||||
|
var dict = new RouteValueDictionary()
|
||||||
|
{
|
||||||
|
{ "other", 5 },
|
||||||
|
{ "dotnet", "rocks" },
|
||||||
|
{ "key", value }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = dict.Remove("key", out var removedValue);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
Assert.Same(value, removedValue);
|
||||||
|
Assert.Equal(2, dict.Count);
|
||||||
|
Assert.False(dict.ContainsKey("key"));
|
||||||
|
Assert.True(dict.ContainsKey("other"));
|
||||||
|
Assert.True(dict.ContainsKey("dotnet"));
|
||||||
|
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TryAdd_EmptyStringIsAllowed()
|
public void TryAdd_EmptyStringIsAllowed()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue