ได้รับการย่อยจากอาร์เรย์ที่มีอยู่


335

ฉันมีอาร์เรย์ X 10 องค์ประกอบ ฉันต้องการสร้างอาร์เรย์ใหม่ที่มีองค์ประกอบทั้งหมดจาก X ที่เริ่มต้นที่ดัชนี 3 และสิ้นสุดในดัชนี 7 แน่นอนฉันสามารถเขียนลูปที่จะทำเพื่อฉันได้ง่าย แต่ฉันต้องการให้โค้ดของฉันสะอาดที่สุดเท่าที่จะทำได้ . มีวิธีการใน C # ที่สามารถทำได้สำหรับฉัน

บางสิ่งที่ชอบ (รหัสหลอก):

Array NewArray = oldArray.createNewArrayFromRange(int BeginIndex , int EndIndex)

Array.Copyไม่พอดีกับความต้องการของฉัน ฉันต้องการไอเท็มในแถวลำดับใหม่เพื่อทำการโคลน Array.copyมันmemcpyเทียบเท่าC-Style ไม่ใช่สิ่งที่ฉันกำลังมองหา



7
@ Kirtan - "dup" นั้นต้องการ IEnumerable <T> โดยเฉพาะซึ่งแตกต่างกันและมีทางออกที่ดีที่สุด IMO
Marc Gravell

ดังนั้นสองบรรทัดที่จะใช้ในการประกาศอาร์เรย์ใหม่และโทร. คัดลอก () ไม่ได้เป็น "รหัสสะอาด"?
Ed S.

2
@Ed Swangren - ไม่ใช่ถ้าคุณจำเป็นต้องทำมันในช่วงกลางของการแสดงออกที่ถูกล่ามโซ่ไม่ใช่ ;-p
Marc Gravell

2
คำตอบของ ShaggyUk น่าจะเป็นคำตอบที่ถูกต้อง: stackoverflow.com/questions/943635/…
Dykam

คำตอบ:


469

คุณสามารถเพิ่มเป็นวิธีส่วนขยาย:

public static T[] SubArray<T>(this T[] data, int index, int length)
{
    T[] result = new T[length];
    Array.Copy(data, index, result, 0, length);
    return result;
}
static void Main()
{
    int[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int[] sub = data.SubArray(3, 4); // contains {3,4,5,6}
}

อัปเดตการโคลนใหม่ (ซึ่งไม่ชัดเจนในคำถามเดิม) หากคุณจริงๆต้องการโคลนลึก สิ่งที่ต้องการ:

public static T[] SubArrayDeepClone<T>(this T[] data, int index, int length)
{
    T[] arrCopy = new T[length];
    Array.Copy(data, index, arrCopy, 0, length);
    using (MemoryStream ms = new MemoryStream())
    {
        var bf = new BinaryFormatter();
        bf.Serialize(ms, arrCopy);
        ms.Position = 0;
        return (T[])bf.Deserialize(ms);
    }
}

สิ่งนี้ต้องการวัตถุที่จะเป็นอนุกรม ( [Serializable]หรือISerializable) แม้ว่า คุณสามารถจะทดแทนการ serializer อื่น ๆ ตามความเหมาะสม - XmlSerializer, DataContractSerializer, protobuf สุทธิ ฯลฯ

โปรดทราบว่าการโคลนแบบลึกนั้นมีความซับซ้อนโดยไม่ทำให้เป็นอันดับ โดยเฉพาะอย่างยิ่งICloneableยากที่จะเชื่อใจในกรณีส่วนใหญ่


1
(เห็นได้ชัดว่าใช้ดัชนีสิ้นมากกว่าความยาวคือการเปลี่ยนแปลงง่ายผมเคยโพสต์ "จะเป็น" เพราะเห็นว่าเป็นมากขึ้น "ปกติ" การใช้งาน)
Marc Gravell

1
ถ้างั้น ... มันไม่ได้ทำอย่างนั้น .... คุณอาจจะต้องใช้อนุกรมเพื่อให้บรรลุสิ่งที่คล้ายกัน
Marc Gravell

1
ดูคำตอบของฉันสำหรับทางเลือกและลิงก์ไปยังการใช้งานหลายอย่าง ส่วนที่เกี่ยวกับการทำมันไปยังอาร์เรย์ย่อยที่เป็นจริงค่อนข้างน่ารำคาญสิ่งที่คุณต้องการจริงๆเป็นโคลนบิตและนั่นเป็นคำถามที่ซับซ้อนและค่อนข้างเปิดซึ่งขึ้นอยู่กับคุณคาดหวังในสิ่งที่พฤติกรรมที่ถูกต้องควรจะเป็น
ShuggyCoUk

2
นี่เป็นสิ่งที่ดี และเป็นสิ่งที่ดีโดยเฉพาะอย่างยิ่งที่จะชี้ให้เห็นว่า ICloneable นั้นไม่น่าเชื่อถือเพราะมันเคย
Marcus Griep

1
ขอบคุณที่ขีดเส้นใต้ปัญหาการโคลนนิ่งใน C # มันเป็นความอัปยศจริงๆเช่นการคัดลอกลึกคือการดำเนินการขั้นพื้นฐาน
Dimitri C.

316

คุณสามารถใช้Array.Copy(...)เพื่อคัดลอกไปยังอาเรย์ใหม่หลังจากที่คุณสร้างมันขึ้นมา แต่ฉันไม่คิดว่าจะมีวิธีการที่จะสร้างอาเรย์ใหม่และคัดลอกองค์ประกอบต่าง ๆ

หากคุณใช้. NET 3.5 คุณสามารถใช้ LINQ:

var newArray = array.Skip(3).Take(5).ToArray();

แต่นั่นจะค่อนข้างมีประสิทธิภาพน้อยกว่า

ดูคำตอบสำหรับคำถามที่คล้ายกันนี้สำหรับตัวเลือกสำหรับสถานการณ์เฉพาะเพิ่มเติม


+1 ฉันชอบรูปแบบนี้เช่นกัน จอนคุณสามารถขยายว่าทำไมเรื่องนี้ถึงมีประสิทธิภาพน้อยลง
Ian Roke

@ จอน: เพื่อให้ตรงกับคำถามนั่นจะไม่ใช่ "ใช้ (5)" ใช่ไหม @Ian: วิธี Array.Copy ไม่เกี่ยวข้องกับตัวแจงนับและมักจะเป็น memcopy ตรง ...
Marc Gravell

@Marc: ใช่แน่นอน เกินไปคำถามมาก skimming :)
จอนสกีต

11
@Ian: วิธี LINQ แนะนำการอ้อมสองระดับ (ตัววนซ้ำ) ต้องข้ามรายการอย่างชัดเจนและไม่ทราบว่าอาร์เรย์สุดท้ายจะมีขนาดใหญ่เพียงใด ลองพิจารณาในช่วงครึ่งหลังของอาเรย์สองล้านองค์ประกอบ: วิธีการ "สร้างเป้าหมายแบบคัดลอก" แบบง่าย ๆ เพียงแค่จะคัดลอกบล็อกที่ต้องการโดยไม่ต้องสัมผัสองค์ประกอบอื่น ๆ และในครั้งเดียว วิธี LINQ จะดำเนินการผ่านอาร์เรย์จนกว่าจะถึงจุดเริ่มต้นจากนั้นเริ่มรับค่าการสร้างบัฟเฟอร์ (การเพิ่มขนาดบัฟเฟอร์และการคัดลอกเป็นระยะ) มีประสิทธิภาพน้อยกว่ามาก
Jon Skeet

ถ้า 5 คือ EndIndexm ดังนั้นคำถามที่ถูกต้องคือ array.Skip (3) .Take (5-3 + 1) .ToArray (); กล่าวคือ array.Skip (StartIndex) .Take (EndIndex-StartIndex + 1) .ToArray ();
Klaus78

73

คุณเคยคิดจะใช้ArraySegmentไหม?

http://msdn.microsoft.com/en-us/library/1hsbd92d.aspx


1
มันอาจทำในสิ่งที่คุณต้องการ แต่ไม่รองรับไวยากรณ์ของอาเรย์เริ่มต้นและไม่สนับสนุน IEnumerable ดังนั้นมันจึงไม่สะอาดโดยเฉพาะ
Alex Black

5
สิ่งนี้ต้องการการโหวตมากขึ้น ในประสบการณ์ของตัวเอง ArraySegment คัดลอกเร็วขึ้นเล็กน้อยเกินไป (หลังจากทั้งหมดอาร์เรย์จะใช้สำหรับวัตถุดิบที่สำคัญความเร็ว) ..
Nawfal

5
@AlexBlack ดูเหมือนว่า ณ. NET 4.5มันใช้IEnumerable<T>และอินเทอร์เฟซที่มีประโยชน์อื่น ๆ อีกมากมาย
pswg

1
คุณจะใช้ArraySegmentเพื่อตอบคำถามเดิมอย่างไร
Craig McQueen

2
@CraigMcQueen - ลองใช้วิธีการบรรทัดเดียวต่อไปนี้:IList<T> newArray = (IList<T>)new ArraySegment<T>(oldArray, beginIndex, endIndex);
skia.heliou

36

ฉันเห็นว่าคุณต้องการทำการโคลนไม่ใช่แค่คัดลอกข้อมูลอ้างอิง ในกรณีนี้คุณสามารถใช้.Selectโปรเจ็กต์สมาชิกอาเรย์กับโคลนนิ่งของพวกเขา ตัวอย่างเช่นหากองค์ประกอบของคุณมีการใช้งานIClonableคุณสามารถทำสิ่งนี้:

var newArray = array.Skip(3).Take(5).Select(eachElement => eachElement.Clone()).ToArray();

หมายเหตุ: โซลูชันนี้ต้องการ. NET Framework 3.5


นี่คือสง่างามมากขึ้น
smwikipedia

นี่คือสิ่งที่ฉันกำลังมองหา IEnumerableงานนี้สำหรับการใด ๆ ฉันจะได้รับIEnumerable, IList, IArrayฯลฯ ... มีความยุ่งยากน้อยที่สุดในบรรทัดถ้าฉันต้อง Selectถ้าฉันไม่จำเป็นต้องสำเนาลึกฉันเพียงแค่ลบ ปล่อยSkipหรือTakeอนุญาตให้ฉันควบคุมช่วง หรือฉันสามารถผสมมันขึ้นกับและSkipWhile / หรือ TakeWhile
Mike

33

รหัสต่อไปนี้ทำได้ในหนึ่งบรรทัด:

// Source array
string[] Source = new string[] { "A", "B", "C", "D" };
// Extracting a slice into another array
string[] Slice = new List<string>(Source).GetRange(2, 2).ToArray();

Singe line และไม่จำเป็นต้องเพิ่ม Linq เป็นวิธีที่ฉันชอบ
Dimitris

ถึงกระนั้นมันก็ไม่ได้ลอกเลียนแบบแหล่งที่มา ... แต่มันก็เป็นวิธีการที่ดีอยู่แล้ว
IG Pascual

1
มันควรโคลนแหล่งที่มาเพราะ ToArray: (1) สร้างอาร์เรย์ใหม่และ (2) ดำเนินการ Array.Copy ในที่สุดที่มาและชิ้นเป็นวัตถุที่แยกจากกัน อย่างไรก็ตามวิธีนี้ถูกต้อง แต่ฉันชอบ Array.Copy: Referencesource.microsoft.com/#mscorlib/system/collections/…
Krauss

13

ใน C # 8 พวกเขาได้แนะนำสิ่งใหม่RangeและIndexประเภท

int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Index i1 = 3;  // number 3 from beginning
Index i2 = ^4; // number 4 from end
var slice = a[i1..i2]; // { 3, 4, 5 }


8

สร้างคำตอบของ Marc แต่เพิ่มพฤติกรรมการโคลนที่ต้องการ

public static T[] CloneSubArray<T>(this T[] data, int index, int length)
    where T : ICloneable
{
    T[] result = new T[length];
    for (int i = 0; i < length; i++)
    { 
        var original = data[index + i];
        if (original != null)
            result[i] = (T)original.Clone();            
    return result;
}

และหากการติดตั้ง ICloneable เป็นงานที่หนักหน่วงเกินกว่าที่จะใช้งานไลบรารี่แบบไตร่ตรองโดยใช้ไลบรารี่ที่คัดลอกได้ของHåvard Strandenเพื่อใช้ในการยกของหนัก

using OX.Copyable;

public static T[] DeepCopySubArray<T>(
    this T[] data, int index, int length)
{
    T[] result = new T[length];
    for (int i = 0; i < length; i++)
    { 
        var original = data[index + i];
        if (original != null)
            result[i] = (T)original.Copy();            
    return result;
}

โปรดทราบว่าการใช้ OX.Copyable ทำงานได้กับ:

เพื่อให้การคัดลอกอัตโนมัติทำงานแม้ว่าข้อความใดข้อความหนึ่งต่อไปนี้จะต้องถูกระงับเช่น:

  • ประเภทของมันจะต้องมีคอนสตรัคเตอร์แบบไม่มีพารามิเตอร์หรือ
  • ต้องเป็นแบบคัดลอกได้หรือ
  • ต้องมีการลงทะเบียน IInstanceProvider สำหรับประเภทของมัน

ดังนั้นสิ่งนี้ควรจะครอบคลุมสถานการณ์ที่คุณมี หากคุณโคลนวัตถุที่กราฟย่อยมีสิ่งต่าง ๆ เช่นการเชื่อมต่อ db หรือไฟล์ / สตรีมจัดการคุณเห็นได้ชัดว่ามีปัญหา แต่มันเป็นจริงสำหรับสำเนาลึกทั่วไปใด ๆ

หากคุณต้องการใช้วิธีการทำสำเนาแบบลึกอื่น ๆ แทนบทความนี้แสดงรายการอื่น ๆ หลายวิธีดังนั้นฉันขอแนะนำว่าอย่าพยายามเขียนของคุณเอง


ข้อแรกน่าจะเป็นคำตอบที่ต้องการในขณะที่เขาขอโคลน โปรดทราบว่าด้วยวิธีการคัดลอกคุณอาจไม่จำเป็นต้องตรวจสอบค่าว่างเนื่องจากเป็นวิธีส่วนขยายหากวิธีการดังกล่าวทำสิ่งนั้นอยู่แล้ว คุ้มค่าที่จะลอง.
Dykam

ใช่ฉันสังเกตการตรวจสอบโมฆะ แต่ไม่ต้องการสับสน OP ในกรณีที่เขาไม่ได้อ่านต้นฉบับ
ShuggyCoUk

2
เพียง sidenote: Copyable รุ่นล่าสุดบน GitHub ไม่ต้องการวัตถุที่จะมี Constructor แบบไม่มีพารามิเตอร์ :) ดูgithub.com/havard/copyable
Håvard S

8

คุณสามารถทำได้ค่อนข้างง่าย

    object[] foo = new object[10];
    object[] bar = new object[7];   
    Array.Copy(foo, 3, bar, 0, 7);  

ไม่บาร์จะยังคงว่างอยู่ Array.Copy ไม่ได้สร้างอาเรย์ใหม่อย่างน่าอัศจรรย์โดยเฉพาะเมื่อแถบไม่ผ่านการอ้างอิงหรือออก
Zr40

2
โอ้ใช่แล้วคุณถูกต้องฉันทำสิ่งนี้ด้วยเสียงอึกทึก แต่เดี๋ยวก่อนบางทีเมื่อคุณเขียนบทวิจารณ์ของคุณคุณควรจะแก้ไขคำวิจารณ์ที่สร้างสรรค์มีประโยชน์มากสำหรับทุกคน ดังนั้นก่อนที่ array.copy คุณจะทำ "bar = new object [7];"
RandomNickName42

4

ฉันคิดว่ารหัสที่คุณต้องการคือ:

Array.Copy(oldArray, 0, newArray, BeginIndex, EndIndex - BeginIndex)


ฉันคิดว่าฉันได้รู้จักเพื่อนที่ดีบางคนที่นี่ .... คำตอบเดียวกับคุณ;) และฉันได้รับการโหวตมากมาย !! ฮะ !! อย่างไรก็ตามเวลาที่ดีเวลาที่ดี
RandomNickName42

3

เป็นอีกทางเลือกหนึ่งในการคัดลอกข้อมูลคุณสามารถสร้าง wrapper ที่ให้คุณสามารถเข้าถึงส่วนหนึ่งของอาร์เรย์เดิมได้ราวกับว่ามันเป็นสำเนาของส่วนหนึ่งของอาร์เรย์ ข้อดีคือคุณจะไม่ได้รับสำเนาของข้อมูลในหน่วยความจำอีกและข้อเสียเปรียบคือค่าใช้จ่ายเล็กน้อยเมื่อเข้าถึงข้อมูล

public class SubArray<T> : IEnumerable<T> {

   private T[] _original;
   private int _start;

   public SubArray(T[] original, int start, int len) {
      _original = original;
      _start = start;
      Length = len;
   }

   public T this[int index] {
      get {
         if (index < 0 || index >= Length) throw new IndexOutOfRangeException();
         return _original[_start + index];
      }
   }

   public int Length { get; private set; }

   public IEnumerator<T> GetEnumerator() {
      for (int i = 0; i < Length; i++) {
        yield return _original[_start + i];
      }
   }

   IEnumerator IEnumerable.GetEnumerator() {
      return GetEnumerator();
   }

}

การใช้งาน:

int[] original = { 1, 2, 3, 4, 5 };
SubArray<int> copy = new SubArray<int>(original, 2, 2);

Console.WriteLine(copy.Length); // shows: 2
Console.WriteLine(copy[0]); // shows: 3
foreach (int i in copy) Console.WriteLine(i); // shows 3 and 4

@ Robert: ไม่มันไม่ใช่ ลองใช้ ArraySegment แทนและคุณจะเห็นมากกว่าที่คุณไม่สามารถเข้าถึงรายการตามดัชนีไม่วนซ้ำรายการ
Guffa

2

Array.ConstrainedCopy จะทำงาน

public static void ConstrainedCopy (
    Array sourceArray,
    int sourceIndex,
    Array destinationArray,
    int destinationIndex,
    int length
)

2
นั่นเป็นเพียงการคัดลอกข้อมูล มันจะไม่สร้างอาร์เรย์ใหม่เป็นต้น และถ้าอาร์เรย์เป็นของใหม่เราสามารถใช้ Array.Copy ซึ่งมีประสิทธิภาพมากขึ้น (ไม่จำเป็นต้องมีการตรวจสอบ / ย้อนกลับเพิ่มเติม)
Marc Gravell

ถูกต้อง แต่การสร้าง Array ใหม่เป็นเพียงบรรทัดเดียวและไม่จำเป็นต้องใช้วิธีการใหม่ ฉันยอมรับว่า Array.Copy จะทำงานได้ดี
crauscher

1

ไม่มีวิธีการเดียวที่จะทำสิ่งที่คุณต้องการ คุณจะต้องทำให้วิธีการโคลนพร้อมใช้งานสำหรับชั้นเรียนในอาร์เรย์ของคุณ จากนั้นถ้า LINQ เป็นตัวเลือก:

Foo[] newArray = oldArray.Skip(3).Take(5).Select(item => item.Clone()).ToArray();

class Foo
{
    public Foo Clone()
    {
        return (Foo)MemberwiseClone();
    }
}

1

วิธีใช้Array.ConstrainedCopy :

int[] ArrayOne = new int[8] {1,2,3,4,5,6,7,8};
int[] ArrayTwo = new int[5];
Array.ConstrainedCopy(ArrayOne, 3, ArrayTwo, 0, 7-3);

ด้านล่างคือโพสต์ดั้งเดิมของฉัน มันจะไม่ทำงาน

คุณสามารถใช้Array.CopyTo :

int[] ArrayOne = new int[8] {1,2,3,4,5,6,7,8};
int[] ArrayTwo = new int[5];
ArrayOne.CopyTo(ArrayTwo,3); //starts copy at index=3 until it reaches end of
                             //either array

1

เกี่ยวกับสิ่งนี้:

public T[] CloneCopy(T[] array, int startIndex, int endIndex) where T : ICloneable
{
    T[] retArray = new T[endIndex - startIndex];
    for (int i = startIndex; i < endIndex; i++)
    {
        array[i - startIndex] = array[i].Clone();
    }
    return retArray;

}

จากนั้นคุณจะต้องใช้งานอินเทอร์เฟซ ICloneable กับคลาสทั้งหมดที่คุณต้องการใช้ในส่วนนี้ แต่ควรทำเช่นนั้น


1

ฉันไม่แน่ใจว่ามันลึกแค่ไหน แต่:

MyArray.ToList<TSource>().GetRange(beginningIndex, endIndex).ToArray()

เป็นค่าใช้จ่ายเล็กน้อย แต่อาจตัดวิธีที่ไม่จำเป็นออกไป


1

C # 8 ได้จัดเตรียมฟีเจอร์ที่เรียกว่าเป็น Range เพื่อรับ subarry ตั้งแต่ต้นจนจบดัชนี คุณสามารถใช้มันได้เช่นนี้

Index i1 = 3; // number 3 from beginning  
Index i2 = ^4; // number 4 from end  
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };  
var slice = a[i1..i2]; // { 3, 4, 5 }

นั่นคือบางส่วนของ pythonic บ้า ** t ฉันรักมัน.
Ch3shire

ใช่มันเป็นคุณสมบัติที่ดีจริงๆ
vivek nuna

0

เท่าที่การโคลนนิ่งฉันไม่คิดว่าการทำให้เป็นอนุกรมจะเรียกคอนสตรัคเตอร์ของคุณ สิ่งนี้อาจทำลายค่าคงที่ของคลาสหากคุณทำสิ่งที่น่าสนใจใน ctor's

ดูเหมือนว่าการเดิมพันที่ปลอดภัยกว่านั้นคือวิธีโคลนเสมือนที่เรียกใช้ตัวสร้างสำเนา

protected MyDerivedClass(MyDerivedClass myClass) 
{
  ...
}

public override MyBaseClass Clone()
{
  return new MyDerivedClass(this);
}

การทำให้เป็นอันดับเรียกว่าตัวสร้างของคุณขึ้นอยู่กับ serializer เฉพาะ บางคนทำบางคนทำไม่ได้ แต่ผู้ที่ไม่มีการสนับสนุนการโทรกลับเพื่อให้คุณสามารถทำการแก้ไขใด ๆ ที่จำเป็น
Marc Gravell

สิ่งนี้ชี้ให้เห็นถึงจุดเสียดทานอื่นของการทำให้เป็นอนุกรม: คุณต้องกำหนดค่าเริ่มต้น
30919 Hans Malherbe เมื่อ

0

องค์ประกอบของการโคลนในอาร์เรย์ไม่ใช่สิ่งที่สามารถทำได้อย่างเป็นสากล คุณต้องการโคลนนิ่งลึกหรือคัดลอกจากสมาชิกทั้งหมดหรือไม่?

มาเพื่อแนวทาง "ความพยายามที่ดีที่สุด": การโคลนวัตถุโดยใช้ส่วนต่อประสาน ICloneable หรือการทำให้เป็นอันดับแบบไบนารี:

public static class ArrayExtensions
{
  public static T[] SubArray<T>(this T[] array, int index, int length)
  {
    T[] result = new T[length];

    for (int i=index;i<length+index && i<array.Length;i++)
    {
       if (array[i] is ICloneable)
          result[i-index] = (T) ((ICloneable)array[i]).Clone();
       else
          result[i-index] = (T) CloneObject(array[i]);
    }

    return result;
  }

  private static object CloneObject(object obj)
  {
    BinaryFormatter formatter = new BinaryFormatter();

    using (MemoryStream stream = new MemoryStream())
    {
      formatter.Serialize(stream, obj);

      stream.Seek(0,SeekOrigin.Begin);

      return formatter.Deserialize(stream);
    }
  }
}

นี่ไม่ใช่โซลูชันที่สมบูรณ์แบบเพราะไม่มีเพียงสิ่งใดที่สามารถใช้ได้กับวัตถุประเภทใดก็ได้


นี่ไม่ใช่สิ่งที่เหมือนกับผลลัพธ์ [i-index] = (T) ... ใช่หรือไม่
Donald Byrd

ใช่ :) และไม่เพียงแค่นั้น ขอบเขตของลูปผิด ฉันจะแก้ไข ขอบคุณ!
Philippe Leybaert

0

คุณสามารถเรียนโดย Microsoft:

internal class Set<TElement>
{
    private int[] _buckets;
    private Slot[] _slots;
    private int _count;
    private int _freeList;
    private readonly IEqualityComparer<TElement> _comparer;

    public Set()
        : this(null)
    {
    }

    public Set(IEqualityComparer<TElement> comparer)
    {
        if (comparer == null)
            comparer = EqualityComparer<TElement>.Default;
        _comparer = comparer;
        _buckets = new int[7];
        _slots = new Slot[7];
        _freeList = -1;
    }

    public bool Add(TElement value)
    {
        return !Find(value, true);
    }

    public bool Contains(TElement value)
    {
        return Find(value, false);
    }

    public bool Remove(TElement value)
    {
        var hashCode = InternalGetHashCode(value);
        var index1 = hashCode % _buckets.Length;
        var index2 = -1;
        for (var index3 = _buckets[index1] - 1; index3 >= 0; index3 = _slots[index3].Next)
        {
            if (_slots[index3].HashCode == hashCode && _comparer.Equals(_slots[index3].Value, value))
            {
                if (index2 < 0)
                    _buckets[index1] = _slots[index3].Next + 1;
                else
                    _slots[index2].Next = _slots[index3].Next;
                _slots[index3].HashCode = -1;
                _slots[index3].Value = default(TElement);
                _slots[index3].Next = _freeList;
                _freeList = index3;
                return true;
            }
            index2 = index3;
        }
        return false;
    }

    private bool Find(TElement value, bool add)
    {
        var hashCode = InternalGetHashCode(value);
        for (var index = _buckets[hashCode % _buckets.Length] - 1; index >= 0; index = _slots[index].Next)
        {
            if (_slots[index].HashCode == hashCode && _comparer.Equals(_slots[index].Value, value))
                return true;
        }
        if (add)
        {
            int index1;
            if (_freeList >= 0)
            {
                index1 = _freeList;
                _freeList = _slots[index1].Next;
            }
            else
            {
                if (_count == _slots.Length)
                    Resize();
                index1 = _count;
                ++_count;
            }
            int index2 = hashCode % _buckets.Length;
            _slots[index1].HashCode = hashCode;
            _slots[index1].Value = value;
            _slots[index1].Next = _buckets[index2] - 1;
            _buckets[index2] = index1 + 1;
        }
        return false;
    }

    private void Resize()
    {
        var length = checked(_count * 2 + 1);
        var numArray = new int[length];
        var slotArray = new Slot[length];
        Array.Copy(_slots, 0, slotArray, 0, _count);
        for (var index1 = 0; index1 < _count; ++index1)
        {
            int index2 = slotArray[index1].HashCode % length;
            slotArray[index1].Next = numArray[index2] - 1;
            numArray[index2] = index1 + 1;
        }
        _buckets = numArray;
        _slots = slotArray;
    }

    internal int InternalGetHashCode(TElement value)
    {
        if (value != null)
            return _comparer.GetHashCode(value) & int.MaxValue;
        return 0;
    }

    internal struct Slot
    {
        internal int HashCode;
        internal TElement Value;
        internal int Next;
    }
}

แล้ว

public static T[] GetSub<T>(this T[] first, T[] second)
    {
        var items = IntersectIteratorWithIndex(first, second);
        if (!items.Any()) return new T[] { };


        var index = items.First().Item2;
        var length = first.Count() - index;
        var subArray = new T[length];
        Array.Copy(first, index, subArray, 0, length);
        return subArray;
    }

    private static IEnumerable<Tuple<T, Int32>> IntersectIteratorWithIndex<T>(IEnumerable<T> first, IEnumerable<T> second)
    {
        var firstList = first.ToList();
        var set = new Set<T>();
        foreach (var i in second)
            set.Add(i);
        foreach (var i in firstList)
        {
            if (set.Remove(i))
                yield return new Tuple<T, Int32>(i, firstList.IndexOf(i));
        }
    }

0

นี่เป็นวิธีที่ดีที่สุดที่ฉันพบเมื่อต้องการทำสิ่งนี้:

private void GetSubArrayThroughArraySegment() {
  int[] array = { 10, 20, 30 };
  ArraySegment<int> segment = new ArraySegment<int>(array,  1, 2);
  Console.WriteLine("-- Array --");
  int[] original = segment.Array;
  foreach (int value in original)
  {
    Console.WriteLine(value);
  }
  Console.WriteLine("-- Offset --");
  Console.WriteLine(segment.Offset);
  Console.WriteLine("-- Count --");
  Console.WriteLine(segment.Count);

  Console.WriteLine("-- Range --");
  for (int i = segment.Offset; i <= segment.Count; i++)
  {
    Console.WriteLine(segment.Array[i]);
  }
}

หวังว่าจะช่วย!


0

ใช้วิธีการขยาย:

public static T[] Slice<T>(this T[] source, int start, int end)
    {
        // Handles negative ends.
        if (end < 0)
        {
            end = source.Length + end;
        }
        int len = end - start;

        // Return new array.
        T[] res = new T[len];
        for (int i = 0; i < len; i++)
        {
            res[i] = source[i + start];
        }
        return res;
    }

และคุณสามารถใช้มันได้

var NewArray = OldArray.Slice(3,7);

0

รหัสจาก System.Private.CoreLib.dll:

public static T[] GetSubArray<T>(T[] array, Range range)
{
    if (array == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
    }
    (int Offset, int Length) offsetAndLength = range.GetOffsetAndLength(array.Length);
    int item = offsetAndLength.Offset;
    int item2 = offsetAndLength.Length;
    if (default(T) != null || typeof(T[]) == array.GetType())
    {
        if (item2 == 0)
        {
            return Array.Empty<T>();
        }
        T[] array2 = new T[item2];
        Buffer.Memmove(ref Unsafe.As<byte, T>(ref array2.GetRawSzArrayData()), ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), item), (uint)item2);
        return array2;
    }
    T[] array3 = (T[])Array.CreateInstance(array.GetType().GetElementType(), item2);
    Array.Copy(array, item, array3, 0, item2);
    return array3;
}



0

ไม่ตรงตามข้อกำหนดการโคลนของคุณ แต่ดูเหมือนง่ายกว่าคำตอบมากมาย:

Array NewArray = new ArraySegment(oldArray,BeginIndex , int Count).ToArray();

-1
public   static   T[]   SubArray<T>(T[] data, int index, int length)
        {
            List<T> retVal = new List<T>();
            if (data == null || data.Length == 0)
                return retVal.ToArray();
            bool startRead = false;
            int count = 0;
            for (int i = 0; i < data.Length; i++)
            {
                if (i == index && !startRead)
                    startRead = true;
                if (startRead)
                {

                    retVal.Add(data[i]);
                    count++;

                    if (count == length)
                        break;
                }
            }
            return retVal.ToArray();
        }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.