问题
How can I start the index in an ArrayList
at 1 instead of 0? Is there a way to do that directly in code?
(Note that I am asking for ArrayList
, for plain arrays please see Initializing an array on arbitrary starting index in c#)
回答1:
The obvious way to do it would be to wrap an ArrayList in your own implementation, and subtract 1 from the indexer in all operations that uses the index.
You can in fact also declare an array in .NET, that has an arbitrary lower bound (Scroll down to the section "Creating Arrays with a Non-zero Lower Bound").
With that said, please don't do it in production code. It matters to be consistent.
回答2:
One legitimate reason to do this is in writing unit tests. Certain 3rd party SDKs (for example Excel COM interop) expect you to work with arrays that have one-based indices. If you're working with such an SDK, you can and should stub out this functionality in your C# test framework.
The following should work:
object[,] comInteropArray = Array.CreateInstance(
typeof(object),
new int[] { 3, 5 },
new int[] { 1, 1 }) as object[,];
System.Diagnostics.Debug.Assert(
1 == comInteropArray.GetLowerBound(0),
"Does it look like a COM interop array?");
System.Diagnostics.Debug.Assert(
1 == comInteropArray.GetLowerBound(1),
"Does it still look like a COM interop array?");
I don't know if it's possible to cast this as a standard C# style array. Probably not. I also don't know of clean way to hard-code the initial values of such an array, other than looping to copy them from a standard C# array with hard-coded initial values. Neither of these shortcomings should be a big deal in test code.
回答3:
Well, you could make your own:
public class MyArrayList<T> extends ArrayList<T> {
public T get(int index) {
super.get(index - 1);
}
public void set(int index, T value) {
super.set(index - 1, value);
}
}
But it begs the question: why on earth would you bother?
回答4:
I don't know if it still does, but at one point, Visual Basic would let you start your array indexes at 1 with Option Base 1.
In C#, none of the System.Collections collections support this, but you could always write a wrapper class that does the index translation for you.
Really, though, this should be avoided. VB's Option Base
created more problems than it solved, I imagine because it broke assumptions and made things more confusing.
回答5:
Stuff getValueAtOneBasedIndex(ArrayList<Stuff> list, index) {
return list.get(index -1);
}
回答6:
As the other answers suggest, there are ways to simulate this, but no direct way to do this in C# or other commonly used .NET languages. Quoting Eric Gunnerson:
The CLR does support this kind of construct (I had a hard time not using the term "travesty" here...), but there aren't, to my knowledge, any languages that have built-in syntax to do that.
回答7:
Here is something I am using right now to quickly port a VBA application to C#:
It's an Array class that starts with 1 and accepts multiple dimensions.
Although there is some additional work to do (IEnumerator, etc...), it's a start, and is enough to me as I only use indexers.
public class OneBasedArray<T>
{
Array innerArray;
public OneBasedArray(params int[] lengths)
{
innerArray = Array.CreateInstance(typeof(T), lengths, Enumerable.Repeat<int>(1, lengths.Length).ToArray());
}
public T this[params int[] i]
{
get
{
return (T)innerArray.GetValue(i);
}
set
{
innerArray.SetValue(value, i);
}
}
}
回答8:
First, let me preface this answer by explaining my use case: Excel Interop. It's much easier to read and write data using arrays, but Excel Range.Value2 magically returns a 1 based object array.
So, if you are writing to Excel and that's the reason you are asking this question in the first place... then perhaps stop fighting c# and let Excel instantiate the array for you. So instead of:
object[,] newArray = new object[indexR, indexC];
You can use:
object[,] newArray = (object[,])RangeToWriteTo.Value2;
In my case, I created a wrapper class to allow me to use an array for the properties like so:
public abstract class ExcelRowBase
{
public object[,] data;
public ExcelRowBase(int index)
{
data = new object[2, index + 1];
}
}
public class InstanceRowModel : ExcelRowBase
{
public InstanceRowModel() : base(8)
{
//constructor unique to Wire Table
}
public object Configuration
{
get
{
return data[1, 1];
}
set
{
data[1, 1] = value;
}
}
...
So in all cases, I'm reading from the same index. So to make the transition from a model I'm passing into a Create method, I just need to copy over the properties into the model that uses the array created from Excel.
insertingModel.data = (object[,])writeRange.Value2;
//manually copying values from one array to the other
insertingModel.Configuration = model.Configuration;
...
writeRange.Value2 = insertingModel.data;
This works for me for now because I only have to do this in the create function. In update / get / delete, you're getting the Excel based array anyway. Perhaps a future improvement would be to create an Excel range factory that avoids the 0 based default construction all together, but this solution just gave me greens on my tests, so I'm moving on!
来源:https://stackoverflow.com/questions/2799088/how-can-i-create-an-arraylist-with-a-starting-index-of-1-instead-of-0