2009-09-30 55 views
9

我在c#陣列是基於1的(從到的get_value呼叫用於Excel範圍 生成的I得到例如2D陣列調整基線C#中的基於1的陣列

object[,] ExcelData = (object[,]) MySheet.UsedRange.get_Value(Excel.XlRangeValueDataType.xlRangeValueDefault); 

這似乎作爲例如ExcelData [1..20,1..5]

有沒有辦法告訴編譯器底墊,這樣我就不需要加1,循環計數器的全部時間?

數組
List<string> RowHeadings = new List<string>(); 
string [,] Results = new string[MaxRows, 1] 
for (int Row = 0; Row < MaxRows; Row++) { 
    if (ExcelData[Row+1, 1] != null) 
     RowHeadings.Add(ExcelData[Row+1, 1]); 
     ... 
     ... 
     Results[Row, 0] = ExcelData[Row+1, 1]; 
     & other stuff in here that requires a 0-based Row 
} 

它使東西le ss可讀,因爲當創建數組寫入數組時將基於零。

+0

c#中的數組始終爲0,您的意思是說您的數據是基於1的? – cjk 2009-09-30 12:34:15

+6

在C#源代碼中聲明*的數組始終是基於0的,但CLR支持具有任意下限的數組。 – 2009-09-30 12:56:28

+1

在c#中數組_created_很可能是基於零的,但是我相信我是正確的,說這裏的數組是基於1的 - 如果你嘗試訪問ExcelData [0,0],就會拋出IncexOutOfRangeException。 – GalleySlave 2009-09-30 13:11:18

回答

10

爲什麼不只是改變你的索引?

List<string> RowHeadings = new List<string>(); 
for (int Row = 1; Row <= MaxRows; Row++) { 
    if (ExcelData[Row, 1] != null) 
     RowHeadings.Add(ExcelData[Row, 1]); 
} 

編輯:這裏是想從你的原始創建一個新的,從零開始的數組的擴展方法(基本上它只是創建一個新的數組,它是一個元素小,並複製到新陣列中的所有元素,但你正在跳過無論如何)的第一個元素:

public static T[] ToZeroBasedArray<T>(this T[] array) 
{ 
    int len = array.Length - 1; 
    T[] newArray = new T[len]; 
    Array.Copy(array, 1, newArray, 0, len); 
    return newArray; 
} 

話雖這麼說,你需要考慮,如果創建新陣列的處罰(但輕微)值得改進代碼的可讀性。我沒有做出判斷(它可能是值得的)我只是確保你不用這個代碼運行,如果它會傷害你的應用程序的性能。

+0

我不能相信我們的樹在同一時間發佈相同的答案:) – Philippe 2009-09-30 12:25:00

+0

抱歉 - 我提供的代碼太簡單了 - 行在for循環中的其他地方使用,需要基於零的計數。 – GalleySlave 2009-09-30 12:40:44

1

正在改變循環計數器太難嗎?

for (int Row = 1; Row <= MaxRows; Row++) 

如果計數器的範圍是正確的,你不必1添加到任何的循環中,這樣你就不會失去可讀性。把事情簡單化。

+10

沒有必要太光顧 - 看到原來的問題的變化(其他人說,更有建設性的相同的事情)... – GalleySlave 2009-09-30 12:47:45

3

爲什麼不使用:

for (int Row = 1; Row <= MaxRows; Row++) { 

或者是有什麼我失蹤?

編輯:事實證明,缺少的東西,我會使用另一個計數器(從0開始)爲此目的,並使用基於1的行索引的數組。將索引用於目標數組中的索引以外的其他用途不是好習慣。

+0

缺少一些東西:)見上面的評論... – GalleySlave 2009-09-30 12:41:40

+0

傑森發佈了一個很好的答案,說明我的編輯。 – Philippe 2009-09-30 12:52:04

6

this[,]索引器爲ExcelData數組創建一個包裝器,並在那裏創建重新綁定邏輯。喜歡的東西:

class ExcelDataWrapper 
{ 
    private object[,] _excelData; 
    public ExcelDataWrapper(object[,] excelData) 
    { 
     _excelData = excelData; 
    } 
    public object this[int x, int y] 
    { 
     return _excelData[x+1, y+1]; 
    } 
} 
+0

我喜歡這個解決方案,因爲你可以讓它變成動態的。我給構造函數添加了一個'start index'參數,所以這個類可以用於基於1和0的數組。我還添加了'RowCount'&'ColumnCount'屬性,而不必指定'array.GetLength(x)' – ChandlerPelhams 2012-02-08 20:05:48

6

由於需要爲-IS(根據您的意見),另外,你可以只介紹另一個循環變量:

List<string> RowHeadings = new List<string>(); 
string [,] Results = new string[MaxRows, 1] 
for (int Row = 0, SrcRow = 1; SrcRow <= MaxRows; Row++, SrcRow++) { 
    if (ExcelData[SrcRow, 1] != null) 
     RowHeadings.Add(ExcelData[SrcRow, 1]); 
     ... 
     ... 
     Results[Row, 0] = ExcelData[SrcRow, 1]; 
} 
0

我同意與基地-1陣列工作來自.NET可能是一件麻煩事。這也有潛在的錯誤傾向,因爲每次使用它時都必須精神上轉變,並且要正確記住哪些情況將是基數1,哪些情況將基於0.

最高性能的方法是隻需根據需要使用base-1或base-0來適當地進行這些精神轉變和索引。

我個人更喜歡將二維base-1數組轉換爲二維base-0數組。不幸的是,這需要將數組複製到新數組,因爲無法將數組重新設置到位。

這裏是一個擴展方法,可以通過Excel中返回的二維數組做到這一點:

public static TResult[,] CloneBase0<TSource, TResult>(
    this TSource[,] sourceArray) 
{ 
    If (sourceArray == null) 
    { 
     throw new ArgumentNullException(
      "The 'sourceArray' is null, which is invalid."); 
    } 

    int numRows = sourceArray.GetLength(0); 
    int numColumns = sourceArray.GetLength(1); 
    TResult[,] resultArray = new TResult[numRows, numColumns]; 

    int lb1 = sourceArray.GetLowerBound(0); 
    int lb2 = sourceArray.GetLowerBound(1); 

    for (int r = 0; r < numRows; r++) 
    { 
     for (int c = 0; c < numColumns; c++) 
     { 
      resultArray[r, c] = sourceArray[lb1 + r, lb2 + c]; 
     } 
    } 

    return resultArray; 
} 

然後你就可以使用它像這樣:

object[,] array2DBase1 = (object[,]) MySheet.UsedRange.get_Value(Type.Missing); 

object[,] array2DBase0 = array2DBase1.CloneBase0(); 

for (int row = 0; row < array2DBase0.GetLength(0); row++) 
{ 
    for (int column = 0; column < array2DBase0.GetLength(1); column++) 
    { 
     // Your code goes here... 
    } 
} 

對於大規模大小的數組,你可能不想這樣做,但是我發現通常它會清理你的代碼(和思維定式)來進行這種轉換,然後總是以base-0工作。

希望這有助於...

邁克

+0

謝謝 - 優化,驗證了長度,您可能想使用System.Array.Copy(from,to,長度)(可能?)更有效地複製二維數組。 – GalleySlave 2009-10-02 09:20:26

+0

這是個好主意,除非你不能。 'Array.Copy'方法只適用於1維數組,而Excel.Range.get_Value返回的數組是二維數組。所以擴展方法必須返回基於TSource [,]的TResult [,],並且不能使用'Array.Copy'。沒有選擇! – 2009-10-02 17:11:55

+0

順便說一句,我在處理由Excel返回的base-1數組時處理所有代碼。我想我正在減慢我的所有代碼,但並沒有注意到我所注意到的,並且始終處理基數爲0的數組的能力確實有助於保持我的理智。 :) – 2009-10-02 17:13:35

-1

你可以使用一個第三方Excel兼容的組件如已.NET友好的API - 包括基於0的索引的API,如IRange [INT的rowIndex ,int colIndex]。

在幾乎所有情況下,這樣的組件也將比Excel API快得多。

聲明:我自己的SpreadsheetGear LLC

+0

謝謝,雖然對於我目前需要的所有東西(一套包裝)來說似乎很昂貴 - 沒有提到編組,我發現 - 我是否仍然需要處理該問題或者SpreadsheetGear是否處理這一切? – GalleySlave 2009-10-02 09:44:07

+0

SpreadsheetGear不僅僅是Excel的包裝器,而且是一個用C#編寫的安全的.NET程序集。它不使用COM Interop進行任何操作。它不依賴於Excel。它是一個Excel兼容的電子表格組件,具有自己的計算,編輯,格式化,渲染等等...引擎。在許多情況下,客戶告訴我們,當應用程序從COM Interop/Excel自動轉換到SpreadsheetGear時,應用程序會顯着加速。您也可以放心擔心用戶擁有哪個版本的Excel。 – 2009-10-02 14:08:11

0

對於基於1個的陣列和Excel範圍操作以及UDF(的SharePoint)函數我使用此效用函數

public static object[,] ToObjectArray(this Object Range) 
    { 
     Type type = Range.GetType(); 
     if (type.IsArray && type.Name == "Object[,]") 
     { 
      var sourceArray = Range as Object[,];    

      int lb1 = sourceArray.GetLowerBound(0); 
      int lb2 = sourceArray.GetLowerBound(1); 
      if (lb1 == 0 && lb2 == 0) 
      { 
       return sourceArray; 
      } 
      else 
      { 
       int numRows = sourceArray.GetLength(0); 
       int numColumns = sourceArray.GetLength(1); 
       var resultArray = new Object[numRows, numColumns]; 
       for (int r = 0; r < numRows; r++) 
       { 
        for (int c = 0; c < numColumns; c++) 
        { 
         resultArray[r, c] = sourceArray[lb1 + r, lb2 + c]; 
        } 
       } 

       return resultArray; 
      } 

     } 
     else if (type.IsCOMObject) 
     { 
      // Get the Value2 property from the object. 
      Object value = type.InvokeMember("Value2", 

        System.Reflection.BindingFlags.Instance | 

        System.Reflection.BindingFlags.Public | 

        System.Reflection.BindingFlags.GetProperty, 

        null, 

        Range, 

        null); 
      if (value == null) 
       value = string.Empty; 
      if (value is string) 
       return new object[,] { { value } }; 
      else if (value is double) 
       return new object[,] { { value } }; 
      else 
      { 
       object[,] range = (object[,])value; 

       int rows = range.GetLength(0); 

       int columns = range.GetLength(1); 

       object[,] param = new object[rows, columns]; 

       Array.Copy(range, param, rows * columns); 
       return param; 
      } 
     } 

     else 
      throw new ArgumentException("Not A Excel Range Com Object"); 

    } 

用法

public object[,] RemoveZeros(object range) 
    { 
     return this.RemoveZeros(range.ToObjectArray()); 
    } 
    [ComVisible(false)] 
    [UdfMethod(IsVolatile = false)] 
    public object[,] RemoveZeros(Object[,] range) 
    {...} 

第一個函數是com可見的,將接受來自另一個函數的excel範圍或鏈接調用(鏈接調用將返回基於1的對象數組),第二個調用是UDF爲SharePoint中的Excel Services啓用。所有的邏輯都在第二個函數中。在這個例子中,我們只是重新格式化一個範圍,用string.empty替換零。