是否可以只克隆List<T>
的一部分?
示例:
List<string> myoriginalstring = new List<string>();
myoriginalstring.Add("Tyrannosaurus");
myoriginalstring.Add("Amargasaurus");
myoriginalstring.Add("Mamenchisaurus");
我想将myoriginalstring
克隆到另一个列表,但只是从索引1
到索引2
。
这可能吗?第二个List<string>
的变化应该首先反映出来,反之亦然。
感谢目前为止的答案。我似乎没有正确表达自己。
其实我不想复制或克隆。我需要创建一个新列表(这将是原始列表的一部分);当我在新列表中更改某些内容(某些值)时,原始内容也应以相同的方式更改。 (列表应始终相同,只是新列表将是原始列表的一部分。)
希望这更清楚。
答案 0 :(得分:4)
您可以创建一个代表现有列表片段的ListSlice<T>
类。切片将表现为只读列表,因为它保留对原始列表的引用,因此您不应在原始列表中添加或删除元素。除非您实现自己的列表,否则无法执行此操作,但我不会在此处执行此操作。
您必须实现整个IList<T>
界面,包括枚举切片所需的IEnumerator<T>
。这是一个例子:
class ListSlice<T> : IList<T> {
readonly IList<T> list;
readonly Int32 startIndex;
readonly Int32 length;
public ListSlice(IList<T> list, Int32 startIndex, Int32 length) {
if (list == null)
throw new ArgumentNullException("list");
if (!(0 <= startIndex && startIndex < list.Count))
throw new ArgumentException("startIndex");
if (!(0 <= length && length <= list.Count - startIndex))
throw new ArgumentException("length");
this.list = list;
this.startIndex = startIndex;
this.length = length;
}
public T this[Int32 index] {
get {
if (!(0 <= index && index < this.length))
throw new ArgumentOutOfRangeException();
return this.list[this.startIndex + index];
}
set {
if (!(0 <= index && index < this.length))
throw new ArgumentOutOfRangeException();
this.list[this.startIndex + index] = value;
}
}
public Int32 IndexOf(T item) {
var index = this.list.IndexOf(item);
return index == -1 || index >= this.startIndex + this.length
? -1 : index - this.startIndex;
}
public void Insert(Int32 index, T item) { throw new NotSupportedException(); }
public void RemoveAt(Int32 index) { throw new NotSupportedException(); }
public Int32 Count { get { return this.length; } }
public Boolean IsReadOnly { get { return true; } }
public void Add(T item) { throw new NotSupportedException(); }
public void Clear() { throw new NotSupportedException(); }
public Boolean Contains(T item) { return IndexOf(item) != -1; }
public void CopyTo(T[] array, Int32 arrayIndex) {
for (var i = this.startIndex; i < this.length; i += 1)
array[i + arrayIndex] = this.list[i];
}
public Boolean Remove(T item) { throw new NotSupportedException(); }
public IEnumerator<T> GetEnumerator() {
return new Enumerator(this.list, this.startIndex, this.length);
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
class Enumerator : IEnumerator<T> {
readonly IList<T> list;
readonly Int32 startIndex;
readonly Int32 length;
Int32 index;
T current;
public Enumerator(IList<T> list, Int32 startIndex, Int32 length) {
this.list = list;
this.startIndex = startIndex;
this.length = length;
}
public T Current { get { return this.current; } }
Object IEnumerator.Current {
get {
if (this.index == 0 || this.index == this.length + 1)
throw new InvalidOperationException();
return Current;
}
}
public Boolean MoveNext() {
if (this.index < this.length) {
this.current = this.list[this.index + this.startIndex];
this.index += 1;
return true;
}
this.current = default(T);
return false;
}
public void Reset() {
this.index = 0;
this.current = default(T);
}
public void Dispose() {
}
}
}
您可以编写扩展方法,以便更轻松地处理切片:
static class ListExtensions {
public static ListSlice<T> Slice<T>(this IList<T> list, Int32 startIndex, Int32 length) {
return new ListSlice<T>(list, startIndex, length);
}
}
要使用切片,您可以编写如下代码:
var list = new List<String> {
"Tyrannosaurus",
"Amargasaurus",
"Mamenchisaurus"
};
var slice = list.Slice(1, 2);
slice[0] = "Stegosaurus";
现在list[1]
以及slice[0]
包含"Stegosaurus"
。
答案 1 :(得分:0)
假设你真的是克隆而不是复制......
List<string> myoriginalstring = new List<string> { "Tyrannosaurus", "Amargasaurus", "Mamenchisaurus" };
List<string> myCloneString = myoriginalstring.GetRange(1, myoriginalstring.Count() -1 );
答案 2 :(得分:0)
乍一看,似乎
var list2 = myoriginalstring.SkipWhile((str, i)=>!(i>=1 && i<=2)).ToList();
list2[1]="Stegosaurus";
将是解决方案。但事实并非如此,因为list2
是一个包含自己元素的独立列表。上面的代码有效,但它只替换(新)list2
中的元素,而不是myoriginalstring
中的元素。
正如您所指出的,这不是您想要做的。
<强>解决方案强>
与C之类的其他语言不同,您无法直接在C#中访问指针。 因此,解决方案更复杂。您需要创建一个对象,而不是直接使用字符串,如下所示:
public class Dino
{
public string Value { get; set; }
public object[] Parent { get; set; }
public int ParentIndex;
}
然后,创建一些辅助扩展方法,如下所示:
public static class Extensions
{
public static Dino AsDino(this string name)
{
return new Dino() {Value=name};
}
public static Dino AsDino(this string name, object[] reference, int parentIndex)
{
return new Dino() {Value=name, Parent=reference, ParentIndex=parentIndex };
}
public static Dino Replace(this object item, Dino replacementItem)
{
replacementItem.ParentIndex=((Dino)item).ParentIndex;
replacementItem.Parent=((Dino)item).Parent;
((Dino)item).Parent[replacementItem.ParentIndex]=replacementItem;
return replacementItem;
}
}
有了这些助手,你可以这样做:
// create array with 3 elements
var myoriginalstring = new object[3];
// fill in the dinosours and keep track of the array object and positions within
myoriginalstring[0]="Tyrannosaurus".AsDino(myoriginalstring, 0);
myoriginalstring[1]="Amargasaurus".AsDino(myoriginalstring, 1);
myoriginalstring[2]="Mamenchisaurus".AsDino(myoriginalstring, 2);
// get a subset of the array
var list2 = myoriginalstring.SkipWhile((str, i)=>!(i>=1 && i<=2)).ToList<object>();
// replace the value at index 1 in list2. This will also replace the value
// in the original array myoriginalstring
list2[1].Replace("Stegosaurus".AsDino());
诀窍是,Dino
类跟踪其原点(数组myoriginalstring
)及其中的位置(即其索引)。