在c#中重新使用基于1的数组
我有一个数组在基于1的(从一个Excel范围调用get_Value生成我得到一个二维数组例如
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 }
它使得事情变得更不可读,因为当创build一个数组来写入数组时将是基于零的。
为什么不只是改变你的索引?
List<string> RowHeadings = new List<string>(); for (int Row = 1; Row <= MaxRows; Row++) { if (ExcelData[Row, 1] != null) RowHeadings.Add(ExcelData[Row, 1]); }
编辑:这是一个扩展方法,将创build一个新的,从原来的基于零的数组(基本上,它只是创build一个新的数组是一个小的元素,并复制到新的数组,所有的元素,但你目前的第一个元素无论如何跳过):
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; }
这就是说你需要考虑创build一个新数组的惩罚(无论是轻微的)是否值得提高代码的可读性。 我没有做出判断(这可能是值得的)我只是确保你不用这个代码运行,如果它会伤害你的应用程序的性能。
用this[,]
索引器为ExcelData
数组创build一个包装器,并在那里创build重定位逻辑。 就像是:
class ExcelDataWrapper { private object[,] _excelData; public ExcelDataWrapper(object[,] excelData) { _excelData = excelData; } public object this[int x, int y] { return _excelData[x+1, y+1]; } }
既然你需要Row
保持原样(根据你的意见),你可以引入另一个循环variables:
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]; }
为什么不使用:
for (int Row = 1; Row <= MaxRows; Row++) {
还是有什么我失踪?
编辑:事实certificate,缺less的东西,我会使用另一个计数器(从0开始)为此目的,并使用基于1的行索引的数组。 将索引用于目标数组中的索引以外的其他用途不是好习惯。
是不是改变循环计数器太难了?
for (int Row = 1; Row <= MaxRows; Row++)
如果计数器的范围是正确的,则不必在循环内部添加1,这样就不会失去可读性。 把事情简单化。
我同意使用.NET的base-1数组可能会很麻烦。 这也是潜在的错误倾向性的,因为每次使用它的时候都必须精神上转移一下,并且正确地记住哪些情况将是基数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... } }
对于大规模的arrays,你可能不想这样做,但是我发现,一般来说,它真的清理你的代码(和思维设置)来进行这个转换,然后总是以base-0工作。
希望这可以帮助…
麦克风
对于基于1的数组和Excel范围操作以及UDF(SharePoint)function,我使用此实用程序function
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的对象数组),第二个调用为SharePoint中的Excel Services启用UDF。 所有的逻辑在第二个function。 在这个例子中,我们只是重新格式化一个范围,用string.emptyreplace零。
您可以使用第三方Excel兼容组件,例如具有.NET友好API的SpreadsheetGear for .NET ,包括基于0的索引,如IRange [int rowIndex,int colIndex]。
在几乎所有情况下,这些组件也将比Excel API快得多。
免责声明:我自己的SpreadsheetGear LLC