2016-03-05 24 views
1

我想有一個數組,它將所有類的參數保存在一個數組中,然後讓這些參數中的每一個都指向它們在大數組中的位置。在C#中有可能有一個大數組,然後是較小的指向較大數組的子區的數組嗎?

我想這樣做的原因是因爲我打算優化基於上述參數的函數,但最小化需要所有參數放在一個數組中 - 我不想複製這個在每一步都來回排列 - 我寧願讓各個參數指向它們在主數組中的位置。事情是這樣的:

class myClass{ 
    int nVariables; 
    public double[] parameters; //length = X * nVarialbes 
    public double[] A; //length = X 
    public double[] B; //length = X 
    public double[] C; //length = X 
} 

其中parameters真的像[a1,a2,a3,b1,b2,b3,c1,c2,c3](其中在本例中X==3) - 每個參數的數量同樣在這裏,但我寧願一個解決辦法,讓這是通用的。

這也將允許我更改ABC的元素,並使數組保持全部更新。

我需要將數組作爲單個連續數組進行優化,所以只是做parameters = new double[]{A,B,C};將無法​​工作 - 除非有辦法,然後我可以訪問它,就好像它是一個單一的數組;我的理解是,一個鋸齒狀的數組在內存中不是連續的,而且我不熟悉矩形數組來做我想要的。

+0

如果你走的不安全的路線你可以用指針和所有那些東西,還是工會式的結構。雖然我不會推薦d it。 – Chris

回答

2

這對於傳統陣列來說是不可能的。 .NET/CIL中的數組以8字節標題開頭,提供虛擬表和數組的長度。由於8字節標題,在另一個數組中聲明一個數組是不可行的。

但是,這可以用指針來完成。採取下面的類:

unsafe class MyClass : IDisposable 
{ 
    double[] parameters = new double[9] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 
    private double* p; 
    public double* A; 
    public double* B; 
    public double* C; 

    GCHandle _handle; 
    public MyClass() 
    { 
     _handle = GCHandle.Alloc(parameters, GCHandleType.Pinned); 
     p = (double*)_handle.AddrOfPinnedObject().ToPointer(); 
     A = p; 
     B = (p + 3); 
     C = (p + 6); 
    } 

    public override string ToString() 
    { 
     return String.Join(", ", parameters.Select(a=>a.ToString())); 
    } 

    private bool disposedValue = false; 
    protected virtual void Dispose(bool disposing) 
    { 
     if (!disposedValue) 
     { 
      _handle.Free(); 
      disposedValue = true; 
     } 
    } 

    ~MyClass() { 
     Dispose(false); 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 
} 

構造函數創建一個固定指針parameters,這防止了垃圾收集器從在存儲器中移動它。指針pparameters的第一個元素的地址。該類公開了指向數組的第0,第3和第6個元素的三個指針(A,B和C)。

雖然指針不是數組,但它們的引用與C數組類似。作爲一個簡單的驅動程序:

unsafe static void Main(string[] args) 
{ 
    MyClass mc = new myClass(); 
    Console.WriteLine(mc.ToString()); // prints 1, 2, 3, 4, 5, 6, 7, 8, 9 
    mc.B[2] = 42; // now parameter[5] set to 42 
    Console.WriteLine(mc.ToString()); // prints 1, 2, 3, 4, 5, 42, 7, 8, 9 
} 

作爲一個謹慎,這種做法是不地道的C#,那裏不安全代碼rarely required一般保留專門的方案。使用ArraySegment<T>或建議的其他方法更傳統。

+0

完美。謝謝。 – will

4

這在.NET中不可行。數組不是指針。

你必須使用非數組的類型。如果你想引用一個數組的子部分,你可以使用ArraySegment<T>類型。您還可以創建包裝數組的一部分的自定義IList<T>

也許答案會變成沒有實際的方法來實現你想要的。我相信正在做一些工作來爲.NET添加「切片」,這些切片能夠以您想要的方式引用內存範圍。這個工作是在Github的corefxlab repo上完成的。

+0

因此,答案基本上是「用cpp來代替」,至少現在呢? – will

+0

我不會那麼說。語言的選擇取決於更多的擔憂。我不能推薦一種語言,因爲我對你的場景的看法有限。 – usr

0

您能否擁有一個簡單的幫助函數,它接受主數組,函數偏移量,長度(用於檢查目的)以及他們想要從函數相對數組中獲得的參數。

我可以看到像這樣的工作的函數:

public T GetFunctionParam(T[] mainArray, 
          int FuncOffset, 
          int FuncParamCount, 
          int targetParam) 

然後需要得到一個特定的參數

var param = GetFunctionParam(mArray, 3, 3, 2) 

時,你可以簡單地調用它的函數內部,而是所有類型必須是在數組中相同,或者使用繼承或接口。

如果主數組中的對象是引用類型,則可以爲每個函數創建單獨的數組,然後使用對原始對象的引用來填充它。

+0

,但這並不能解決直接訪問我想要通過優化的元素的需求。 – will

0

如何不創建第二個數組,其中包含主數組的索引?

paramters[A[0]] 

呼叫然後你的價值

喜歡的東西:

using System; 

namespace Test 
{ 
    class Program 
    { 
    static void Main(string[] args) 
    { 
     var mClass = new myClass(); 

     Console.WriteLine("Before Optimization"); 
     mClass.PrintParameters(); 

     mClass.Optimize(); 

     Console.WriteLine("After Optimization"); 
     mClass.PrintParameters(); 

     Console.ReadKey(); 
    } 
    } 

    class myClass 
    { 
    int nVariables; 
    public double[] parameters; //length = X * nVarialbes 
    public int[] A; //length = X 
    public int[] B; //length = X 
    public int[] C; //length = X 

    public myClass() 
    { 
     parameters = new[] {0.0, 1.1, 2.2, 3.3, 4.4, 5.5}; 

     A = new[] {0, 1}; 
     B = new[] {2, 3}; 
     C = new[] {4, 5}; 
    } 

    public void Optimize() 
    { 
     //A's need to have b's paramaters added to them 
     parameters[A[0]] += parameters[B[0]]; 
     parameters[A[1]] += parameters[B[1]]; 

     //C's paramaters get their sign inverted 
     parameters[C[0]] *= -1; 
     parameters[C[1]] *= -1; 
    } 

    public void PrintParameters() 
    { 
     for(int index =0; index < parameters.Length; ++ index) 
     { 
     Console.WriteLine("[{0}] = {1}", index, parameters[index]); 
     } 
    } 
    } 
} 
+0

問題是我需要能夠將參數和相同參數的任意子集傳遞給優化,並且使得內部所做的更改反映在對象中,而不必反覆地複製數組內容。 – will

+0

在這種情況下,函數數組'A'不直接包含這些值,它們包含一個指向主'參數'數組的指針。當優化器更改值'parameter [A [0]] = 4'時,主數組中的值會更改。這是使用第二個數組指向主數組中的元素,它從不具體保存實際值。 – SBurris

+0

這仍然沒有達到我想要的 - 優化需要一個連續的變量數組,而不是整個數組和索引列表。 – will

0
int n = 0; //index for long array 
parameters = new double[combinedLength]; 
double[][] arrays = new double[][]{A,B,C}; //you can do it with 3 "for" loops instead. 
for(int i = 0; i < arrays.length; i++){ 
    for(int j = 0; j < arrays[i].length; j++){ 
     parameters[n++] = arrays[i][j]; 
    } 
} 

這段代碼使用任何長度的3個陣列,並將它們複製到一個單一的長陣列。
重要的是要注意這是一個副本,而不是一個參考,所以如果你改變一個,另一個將保持不變。

如果你想讓一個數組影響其他數組,那麼讓A,B和C成爲具有所需變量的對象數組。相同的代碼應該用於組裝數組。
使用對象時,賦值運算符不會複製該值,而是分配一個引用。
因此參數[0]與A [0]是同一個對象。

小心保持同一個對象的多個引用,這是很多bug,內存泄漏和一般頭痛的常見原因。

+0

這正是我試圖避免的情況,我不認爲你理解這個問題。 – will

0

只是爲了好玩,我會在不安全的結構,固定陣列和顯式佈局的另一個選項添加:

[StructLayout(LayoutKind.Explicit)] 
unsafe struct BadSolution 
{ 
    [FieldOffset(0)] 
    public fixed double Data[3 * 3]; 

    [FieldOffset(8 * 0)] 
    public fixed double A[3]; 
    [FieldOffset(8 * 3)] 
    public fixed double B[3]; 
    [FieldOffset(8 * 6)] 
    public fixed double C[3]; 
} 

然而,這種帶有一堆缺點,其中一些是:

  1. 它只適用於結構,所以你必須非常小心你如何傳遞它(與任何可變結構一樣)。
  2. 它給你一個非常大的結構(數組是結構的一部分,而不是引用)。
  3. 它是固定的 - 您必須在編譯時手動執行佈局。
  4. 他們不再合適Array的。的任何方法(如Array.CopyArray.Sort一個期望的Array將不起作用。
  5. 它使用不安全的代碼。

(當然4 & 5適用於使用不安全的代碼以及其他的答案)

所以這樣做的缺點是很多,我不會推薦任何人走這條道路,但是,這是可能的

+0

這種方法有什麼缺點? – will

+0

@ will增加了一些缺點。 – Chris