#177 Immutable HeaderValue objects.
This commit is contained in:
parent
12b78a31bf
commit
652d885402
|
|
@ -228,5 +228,13 @@ namespace Microsoft.Net.Http.Headers
|
||||||
}
|
}
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void ThrowIfReadOnly(bool isReadOnly)
|
||||||
|
{
|
||||||
|
if (isReadOnly)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The object cannot be modified because it is read-only.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Microsoft.Net.Http.Headers
|
namespace Microsoft.Net.Http.Headers
|
||||||
|
|
@ -19,9 +20,10 @@ namespace Microsoft.Net.Http.Headers
|
||||||
private static readonly HttpHeaderParser<MediaTypeHeaderValue> MultipleValueParser
|
private static readonly HttpHeaderParser<MediaTypeHeaderValue> MultipleValueParser
|
||||||
= new GenericHeaderParser<MediaTypeHeaderValue>(true, GetMediaTypeLength);
|
= new GenericHeaderParser<MediaTypeHeaderValue>(true, GetMediaTypeLength);
|
||||||
|
|
||||||
// Use list instead of dictionary since we may have multiple parameters with the same name.
|
// Use a collection instead of a dictionary since we may have multiple parameters with the same name.
|
||||||
private ICollection<NameValueHeaderValue> _parameters;
|
private ObjectCollection<NameValueHeaderValue> _parameters;
|
||||||
private string _mediaType;
|
private string _mediaType;
|
||||||
|
private bool _isReadOnly;
|
||||||
|
|
||||||
private MediaTypeHeaderValue()
|
private MediaTypeHeaderValue()
|
||||||
{
|
{
|
||||||
|
|
@ -48,6 +50,7 @@ namespace Microsoft.Net.Http.Headers
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
HeaderUtilities.ThrowIfReadOnly(IsReadOnly);
|
||||||
// We don't prevent a user from setting whitespace-only charsets. Like we can't prevent a user from
|
// We don't prevent a user from setting whitespace-only charsets. Like we can't prevent a user from
|
||||||
// setting a non-existing charset.
|
// setting a non-existing charset.
|
||||||
var charsetParameter = NameValueHeaderValue.Find(_parameters, CharsetString);
|
var charsetParameter = NameValueHeaderValue.Find(_parameters, CharsetString);
|
||||||
|
|
@ -93,6 +96,7 @@ namespace Microsoft.Net.Http.Headers
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
HeaderUtilities.ThrowIfReadOnly(IsReadOnly);
|
||||||
if (value == null)
|
if (value == null)
|
||||||
{
|
{
|
||||||
Charset = null;
|
Charset = null;
|
||||||
|
|
@ -112,6 +116,7 @@ namespace Microsoft.Net.Http.Headers
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
HeaderUtilities.ThrowIfReadOnly(IsReadOnly);
|
||||||
var boundaryParameter = NameValueHeaderValue.Find(_parameters, BoundaryString);
|
var boundaryParameter = NameValueHeaderValue.Find(_parameters, BoundaryString);
|
||||||
if (string.IsNullOrEmpty(value))
|
if (string.IsNullOrEmpty(value))
|
||||||
{
|
{
|
||||||
|
|
@ -141,7 +146,14 @@ namespace Microsoft.Net.Http.Headers
|
||||||
{
|
{
|
||||||
if (_parameters == null)
|
if (_parameters == null)
|
||||||
{
|
{
|
||||||
_parameters = new ObjectCollection<NameValueHeaderValue>();
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
_parameters = ObjectCollection<NameValueHeaderValue>.EmptyReadOnlyCollection;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_parameters = new ObjectCollection<NameValueHeaderValue>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return _parameters;
|
return _parameters;
|
||||||
}
|
}
|
||||||
|
|
@ -158,6 +170,7 @@ namespace Microsoft.Net.Http.Headers
|
||||||
get { return _mediaType; }
|
get { return _mediaType; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
HeaderUtilities.ThrowIfReadOnly(IsReadOnly);
|
||||||
CheckMediaTypeFormat(value, "value");
|
CheckMediaTypeFormat(value, "value");
|
||||||
_mediaType = value;
|
_mediaType = value;
|
||||||
}
|
}
|
||||||
|
|
@ -201,6 +214,11 @@ namespace Microsoft.Net.Http.Headers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly
|
||||||
|
{
|
||||||
|
get { return _isReadOnly; }
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsSubsetOf(MediaTypeHeaderValue otherMediaType)
|
public bool IsSubsetOf(MediaTypeHeaderValue otherMediaType)
|
||||||
{
|
{
|
||||||
if (otherMediaType == null)
|
if (otherMediaType == null)
|
||||||
|
|
@ -253,22 +271,47 @@ namespace Microsoft.Net.Http.Headers
|
||||||
/// while avoiding the cost of revalidating the components.
|
/// while avoiding the cost of revalidating the components.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A deep copy.</returns>
|
/// <returns>A deep copy.</returns>
|
||||||
public MediaTypeHeaderValue Clone()
|
public MediaTypeHeaderValue Copy()
|
||||||
{
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
var other = new MediaTypeHeaderValue();
|
var other = new MediaTypeHeaderValue();
|
||||||
other._mediaType = _mediaType;
|
other._mediaType = _mediaType;
|
||||||
|
|
||||||
if (_parameters != null)
|
if (_parameters != null)
|
||||||
{
|
{
|
||||||
other._parameters = new ObjectCollection<NameValueHeaderValue>();
|
other._parameters = new ObjectCollection<NameValueHeaderValue>(
|
||||||
foreach (var pair in _parameters)
|
_parameters.Select(item => item.Copy()));
|
||||||
{
|
|
||||||
other._parameters.Add(pair.Clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return other;
|
return other;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs a deep copy of this object and all of it's NameValueHeaderValue sub components,
|
||||||
|
/// while avoiding the cost of revalidating the components. This copy is read-only.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A deep, read-only, copy.</returns>
|
||||||
|
public MediaTypeHeaderValue CopyAsReadOnly()
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
var other = new MediaTypeHeaderValue();
|
||||||
|
other._mediaType = _mediaType;
|
||||||
|
if (_parameters != null)
|
||||||
|
{
|
||||||
|
other._parameters = new ObjectCollection<NameValueHeaderValue>(
|
||||||
|
_parameters.Select(item => item.CopyAsReadOnly()), isReadOnly: true);
|
||||||
|
}
|
||||||
|
other._isReadOnly = true;
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return _mediaType + NameValueHeaderValue.ToString(_parameters, ';', true);
|
return _mediaType + NameValueHeaderValue.ToString(_parameters, ';', true);
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ namespace Microsoft.Net.Http.Headers
|
||||||
|
|
||||||
private string _name;
|
private string _name;
|
||||||
private string _value;
|
private string _value;
|
||||||
|
private bool _isReadOnly;
|
||||||
|
|
||||||
private NameValueHeaderValue()
|
private NameValueHeaderValue()
|
||||||
{
|
{
|
||||||
|
|
@ -49,17 +50,25 @@ namespace Microsoft.Net.Http.Headers
|
||||||
get { return _value; }
|
get { return _value; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
HeaderUtilities.ThrowIfReadOnly(IsReadOnly);
|
||||||
CheckValueFormat(value);
|
CheckValueFormat(value);
|
||||||
_value = value;
|
_value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly { get { return _isReadOnly; } }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides a copy of this object without the cost of re-validating the values.
|
/// Provides a copy of this object without the cost of re-validating the values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A copy.</returns>
|
/// <returns>A copy.</returns>
|
||||||
public NameValueHeaderValue Clone()
|
public NameValueHeaderValue Copy()
|
||||||
{
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
return new NameValueHeaderValue()
|
return new NameValueHeaderValue()
|
||||||
{
|
{
|
||||||
_name = _name,
|
_name = _name,
|
||||||
|
|
@ -67,6 +76,21 @@ namespace Microsoft.Net.Http.Headers
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NameValueHeaderValue CopyAsReadOnly()
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NameValueHeaderValue()
|
||||||
|
{
|
||||||
|
_name = _name,
|
||||||
|
_value = _value,
|
||||||
|
_isReadOnly = true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
Contract.Assert(_name != null);
|
Contract.Assert(_name != null);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Microsoft.Net.Http.Headers
|
namespace Microsoft.Net.Http.Headers
|
||||||
|
|
@ -10,40 +12,74 @@ namespace Microsoft.Net.Http.Headers
|
||||||
// type to throw if 'null' gets added. Collection<T> internally uses List<T> which comes at some cost. In addition
|
// type to throw if 'null' gets added. Collection<T> internally uses List<T> which comes at some cost. In addition
|
||||||
// Collection<T>.Add() calls List<T>.InsertItem() which is an O(n) operation (compared to O(1) for List<T>.Add()).
|
// Collection<T>.Add() calls List<T>.InsertItem() which is an O(n) operation (compared to O(1) for List<T>.Add()).
|
||||||
// This type is only used for very small collections (1-2 items) to keep the impact of using Collection<T> small.
|
// This type is only used for very small collections (1-2 items) to keep the impact of using Collection<T> small.
|
||||||
internal class ObjectCollection<T> : Collection<T> where T : class
|
internal class ObjectCollection<T> : ICollection<T> where T : class
|
||||||
{
|
{
|
||||||
private static readonly Action<T> DefaultValidator = CheckNotNull;
|
internal static readonly Action<T> DefaultValidator = CheckNotNull;
|
||||||
|
internal static readonly ObjectCollection<T> EmptyReadOnlyCollection
|
||||||
|
= new ObjectCollection<T>(DefaultValidator, isReadOnly: true);
|
||||||
|
|
||||||
private Action<T> _validator;
|
private readonly Collection<T> _collection = new Collection<T>();
|
||||||
|
private readonly Action<T> _validator;
|
||||||
|
private readonly bool _isReadOnly;
|
||||||
|
|
||||||
public ObjectCollection()
|
public ObjectCollection()
|
||||||
: this(DefaultValidator)
|
: this(DefaultValidator)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectCollection(Action<T> validator)
|
public ObjectCollection(Action<T> validator, bool isReadOnly = false)
|
||||||
{
|
{
|
||||||
_validator = validator;
|
_validator = validator;
|
||||||
|
_isReadOnly = isReadOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void InsertItem(int index, T item)
|
public ObjectCollection(IEnumerable<T> other, bool isReadOnly = false)
|
||||||
{
|
{
|
||||||
if (_validator != null)
|
_validator = DefaultValidator;
|
||||||
|
foreach (T item in other)
|
||||||
{
|
{
|
||||||
_validator(item);
|
Add(item);
|
||||||
}
|
}
|
||||||
base.InsertItem(index, item);
|
_isReadOnly = isReadOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SetItem(int index, T item)
|
public int Count
|
||||||
{
|
{
|
||||||
if (_validator != null)
|
get { return _collection.Count; }
|
||||||
{
|
|
||||||
_validator(item);
|
|
||||||
}
|
|
||||||
base.SetItem(index, item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsReadOnly
|
||||||
|
{
|
||||||
|
get { return _isReadOnly; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(T item)
|
||||||
|
{
|
||||||
|
HeaderUtilities.ThrowIfReadOnly(IsReadOnly);
|
||||||
|
_validator(item);
|
||||||
|
_collection.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(T item)
|
||||||
|
{
|
||||||
|
HeaderUtilities.ThrowIfReadOnly(IsReadOnly);
|
||||||
|
return _collection.Remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
HeaderUtilities.ThrowIfReadOnly(IsReadOnly);
|
||||||
|
_collection.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(T item) => _collection.Contains(item);
|
||||||
|
|
||||||
|
public void CopyTo(T[] array, int arrayIndex) => _collection.CopyTo(array, arrayIndex);
|
||||||
|
|
||||||
|
public IEnumerator<T> GetEnumerator() => _collection.GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => _collection.GetEnumerator();
|
||||||
|
|
||||||
private static void CheckNotNull(T item)
|
private static void CheckNotNull(T item)
|
||||||
{
|
{
|
||||||
// null values cannot be added to the collection.
|
// null values cannot be added to the collection.
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
"System.Diagnostics.Contracts": "4.0.0-beta-*",
|
"System.Diagnostics.Contracts": "4.0.0-beta-*",
|
||||||
"System.Globalization": "4.0.10-beta-*",
|
"System.Globalization": "4.0.10-beta-*",
|
||||||
"System.Globalization.Extensions": "4.0.0-beta-*",
|
"System.Globalization.Extensions": "4.0.0-beta-*",
|
||||||
|
"System.Linq": "4.0.0-beta-*",
|
||||||
"System.Text.Encoding": "4.0.10-beta-*",
|
"System.Text.Encoding": "4.0.10-beta-*",
|
||||||
"System.Runtime": "4.0.20-beta-*"
|
"System.Runtime": "4.0.20-beta-*"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,5 +20,5 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resources": "..\\..\\unicode\\UnicodeData.txt"
|
"resource": "..\\..\\unicode\\UnicodeData.txt"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,10 +65,10 @@ namespace Microsoft.Net.Http.Headers
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Clone_SimpleMediaType_Copied()
|
public void Copy_SimpleMediaType_Copied()
|
||||||
{
|
{
|
||||||
var mediaType0 = new MediaTypeHeaderValue("text/plain");
|
var mediaType0 = new MediaTypeHeaderValue("text/plain");
|
||||||
var mediaType1 = mediaType0.Clone();
|
var mediaType1 = mediaType0.Copy();
|
||||||
Assert.NotSame(mediaType0, mediaType1);
|
Assert.NotSame(mediaType0, mediaType1);
|
||||||
Assert.Same(mediaType0.MediaType, mediaType1.MediaType);
|
Assert.Same(mediaType0.MediaType, mediaType1.MediaType);
|
||||||
Assert.NotSame(mediaType0.Parameters, mediaType1.Parameters);
|
Assert.NotSame(mediaType0.Parameters, mediaType1.Parameters);
|
||||||
|
|
@ -76,11 +76,26 @@ namespace Microsoft.Net.Http.Headers
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Clone_WithParameters_Copied()
|
public void CopyAsReadOnly_SimpleMediaType_CopiedAndReadOnly()
|
||||||
|
{
|
||||||
|
var mediaType0 = new MediaTypeHeaderValue("text/plain");
|
||||||
|
var mediaType1 = mediaType0.CopyAsReadOnly();
|
||||||
|
Assert.NotSame(mediaType0, mediaType1);
|
||||||
|
Assert.Same(mediaType0.MediaType, mediaType1.MediaType);
|
||||||
|
Assert.NotSame(mediaType0.Parameters, mediaType1.Parameters);
|
||||||
|
Assert.Equal(mediaType0.Parameters.Count, mediaType1.Parameters.Count);
|
||||||
|
|
||||||
|
Assert.False(mediaType0.IsReadOnly);
|
||||||
|
Assert.True(mediaType1.IsReadOnly);
|
||||||
|
Assert.Throws<InvalidOperationException>(() => { mediaType1.MediaType = "some/value"; });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Copy_WithParameters_Copied()
|
||||||
{
|
{
|
||||||
var mediaType0 = new MediaTypeHeaderValue("text/plain");
|
var mediaType0 = new MediaTypeHeaderValue("text/plain");
|
||||||
mediaType0.Parameters.Add(new NameValueHeaderValue("name", "value"));
|
mediaType0.Parameters.Add(new NameValueHeaderValue("name", "value"));
|
||||||
var mediaType1 = mediaType0.Clone();
|
var mediaType1 = mediaType0.Copy();
|
||||||
Assert.NotSame(mediaType0, mediaType1);
|
Assert.NotSame(mediaType0, mediaType1);
|
||||||
Assert.Same(mediaType0.MediaType, mediaType1.MediaType);
|
Assert.Same(mediaType0.MediaType, mediaType1.MediaType);
|
||||||
Assert.NotSame(mediaType0.Parameters, mediaType1.Parameters);
|
Assert.NotSame(mediaType0.Parameters, mediaType1.Parameters);
|
||||||
|
|
@ -92,6 +107,34 @@ namespace Microsoft.Net.Http.Headers
|
||||||
Assert.Same(pair0.Value, pair1.Value);
|
Assert.Same(pair0.Value, pair1.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CopyAsReadOnly_WithParameters_CopiedAndReadOnly()
|
||||||
|
{
|
||||||
|
var mediaType0 = new MediaTypeHeaderValue("text/plain");
|
||||||
|
mediaType0.Parameters.Add(new NameValueHeaderValue("name", "value"));
|
||||||
|
var mediaType1 = mediaType0.CopyAsReadOnly();
|
||||||
|
Assert.NotSame(mediaType0, mediaType1);
|
||||||
|
Assert.False(mediaType0.IsReadOnly);
|
||||||
|
Assert.True(mediaType1.IsReadOnly);
|
||||||
|
Assert.Same(mediaType0.MediaType, mediaType1.MediaType);
|
||||||
|
|
||||||
|
Assert.NotSame(mediaType0.Parameters, mediaType1.Parameters);
|
||||||
|
Assert.False(mediaType0.Parameters.IsReadOnly);
|
||||||
|
Assert.True(mediaType1.Parameters.IsReadOnly);
|
||||||
|
Assert.Equal(mediaType0.Parameters.Count, mediaType1.Parameters.Count);
|
||||||
|
Assert.Throws<InvalidOperationException>(() => mediaType1.Parameters.Add(new NameValueHeaderValue("name")));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => mediaType1.Parameters.Remove(new NameValueHeaderValue("name")));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => mediaType1.Parameters.Clear());
|
||||||
|
|
||||||
|
var pair0 = mediaType0.Parameters.First();
|
||||||
|
var pair1 = mediaType1.Parameters.First();
|
||||||
|
Assert.NotSame(pair0, pair1);
|
||||||
|
Assert.False(pair0.IsReadOnly);
|
||||||
|
Assert.True(pair1.IsReadOnly);
|
||||||
|
Assert.Same(pair0.Name, pair1.Name);
|
||||||
|
Assert.Same(pair0.Value, pair1.Value);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MediaType_SetAndGetMediaType_MatchExpectations()
|
public void MediaType_SetAndGetMediaType_MatchExpectations()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -60,29 +60,71 @@ namespace Microsoft.Net.Http.Headers
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Clone_NameOnly_SuccesfullyCopied()
|
public void Copy_NameOnly_SuccesfullyCopied()
|
||||||
{
|
{
|
||||||
var pair0 = new NameValueHeaderValue("name");
|
var pair0 = new NameValueHeaderValue("name");
|
||||||
var pair1 = pair0.Clone();
|
var pair1 = pair0.Copy();
|
||||||
Assert.NotSame(pair0, pair1);
|
Assert.NotSame(pair0, pair1);
|
||||||
Assert.Same(pair0.Name, pair1.Name);
|
Assert.Same(pair0.Name, pair1.Name);
|
||||||
Assert.Null(pair0.Value);
|
Assert.Null(pair0.Value);
|
||||||
Assert.Null(pair1.Value);
|
Assert.Null(pair1.Value);
|
||||||
|
|
||||||
|
// Change one value and verify the other is unchanged.
|
||||||
|
pair0.Value = "othervalue";
|
||||||
|
Assert.Equal("othervalue", pair0.Value);
|
||||||
|
Assert.Equal(null, pair1.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Clone_NameAndValue_SuccesfullyCopied()
|
public void CopyAsReadOnly_NameOnly_CopiedAndReadOnly()
|
||||||
|
{
|
||||||
|
var pair0 = new NameValueHeaderValue("name");
|
||||||
|
var pair1 = pair0.CopyAsReadOnly();
|
||||||
|
Assert.NotSame(pair0, pair1);
|
||||||
|
Assert.Same(pair0.Name, pair1.Name);
|
||||||
|
Assert.Null(pair0.Value);
|
||||||
|
Assert.Null(pair1.Value);
|
||||||
|
Assert.False(pair0.IsReadOnly);
|
||||||
|
Assert.True(pair1.IsReadOnly);
|
||||||
|
|
||||||
|
// Change one value and verify the other is unchanged.
|
||||||
|
pair0.Value = "othervalue";
|
||||||
|
Assert.Equal("othervalue", pair0.Value);
|
||||||
|
Assert.Equal(null, pair1.Value);
|
||||||
|
Assert.Throws<InvalidOperationException>(() => { pair1.Value = "othervalue"; });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Copy_NameAndValue_SuccesfullyCopied()
|
||||||
{
|
{
|
||||||
var pair0 = new NameValueHeaderValue("name", "value");
|
var pair0 = new NameValueHeaderValue("name", "value");
|
||||||
var pair1 = pair0.Clone();
|
var pair1 = pair0.Copy();
|
||||||
Assert.NotSame(pair0, pair1);
|
Assert.NotSame(pair0, pair1);
|
||||||
Assert.Same(pair0.Name, pair1.Name);
|
Assert.Same(pair0.Name, pair1.Name);
|
||||||
Assert.Same(pair0.Value, pair1.Value);
|
Assert.Same(pair0.Value, pair1.Value);
|
||||||
|
|
||||||
// Change one value and verify the other is unchanged.
|
// Change one value and verify the other is unchanged.
|
||||||
pair1.Value = "othervalue";
|
pair0.Value = "othervalue";
|
||||||
Assert.Equal("value", pair0.Value);
|
Assert.Equal("othervalue", pair0.Value);
|
||||||
Assert.Equal("othervalue", pair1.Value);
|
Assert.Equal("value", pair1.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CopyAsReadOnly_NameAndValue_CopiedAndReadOnly()
|
||||||
|
{
|
||||||
|
var pair0 = new NameValueHeaderValue("name", "value");
|
||||||
|
var pair1 = pair0.CopyAsReadOnly();
|
||||||
|
Assert.NotSame(pair0, pair1);
|
||||||
|
Assert.Same(pair0.Name, pair1.Name);
|
||||||
|
Assert.Same(pair0.Value, pair1.Value);
|
||||||
|
Assert.False(pair0.IsReadOnly);
|
||||||
|
Assert.True(pair1.IsReadOnly);
|
||||||
|
|
||||||
|
// Change one value and verify the other is unchanged.
|
||||||
|
pair0.Value = "othervalue";
|
||||||
|
Assert.Equal("othervalue", pair0.Value);
|
||||||
|
Assert.Equal("value", pair1.Value);
|
||||||
|
Assert.Throws<InvalidOperationException>(() => { pair1.Value = "othervalue"; });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue