如何克隆List的一部分?

时间:2014-10-21 20:16:34

标签: c# .net list clone

是否可以只克隆List<T>的一部分?

示例:

List<string> myoriginalstring = new List<string>();
myoriginalstring.Add("Tyrannosaurus");
myoriginalstring.Add("Amargasaurus");
myoriginalstring.Add("Mamenchisaurus");

我想将myoriginalstring克隆到另一个列表,但只是从索引1到索引2

这可能吗?第二个List<string>的变化应该首先反映出来,反之亦然。

更新

感谢目前为止的答案。我似乎没有正确表达自己。

其实我不想复制或克隆。我需要创建一个新列表(这将是原始列表的一部分);当我在新列表中更改某些内容(某些值)时,原始内容也应以相同的方式更改。 (列表应始终相同,只是新列表将是原始列表的一部分。)

希望这更清楚。

3 个答案:

答案 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)及其中的位置(即其索引)。