【问题标题】:How to clone part of a List?如何克隆列表的一部分?
【发布时间】:2014-12-17 04:44:18
【问题描述】:

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

例子:

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

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

这可能吗?第二个List&lt;string&gt; 中的更改应反映在第一个中,反之亦然。

更新

感谢到目前为止的回答。看来我没有正确表达自己。

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

希望这更清楚。

【问题讨论】:

  • 应该反映变化吗?在这种情况下,听起来您实际上并没有尝试 clone 任何东西,您只是想用另一个对象包装一个对象(包装器可能会做一些重新索引的事情)。
  • 听起来 ref 关键字是出路
  • 更多关于反射变化的细节会很有帮助。如果我在索引 2 处插入一个新元素,这对索引 1-2 的子列表有何影响?
  • 在我看来,软件设计需要更多考虑。
  • 请允许我澄清 O. R. Mapper 的声明:术语“克隆”意味着更改反映。如果我克隆自己,那么我的克隆会换衣服,我的衣服保持不变。 :-)

标签: c# .net list clone


【解决方案1】:

您可以创建一个ListSlice&lt;T&gt; 类来表示现有列表的一部分。切片将表现为只读列表,并且因为它保留对原始列表的引用,所以您不应该在原始列表中添加或删除元素。除非您实现自己的列表,否则无法强制执行此操作,但我不会在这里执行。

您必须实现整个IList&lt;T&gt; 接口,包括枚举切片所需的IEnumerator&lt;T&gt;。这是一个例子:

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"

【讨论】:

  • 非常感谢,您的代码肯定会朝着我需要的方向发展。 M.
【解决方案2】:

乍一看,好像

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)和其中的位置(即它的索引)。

【讨论】:

    【解决方案3】:

    假设您确实是指克隆而不是复制...

        List<string> myoriginalstring = new List<string> { "Tyrannosaurus", "Amargasaurus", "Mamenchisaurus" };
        List<string> myCloneString = myoriginalstring.GetRange(1, myoriginalstring.Count() -1 );
    

    【讨论】:

    • GetRange 方法根据定义返回一个副本。即使除此之外,也不确定“您确实是指克隆而不是复制”在这里是什么意思。
    • 对不起,“...而不是对原作的引用”。
    • 感谢您到目前为止的回答。看来我没有正确表达自己。实际上我不想复制或克隆,我需要创建新列表(这将是原始列表的一部分),当我在新列表中更改某些内容(某些值)时,原始列表也应该以相同的方式进行更改。 (列表应该始终相同,只是新列表将是原始列表的一部分)。我希望它现在更清楚。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-06
    • 1970-01-01
    • 1970-01-01
    • 2022-01-20
    • 2010-12-09
    相关资源
    最近更新 更多