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;
|
||||
}
|
||||
|
||||
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);
|
||||
if (index >= 0)
|
||||
|
|
@ -404,6 +406,48 @@ namespace Microsoft.AspNetCore.Routing
|
|||
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>
|
||||
/// Attempts to the add the provided <paramref name="key"/> and <paramref name="value"/> to the dictionary.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -471,11 +471,7 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
else if (part is RoutePatternParameterPart parameterPart)
|
||||
{
|
||||
// If it's a parameter, get its value
|
||||
var hasValue = acceptedValues.TryGetValue(parameterPart.Name, out var value);
|
||||
if (hasValue)
|
||||
{
|
||||
acceptedValues.Remove(parameterPart.Name);
|
||||
}
|
||||
acceptedValues.Remove(parameterPart.Name, out var value);
|
||||
|
||||
var isSameAsDefault = false;
|
||||
if (_defaults != null &&
|
||||
|
|
|
|||
|
|
@ -1393,6 +1393,235 @@ namespace Microsoft.AspNetCore.Routing.Tests
|
|||
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]
|
||||
public void TryAdd_EmptyStringIsAllowed()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue