2013-09-04 62 views
5

Tôi có một mảng lớn chứa các phần tử kiểu tham chiếu và tôi muốn tạo nhiều mảng khác về cơ bản chỉ trỏ đến các phần cụ thể của một mảng lớn đó.Tạo một mảng chỉ tới một phần của mảng khác?

Nói cách khác, tôi muốn tạo "chỉ mục" hoặc "con trỏ có độ dài".

Trong C++ thật dễ dàng để làm như vậy bằng cách sử dụng con trỏ và cho mỗi con trỏ gán một chiều dài, ví dụ tạo một cấu trúc có chứa một con trỏ có độ dài.

Làm thế nào tôi có thể đạt được điều này trong C# /. NET?

Toàn bộ vấn đề là tránh sao chép bất kỳ thứ gì, tôi chỉ muốn con trỏ đến các phần cụ thể trong một mảng đã tồn tại trong bộ nhớ.

Bất kỳ ý tưởng nào?

+0

bạn đã thử bỏ qua chưa? – Sruti

+0

Nếu bạn muốn, bạn có thể sử dụng con trỏ – wudzik

+1

Mảng được lập chỉ mục sử dụng các số nguyên, vì vậy rõ ràng là "con trỏ tới một vị trí cụ thể trong một mảng" là một số nguyên. Độ dài cũng được biểu diễn dưới dạng số nguyên. Vì vậy, bạn đang nói về một cặp số nguyên ở đây. Bạn có thể gộp chúng lại với nhau trong một 'Tuple ' hoặc tạo 'struct' của riêng bạn nếu bạn thích một tên mô tả hơn. – Jon

Trả lời

11

Đề xuất của Jon về việc sử dụng ArraySegment<T> có thể là những gì bạn muốn. Tuy nhiên, nếu bạn muốn đại diện cho một con trỏ đến bên trong của một mảng, cách bạn có thể trong C++, đây là một số mã cho điều đó. Không có bảo hành được thể hiện hoặc ngụ ý, sử dụng có nguy cơ của riêng bạn.

Mã này không theo dõi "độ dài" của con trỏ bên trong theo bất kỳ cách nào, nhưng thật dễ dàng để thêm tính năng đó nếu bạn muốn.

internal struct ArrayPtr<T> 
{ 
    public static ArrayPtr<T> Null { get { return default(ArrayPtr<T>); } } 
    private readonly T[] source; 
    private readonly int index; 

    private ArrayPtr(ArrayPtr<T> old, int delta) 
    { 
    this.source = old.source; 
    this.index = old.index + delta; 
    Debug.Assert(index >= 0); 
    Debug.Assert(index == 0 || this.source != null && index < this.source.Length); 
    } 

    public ArrayPtr(T[] source) 
    { 
    this.source = source; 
    index = 0; 
    } 

    public bool IsNull() 
    { 
    return this.source == null; 
    } 

    public static bool operator <(ArrayPtr<T> a, ArrayPtr<T> b) 
    { 
    Debug.Assert(Object.ReferenceEquals(a.source, b.source)); 
    return a.index < b.index; 
    } 

    public static bool operator >(ArrayPtr<T> a, ArrayPtr<T> b) 
    { 
    Debug.Assert(Object.ReferenceEquals(a.source, b.source)); 
    return a.index > b.index; 
    } 

    public static bool operator <=(ArrayPtr<T> a, ArrayPtr<T> b) 
    { 
    Debug.Assert(Object.ReferenceEquals(a.source, b.source)); 
    return a.index <= b.index; 
    } 

    public static bool operator >=(ArrayPtr<T> a, ArrayPtr<T> b) 
    { 
    Debug.Assert(Object.ReferenceEquals(a.source, b.source)); 
    return a.index >= b.index; 
    } 

    public static int operator -(ArrayPtr<T> a, ArrayPtr<T> b) 
    { 
    Debug.Assert(Object.ReferenceEquals(a.source, b.source)); 
    return a.index - b.index; 
    } 

    public static ArrayPtr<T> operator +(ArrayPtr<T> a, int count) 
    { 
    return new ArrayPtr<T>(a, +count); 
    } 

    public static ArrayPtr<T> operator -(ArrayPtr<T> a, int count) 
    { 
    return new ArrayPtr<T>(a, -count); 
    } 

    public static ArrayPtr<T> operator ++(ArrayPtr<T> a) 
    { 
    return a + 1; 
    } 

    public static ArrayPtr<T> operator --(ArrayPtr<T> a) 
    { 
    return a - 1; 
    } 

    public static implicit operator ArrayPtr<T>(T[] x) 
    { 
    return new ArrayPtr<T>(x); 
    } 

    public static bool operator ==(ArrayPtr<T> x, ArrayPtr<T> y) 
    { 
    return x.source == y.source && x.index == y.index; 
    } 

    public static bool operator !=(ArrayPtr<T> x, ArrayPtr<T> y) 
    { 
    return !(x == y); 
    } 

    public override bool Equals(object x) 
    { 
    if (x == null) return this.source == null; 
    var ptr = x as ArrayPtr<T>?; 
    if (!ptr.HasValue) return false; 
    return this == ptr.Value; 
    } 

    public override int GetHashCode() 
    { 
    unchecked 
    { 
     int hash = this.source == null ? 0 : this.source.GetHashCode(); 
     return hash + this.index; 
    } 
    } 

    public T this[int index] 
    { 
    get { return source[index + this.index]; } 
    set { source[index + this.index] = value; } 
    } 
} 

Bây giờ chúng ta có thể làm những thứ như:

double[] arr = new double[10]; 
var p0 = (ArrayPtr<double>)arr; 
var p5 = p0 + 5; 
p5[0] = 123.4; // sets arr[5] to 123.4 
var p7 = p0 + 7; 
int diff = p7 - p5; // 2 
+0

Tôi ngạc nhiên rằng nó có tất cả các loại toán tử mà tôi không mong đợi (những cái so sánh cụ thể) nhưng không thực hiện 'IEnumerable '- Tôi đã dự kiến ​​rằng lặp qua một phân đoạn sẽ nhiều hơn thường hữu ích hơn các toán tử đó. Nó có ý nghĩa khi bạn nhìn vào nó như thể nó là một con trỏ - ít hơn nếu bạn cố gắng xem nó như là một khung nhìn lên một mảng. –

+0

@JonSkeet: Bạn làm cho một điểm tốt. Ý định của tôi không phải là để làm cho mã này đầy đủ tính năng - tôi sẽ muốn một tài sản 'Length' và' IEnumerable 'như bạn lưu ý và như vậy. Ý định của tôi đúng hơn là lấy một đoạn mã C++ phức tạp được chuyển đến C# càng nhanh càng tốt để tôi có thể tái cấu trúc nó khi rảnh rỗi. Các mã trong câu hỏi đã so sánh con trỏ, bổ sung và trừ, vì vậy đó là tất cả tôi thực hiện. –

+0

Cảm ơn rất nhiều, tôi đã nghĩ đến việc đó (tạo nên lớp học của riêng mình), nhưng tôi đã hy vọng được xây dựng trong đó không lãng phí thời gian của tôi. Jon đã đề nghị tôi sử dụng ArraySegment, thay vào đó sẽ nhanh hơn khi sử dụng nó? Tôi biết tôi đang lười biếng vì tôi có thể tự mình kiểm tra hiệu suất, nhưng tôi đoán bạn chắc đã thử điều đó rồi. –

12

Có vẻ như bạn đang tìm kiếm cái gì đó như ArraySegment<T>. Trái ngược với những suy nghĩ trước đây của tôi, nó không có trình chỉ mục và triển khai IEnumerable<T> v.v. - nó chỉ được thực hiện với giao diện rõ ràng.

Mẫu mã:

using System; 
using System.Collections.Generic; 

static class Test 
{ 
    static void Main() 
    { 
     string[] original = { "The", "quick", "brown", "fox", "jumped", "over", 
       "the", "lazy", "dog" }; 

     IList<string> segment = new ArraySegment<string>(original, 3, 4); 
     Console.WriteLine(segment[2]); // over 
     foreach (var word in segment) 
     { 
      Console.WriteLine(word); // fox jumped over the 
     } 
    } 
} 

EDIT: Như đã nêu trong các ý kiến, ArraySegment<T> là chỉ thực sự "đầy đủ chức năng" trong .NET 4.5. .NET 4 version không triển khai bất kỳ giao diện nào.

+0

Cấu trúc này, 'ArraySegment <>', được mở rộng rất nhiều trong .NET 4.5. Trước 4.5 nó thiếu rất nhiều chức năng "tự nhiên". –

+0

@JeppeStigNielsen: Ah, điều đó có ý nghĩa rất nhiều. Cảm ơn, sẽ lưu ý rằng. –

+0

Cảm ơn rất nhiều, tôi đã không mong đợi để nhận được câu trả lời từ bạn! Một câu hỏi mặc dù, nó đi kèm với bất kỳ hình phạt hiệu suất để sử dụng chỉ số với ArraySegment? –

1

Bạn có thể sử dụng LINQ:

yourArray.Skip(startIndex).Take(numberToTake) 

Truy vấn được lười biếng đánh giá.

Các vấn đề liên quan