Added ListSegment class which is analogous to StringSegment and ArraySegment, but for IList<T>

git-svn-id: file:///srv/devel/repo-conversion/nusu@336 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2022-11-02 18:05:04 +00:00
parent 207a17712e
commit c7dd57f59f
5 changed files with 907 additions and 397 deletions

View File

@ -116,6 +116,8 @@
<Compile Include="Source\Collections\ItemReplaceEventArgs.Test.cs"> <Compile Include="Source\Collections\ItemReplaceEventArgs.Test.cs">
<DependentUpon>ItemReplaceEventArgs.cs</DependentUpon> <DependentUpon>ItemReplaceEventArgs.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Source\Collections\ListSegment.cs" />
<Compile Include="Source\Collections\ListSegment.Test.cs" />
<Compile Include="Source\Collections\MultiDictionary.cs" /> <Compile Include="Source\Collections\MultiDictionary.cs" />
<Compile Include="Source\Collections\MultiDictionary.ValueCollection.cs"> <Compile Include="Source\Collections\MultiDictionary.ValueCollection.cs">
<DependentUpon>MultiDictionary.cs</DependentUpon> <DependentUpon>MultiDictionary.cs</DependentUpon>

View File

@ -0,0 +1,257 @@
#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2017 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
#if UNITTEST
using NUnit.Framework;
namespace Nuclex.Support.Collections {
/// <summary>Unit Test for the list segment class</summary>
[TestFixture]
internal class ListSegmentTest {
/// <summary>
/// Tests whether the default constructor of the ListSegment class throws the
/// right exception when being passed 'null' instead of a list
/// </summary>
[Test]
public void SimpleConstructorThrowsWhenListIsNull() {
Assert.Throws<ArgumentNullException>(
delegate() { new ListSegment<int>(null); }
);
}
/// <summary>
/// Tests whether the simple constructor of the ListSegment class accepts
/// an empty list
/// </summary>
[Test]
public void SimpleConstructorAcceptsEmptyList() {
new ListSegment<int>(new List<int>());
}
/// <summary>
/// Tests whether the full constructor of the ListSegment class throws the
/// right exception when being passed 'null' instead of a string
/// </summary>
[Test]
public void ConstructorThrowsWhenListIsNull() {
Assert.Throws<ArgumentNullException>(
delegate() { new ListSegment<int>(null, 0, 0); }
);
}
/// <summary>
/// Tests whether the full constructor of the ListSegment class accepts
/// an empty string
/// </summary>
[Test]
public void ConstructorAcceptsEmptyList() {
new ListSegment<int>(new List<int>(), 0, 0);
}
/// <summary>
/// Tests whether the full constructor of the ListSegment class throws the
/// right exception when being passed an invalid start offset
/// </summary>
[Test]
public void ConstructorThrowsOnInvalidOffset() {
Assert.Throws<ArgumentOutOfRangeException>(
delegate() { new ListSegment<int>(new List<int>(), -1, 0); }
);
}
/// <summary>
/// Tests whether the full constructor of the ListSegment class throws the
/// right exception when being passed an invalid element count
/// </summary>
[Test]
public void ConstructorThrowsOnInvalidCount() {
Assert.Throws<ArgumentOutOfRangeException>(
delegate() { new ListSegment<int>(new List<int>(), 0, -1); }
);
}
/// <summary>
/// Tests whether the full constructor of the ListSegment class throws the
/// right exception when being passed a string length that's too large
/// </summary>
[Test]
public void ConstructorThrowsOnListOverrun() {
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
Assert.Throws<ArgumentException>(
delegate() { new ListSegment<int>(testList, 3, 3); }
);
}
/// <summary>Tests whether the 'Text' property works as expected</summary>
[Test]
public void ListPropertyStoresOriginalList() {
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
Assert.AreSame(testList, testSegment.List);
}
/// <summary>Tests whether the 'Offset' property works as expected</summary>
[Test]
public void OffsetPropertyIsStored() {
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
Assert.AreEqual(1, testSegment.Offset);
}
/// <summary>Tests whether the 'Count' property works as expected</summary>
[Test]
public void CountPropertyIsStored() {
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
Assert.AreEqual(3, testSegment.Count);
}
/// <summary>
/// Tests whether two differing instances produce different hash codes
/// </summary>
[Test]
public void DifferentInstancesHaveDifferentHashCodes_Usually() {
var forwardCountSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
var reverseCountSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 9, 8, 7, 6, 5, 4, 3, 2, 1 }, 1, 8
);
Assert.AreNotEqual(
forwardCountSegment.GetHashCode(), reverseCountSegment.GetHashCode()
);
}
/// <summary>
/// Tests whether two equivalent instances produce an identical hash code
/// </summary>
[Test]
public void EquivalentInstancesHaveSameHashcode() {
var testSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
var identicalSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
Assert.AreEqual(
testSegment.GetHashCode(), identicalSegment.GetHashCode()
);
}
/// <summary>Tests the equals method performing a comparison against null</summary>
[Test]
public void EqualsAgainstNullIsAlwaysFalse() {
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
Assert.IsFalse(
testSegment.Equals(null)
);
}
/// <summary>Tests the equality operator with differing instances</summary>
[Test]
public void DifferingInstancesAreNotEqual() {
var forwardCountSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
var reverseCountSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 9, 8, 7, 6, 5, 4, 3, 2, 1 }, 1, 8
);
Assert.IsFalse(forwardCountSegment == reverseCountSegment);
}
/// <summary>Tests the equality operator with equivalent instances</summary>
[Test]
public void EquivalentInstancesAreEqual() {
var testSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
var identicalSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
Assert.IsTrue(testSegment == identicalSegment);
}
/// <summary>Tests the inequality operator with differing instances</summary>
[Test]
public void DifferingInstancesAreUnequal() {
var forwardCountSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
var reverseCountSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 9, 8, 7, 6, 5, 4, 3, 2, 1 }, 1, 8
);
Assert.IsTrue(forwardCountSegment != reverseCountSegment);
}
/// <summary>Tests the inequality operator with equivalent instances</summary>
[Test]
public void EquivalentInstancesAreNotUnequal() {
var testSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
var identicalSegment = new ListSegment<int>(
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
);
Assert.IsFalse(testSegment != identicalSegment);
}
/// <summary>Tests the ToString() method of the string segment</summary>
[Test]
public void TestToString() {
var testList = new List<int>(capacity: 6) { 1, 2, 3, 4, 5, 6 };
ListSegment<int> testSegment = new ListSegment<int>(testList, 2, 2);
string stringRepresentation = testSegment.ToString();
StringAssert.Contains("3, 4", stringRepresentation);
StringAssert.DoesNotContain("2", stringRepresentation);
StringAssert.DoesNotContain("5", stringRepresentation);
}
/// <summary>Tests whether the 'Text' property works as expected</summary>
[Test]
public void ToListReturnsSubset() {
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
CollectionAssert.AreEqual(
new List<int>(capacity: 3) { 2, 3, 4 },
testSegment.ToList()
);
}
}
} // namespace Nuclex.Support.Collections
#endif // UNITTEST

View File

@ -0,0 +1,281 @@
#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2017 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Nuclex.Support.Collections {
/// <summary>View into a section of an IList&lt;T&gt; without copying said string</summary>
/// <typeparam name="TElement">
/// Type of elements that are stored in the list the segment references
/// </typeparam>
/// <remarks>
/// <para>
/// The design of this class pretty much mirrors that of the
/// <see cref="T:System.ArraySegment" /> class found in the .NET framework, but is
/// specialized to be used for IList&lt;T&gt;, which can not be cast to arrays
/// directly (and <see cref="M:System.ArrayList.Adapter" /> loses type safety).
/// </para>
/// <para>
/// In certain situations, passing a ListSegment instead of storing the selected
/// elements in a new list is useful. For example, the caller might want to know
/// from which index of the original list the section was taken. When the original
/// list needs to be modified, for example in a sorting algorithm, the list segment
/// can be used to specify a region for the algorithm to work on while still accessing
/// the original list.
/// </para>
/// </remarks>
#if !NO_SERIALIZATION
[Serializable, StructLayout(LayoutKind.Sequential)]
#endif
public struct ListSegment<TElement> {
/// <summary>
/// Initializes a new instance of the <see cref="ListSegment&lt;TElement&gt;" /> class
/// that delimits all the elements in the specified string
/// </summary>
/// <param name="list">List that will be wrapped</param>
/// <exception cref="System.ArgumentNullException">String is null</exception>
public ListSegment(IList<TElement> list) {
if(list == null) { // questionable, but matches behavior of ArraySegment class
throw new ArgumentNullException("text", "Text must not be null");
}
this.list = list;
this.offset = 0;
this.count = list.Count;
}
/// <summary>
/// Initializes a new instance of the <see cref="ListSegment&lt;TElement&gt;" /> class
/// that delimits the specified range of the elements in the specified string
/// </summary>
/// <param name="list">The list containing the range of elements to delimit</param>
/// <param name="offset">The zero-based index of the first element in the range</param>
/// <param name="count">The number of elements in the range</param>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Offset or count is less than 0
/// </exception>
/// <exception cref="System.ArgumentException">
/// Offset and count do not specify a valid range in array
/// </exception>
/// <exception cref="System.ArgumentNullException">String is null</exception>
public ListSegment(IList<TElement> list, int offset, int count) {
if(list == null) { // questionable, but matches behavior of ArraySegment class
throw new ArgumentNullException("list");
}
if(offset < 0) {
throw new ArgumentOutOfRangeException(
"offset", "Argument out of range, non-negative number required"
);
}
if(count < 0) {
throw new ArgumentOutOfRangeException(
"count", "Argument out of range, non-negative number required"
);
}
if(count > (list.Count - offset)) {
throw new ArgumentException(
"Invalid argument, specified offset and count exceed list size"
);
}
this.list = list;
this.offset = offset;
this.count = count;
}
/// <summary>
/// Gets the original list containing the range of elements that the list
/// segment delimits
/// </summary>
/// <returns>
/// The original list that was passed to the constructor, and that contains the range
/// delimited by the <see cref="ListSegment&lt;TElement&gt;" />
/// </returns>
public IList<TElement> List {
get { return this.list; }
}
/// <summary>
/// Gets the position of the first element in the range delimited by the list segment,
/// relative to the start of the original list
/// </summary>
/// <returns>
/// The position of the first element in the range delimited by the
/// <see cref="ListSegment&lt;TElement&gt;" />, relative to the start of the original list
/// </returns>
public int Offset {
get { return this.offset; }
}
/// <summary>
/// Gets the number of elements in the range delimited by the list segment
/// </summary>
/// <returns>
/// The number of elements in the range delimited by
/// the <see cref="ListSegment&lt;TElement&gt;" />
/// </returns>
public int Count {
get { return this.count; }
}
/// <summary>Returns the hash code for the current instance</summary>
/// <returns>A 32-bit signed integer hash code</returns>
public override int GetHashCode() {
int hashCode = this.offset ^ this.count;
for(int index = 0; index < this.count; ++index) {
hashCode ^= this.list[index + this.offset].GetHashCode();
}
return hashCode;
}
/// <summary>
/// Determines whether the specified object is equal to the current instance
/// </summary>
/// <returns>
/// True if the specified object is a <see cref="ListSegment&lt;TElement&gt;" /> structure
/// and is equal to the current instance; otherwise, false
/// </returns>
/// <param name="other">The object to be compared with the current instance</param>
public override bool Equals(object other) {
return
(other is ListSegment<TElement>) &&
this.Equals((ListSegment<TElement>)other);
}
/// <summary>
/// Determines whether the specified <see cref="ListSegment&lt;TElement&gt;" />
/// structure is equal to the current instance
/// </summary>
/// <returns>
/// True if the specified <see cref="ListSegment&lt;TElement&gt;" /> structure is equal
/// to the current instance; otherwise, false
/// </returns>
/// <param name="other">
/// The <see cref="ListSegment&lt;TElement&gt;" /> structure to be compared with
/// the current instance
/// </param>
public bool Equals(ListSegment<TElement> other) {
if(other.count != this.count) {
return false;
}
if(ReferenceEquals(other.list, this.list)) {
return (other.offset == this.offset);
} else {
var comparer = Comparer<TElement>.Default;
for(int index = 0; index < this.count; ++index) {
int difference = comparer.Compare(
other.list[index + other.offset], this.list[index + this.offset]
);
if(difference != 0) {
return false;
}
}
}
return true;
}
/// <summary>
/// Indicates whether two <see cref="ListSegment&lt;TElement&gt;" /> structures are equal
/// </summary>
/// <returns>True if a is equal to b; otherwise, false</returns>
/// <param name="left">
/// The <see cref="ListSegment&lt;TElement&gt;" /> structure on the left side of
/// the equality operator
/// </param>
/// <param name="right">
/// The <see cref="ListSegment&lt;TElement&gt;" /> structure on the right side of
/// the equality operator
/// </param>
public static bool operator ==(ListSegment<TElement> left, ListSegment<TElement> right) {
return left.Equals(right);
}
/// <summary>
/// Indicates whether two <see cref="ListSegment&lt;TElement&gt;" /> structures are unequal
/// </summary>
/// <returns>True if a is not equal to b; otherwise, false</returns>
/// <param name="left">
/// The <see cref="ListSegment&lt;TElement&gt;" /> structure on the left side of
/// the inequality operator
/// </param>
/// <param name="right">
/// The <see cref="ListSegment&lt;TElement&gt;" /> structure on the right side of
/// the inequality operator
/// </param>
public static bool operator !=(ListSegment<TElement> left, ListSegment<TElement> right) {
return !(left == right);
}
/// <summary>Returns a string representation of the list segment</summary>
/// <returns>The string representation of the list segment</returns>
public override string ToString() {
var builder = new System.Text.StringBuilder();
builder.Append("ListSegment {");
for(int index = 0; index < Math.Min(this.count, 10); ++index) {
if(index == 0) {
builder.Append(" ");
} else {
builder.Append(", ");
}
builder.Append(this.list[index + this.offset].ToString());
}
if(this.count >= 11) {
builder.Append(", ... }");
} else {
builder.Append(" }");
}
return builder.ToString();
}
/// <summary>Returns a new list containing only the elements in the list segment</summary>
/// <returns>A new list containing only the elements in the list segment</returns>
public List<TElement> ToList() {
if(this.count == 0) {
return new List<TElement>(capacity: 0);
} else {
var newList = new List<TElement>(capacity: this.count);
{
int endIndex = this.offset + this.count;
for(int index = this.offset; index < endIndex; ++index) {
newList.Add(this.list[index]);
}
}
return newList;
}
}
/// <summary>List wrapped by the list segment</summary>
private IList<TElement> list;
/// <summary>Offset in the original list the segment begins at</summary>
private int offset;
/// <summary>Number of elements in the segment</summary>
private int count;
}
} // namespace Nuclex.Support.Collections

View File

@ -27,7 +27,7 @@ using NUnit.Framework;
namespace Nuclex.Support { namespace Nuclex.Support {
/// <summary>Unit Test for the strign segment class</summary> /// <summary>Unit Test for the string segment class</summary>
[TestFixture] [TestFixture]
internal class StringSegmentTest { internal class StringSegmentTest {
@ -36,7 +36,7 @@ namespace Nuclex.Support {
/// right exception when being passed 'null' instead of a string /// right exception when being passed 'null' instead of a string
/// </summary> /// </summary>
[Test] [Test]
public void TestNullStringInSimpleConstructor() { public void SimpleConstructorThrowsWhenStringIsNull() {
Assert.Throws<ArgumentNullException>( Assert.Throws<ArgumentNullException>(
delegate() { new StringSegment(null); } delegate() { new StringSegment(null); }
); );
@ -47,7 +47,7 @@ namespace Nuclex.Support {
/// an empty string /// an empty string
/// </summary> /// </summary>
[Test] [Test]
public void TestEmptyStringInSimpleConstructor() { public void SimpleConstructorAcceptsEmptyString() {
new StringSegment(string.Empty); new StringSegment(string.Empty);
} }
@ -56,7 +56,7 @@ namespace Nuclex.Support {
/// right exception when being passed 'null' instead of a string /// right exception when being passed 'null' instead of a string
/// </summary> /// </summary>
[Test] [Test]
public void TestNullStringInFullConstructor() { public void ConstructorThrowsWhenStringIsNull() {
Assert.Throws<ArgumentNullException>( Assert.Throws<ArgumentNullException>(
delegate() { new StringSegment(null, 0, 0); } delegate() { new StringSegment(null, 0, 0); }
); );
@ -67,7 +67,7 @@ namespace Nuclex.Support {
/// an empty string /// an empty string
/// </summary> /// </summary>
[Test] [Test]
public void TestEmptyStringInFullConstructor() { public void ConstructorAcceptsEmptyString() {
new StringSegment(string.Empty, 0, 0); new StringSegment(string.Empty, 0, 0);
} }
@ -76,7 +76,7 @@ namespace Nuclex.Support {
/// right exception when being passed an invalid start offset /// right exception when being passed an invalid start offset
/// </summary> /// </summary>
[Test] [Test]
public void TestInvalidOffsetInConstructor() { public void ConstructorThrowsOnInvalidOffset() {
Assert.Throws<ArgumentOutOfRangeException>( Assert.Throws<ArgumentOutOfRangeException>(
delegate() { new StringSegment(string.Empty, -1, 0); } delegate() { new StringSegment(string.Empty, -1, 0); }
); );
@ -87,7 +87,7 @@ namespace Nuclex.Support {
/// right exception when being passed an invalid string length /// right exception when being passed an invalid string length
/// </summary> /// </summary>
[Test] [Test]
public void TestInvalidLengthInConstructor() { public void ConstructorThrowsOnInvalidLength() {
Assert.Throws<ArgumentOutOfRangeException>( Assert.Throws<ArgumentOutOfRangeException>(
delegate() { new StringSegment(string.Empty, 0, -1); } delegate() { new StringSegment(string.Empty, 0, -1); }
); );
@ -98,7 +98,7 @@ namespace Nuclex.Support {
/// right exception when being passed a string length that's too large /// right exception when being passed a string length that's too large
/// </summary> /// </summary>
[Test] [Test]
public void TestExcessiveLengthInConstructor() { public void ConstructorThrowsOnLengthOverrun() {
Assert.Throws<ArgumentException>( Assert.Throws<ArgumentException>(
delegate() { new StringSegment("hello", 3, 3); } delegate() { new StringSegment("hello", 3, 3); }
); );
@ -106,21 +106,21 @@ namespace Nuclex.Support {
/// <summary>Tests whether the 'Text' property works as expected</summary> /// <summary>Tests whether the 'Text' property works as expected</summary>
[Test] [Test]
public void TestTextProperty() { public void TextPropertyStoresOriginalString() {
StringSegment testSegment = new StringSegment("hello", 1, 3); StringSegment testSegment = new StringSegment("hello", 1, 3);
Assert.AreEqual("hello", testSegment.Text); Assert.AreEqual("hello", testSegment.Text);
} }
/// <summary>Tests whether the 'Offset' property works as expected</summary> /// <summary>Tests whether the 'Offset' property works as expected</summary>
[Test] [Test]
public void TestOffsetProperty() { public void OffsetPropertyIsStored() {
StringSegment testSegment = new StringSegment("hello", 1, 3); StringSegment testSegment = new StringSegment("hello", 1, 3);
Assert.AreEqual(1, testSegment.Offset); Assert.AreEqual(1, testSegment.Offset);
} }
/// <summary>Tests whether the 'Count' property works as expected</summary> /// <summary>Tests whether the 'Count' property works as expected</summary>
[Test] [Test]
public void TestCountProperty() { public void CountPropertyIsStored() {
StringSegment testSegment = new StringSegment("hello", 1, 3); StringSegment testSegment = new StringSegment("hello", 1, 3);
Assert.AreEqual(3, testSegment.Count); Assert.AreEqual(3, testSegment.Count);
} }
@ -129,7 +129,7 @@ namespace Nuclex.Support {
/// Tests whether two differing instances produce different hash codes /// Tests whether two differing instances produce different hash codes
/// </summary> /// </summary>
[Test] [Test]
public void TestHashCodeOnDifferingInstances() { public void DifferentInstancesHaveDifferentHashCodes_Usually() {
StringSegment helloWorldSegment = new StringSegment("hello world", 2, 7); StringSegment helloWorldSegment = new StringSegment("hello world", 2, 7);
StringSegment howAreYouSegment = new StringSegment("how are you", 1, 9); StringSegment howAreYouSegment = new StringSegment("how are you", 1, 9);
@ -142,7 +142,7 @@ namespace Nuclex.Support {
/// Tests whether two equivalent instances produce an identical hash code /// Tests whether two equivalent instances produce an identical hash code
/// </summary> /// </summary>
[Test] [Test]
public void TestHashCodeOnEquivalentInstances() { public void EquivalentInstancesHaveSameHashcode() {
StringSegment helloWorld1Segment = new StringSegment("hello world", 2, 7); StringSegment helloWorld1Segment = new StringSegment("hello world", 2, 7);
StringSegment helloWorld2Segment = new StringSegment("hello world", 2, 7); StringSegment helloWorld2Segment = new StringSegment("hello world", 2, 7);
@ -153,7 +153,7 @@ namespace Nuclex.Support {
/// <summary>Tests the equals method performing a comparison against null</summary> /// <summary>Tests the equals method performing a comparison against null</summary>
[Test] [Test]
public void TestEqualsOnNull() { public void EqualsAgainstNullIsAlwaysFalse() {
StringSegment helloWorldSegment = new StringSegment("hello world", 2, 7); StringSegment helloWorldSegment = new StringSegment("hello world", 2, 7);
Assert.IsFalse( Assert.IsFalse(
@ -163,7 +163,7 @@ namespace Nuclex.Support {
/// <summary>Tests the equality operator with differing instances</summary> /// <summary>Tests the equality operator with differing instances</summary>
[Test] [Test]
public void TestEqualityOnDifferingInstances() { public void DifferingInstancesAreNotEqual() {
StringSegment helloWorldSegment = new StringSegment("hello world", 2, 7); StringSegment helloWorldSegment = new StringSegment("hello world", 2, 7);
StringSegment howAreYouSegment = new StringSegment("how are you", 1, 9); StringSegment howAreYouSegment = new StringSegment("how are you", 1, 9);
@ -172,7 +172,7 @@ namespace Nuclex.Support {
/// <summary>Tests the equality operator with equivalent instances</summary> /// <summary>Tests the equality operator with equivalent instances</summary>
[Test] [Test]
public void TestEqualityOnEquivalentInstances() { public void EquivalentInstancesAreEqual() {
StringSegment helloWorld1Segment = new StringSegment("hello world", 2, 7); StringSegment helloWorld1Segment = new StringSegment("hello world", 2, 7);
StringSegment helloWorld2Segment = new StringSegment("hello world", 2, 7); StringSegment helloWorld2Segment = new StringSegment("hello world", 2, 7);
@ -181,7 +181,7 @@ namespace Nuclex.Support {
/// <summary>Tests the inequality operator with differing instances</summary> /// <summary>Tests the inequality operator with differing instances</summary>
[Test] [Test]
public void TestInequalityOnDifferingInstances() { public void DifferingInstancesAreUnequal() {
StringSegment helloWorldSegment = new StringSegment("hello world", 2, 7); StringSegment helloWorldSegment = new StringSegment("hello world", 2, 7);
StringSegment howAreYouSegment = new StringSegment("how are you", 1, 9); StringSegment howAreYouSegment = new StringSegment("how are you", 1, 9);
@ -190,7 +190,7 @@ namespace Nuclex.Support {
/// <summary>Tests the inequality operator with equivalent instances</summary> /// <summary>Tests the inequality operator with equivalent instances</summary>
[Test] [Test]
public void TestInequalityOnEquivalentInstances() { public void EquivalentInstancesAreNotUnequal() {
StringSegment helloWorld1Segment = new StringSegment("hello world", 2, 7); StringSegment helloWorld1Segment = new StringSegment("hello world", 2, 7);
StringSegment helloWorld2Segment = new StringSegment("hello world", 2, 7); StringSegment helloWorld2Segment = new StringSegment("hello world", 2, 7);
@ -205,36 +205,6 @@ namespace Nuclex.Support {
Assert.AreEqual("o w", helloWorldSegment.ToString()); Assert.AreEqual("o w", helloWorldSegment.ToString());
} }
/// <summary>
/// Tests the ToString() method of the string segment with an invalid string
/// </summary>
[Test]
public void TestToStringWithInvalidString() {
Assert.Throws<ArgumentNullException>(
delegate() { new StringSegment(null, 4, 3); }
);
}
/// <summary>
/// Tests the ToString() method of the string segment with an invalid offset
/// </summary>
[Test]
public void TestToStringWithInvalidOffset() {
Assert.Throws<ArgumentOutOfRangeException>(
delegate() { new StringSegment("hello world", -4, 3); }
);
}
/// <summary>
/// Tests the ToString() method of the string segment with an invalid count
/// </summary>
[Test]
public void TestToStringWithInvalidCount() {
Assert.Throws<ArgumentOutOfRangeException>(
delegate() { new StringSegment("hello world", 4, -3); }
);
}
} }
} // namespace Nuclex.Support } // namespace Nuclex.Support

View File

@ -23,7 +23,7 @@ using System.Runtime.InteropServices;
namespace Nuclex.Support { namespace Nuclex.Support {
/// <summary>Delimits a section of a string</summary> /// <summary>View into a section of a string without copying said string</summary>
/// <remarks> /// <remarks>
/// <para> /// <para>
/// The design of this class pretty much mirrors that of the /// The design of this class pretty much mirrors that of the
@ -32,10 +32,10 @@ namespace Nuclex.Support {
/// share a lot of the characteristics of an array. /// share a lot of the characteristics of an array.
/// </para> /// </para>
/// <para> /// <para>
/// In certain situations, passing a StringSegment instead of the the actual /// In certain situations, passing a StringSegment instead of the actual copied
/// section from a string is useful. For example, the caller might want to know /// substring is useful. For example, the caller might want to know from which
/// from which index of the original string the substring was taken. Used internally /// index of the original string the substring was taken. Used internally in parsers,
/// in parsers, it can also prevent needless string copying and garbage generation. /// it can also prevent needless string copying and garbage generation.
/// </para> /// </para>
/// </remarks> /// </remarks>
#if !NO_SERIALIZATION #if !NO_SERIALIZATION
@ -103,7 +103,7 @@ namespace Nuclex.Support {
/// segment delimits /// segment delimits
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The original array that was passed to the constructor, and that contains the range /// The original string that was passed to the constructor, and that contains the range
/// delimited by the <see cref="StringSegment" /> /// delimited by the <see cref="StringSegment" />
/// </returns> /// </returns>
public string Text { public string Text {
@ -111,19 +111,19 @@ namespace Nuclex.Support {
} }
/// <summary> /// <summary>
/// Gets the position of the first element in the range delimited by the array segment, /// Gets the position of the first element in the range delimited by the string segment,
/// relative to the start of the original array /// relative to the start of the original string
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The position of the first element in the range delimited by the /// The position of the first element in the range delimited by the
/// <see cref="StringSegment" />, relative to the start of the original array /// <see cref="StringSegment" />, relative to the start of the original string
/// </returns> /// </returns>
public int Offset { public int Offset {
get { return this.offset; } get { return this.offset; }
} }
/// <summary> /// <summary>
/// Gets the number of elements in the range delimited by the array segment /// Gets the number of elements in the range delimited by the string segment
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The number of elements in the range delimited by the <see cref="StringSegment" /> /// The number of elements in the range delimited by the <see cref="StringSegment" />