2010-05-09 11 views
10

¿Cómo puedo iniciar el índice en un ArrayList en 1 en lugar de 0? ¿Hay alguna forma de hacerlo directamente en el código?¿Cómo puedo crear una ArrayList con un índice inicial de 1 (en lugar de 0)

(Tenga en cuenta que estoy pidiendo ArrayList, para las matrices de civil consulte Initializing an array on arbitrary starting index in c#)

+14

¿Por qué le gustaría? – Eric

+0

@Eric Diré por qué, en el primer año en nuestra universidad, los estudiantes toman Pascal y la indexación aquí comienza desde 1, así que preguntaron si pueden hacer lo mismo con C# (solo una pregunta). –

+0

¿Qué hay de 0.5? :) –

Respuesta

17

La manera obvia de hacerlo sería para envolver un ArrayList en su propia aplicación, y restar 1 del controlador paso a paso en todas las operaciones que usa el índice.

De hecho, también puede declarar una matriz en .NET, que tiene un arbitrary lower bound (Desplácese hacia abajo a la sección "Creación de matrices con un límite inferior distinto de cero").

Dicho esto, por favor no lo haga en el código de producción. Importa ser consistente.

1
Stuff getValueAtOneBasedIndex(ArrayList<Stuff> list, index) { 
    return list.get(index -1); 
} 
4

Bueno, usted podría hacer su propia:

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); 
    } 
} 

Pero se plantea la pregunta: ¿por qué molestarse?

1

Como sugieren las otras respuestas, hay formas de simular esto, pero no hay una forma directa de hacerlo en C# u otros lenguajes .NET comúnmente utilizados. Citando Eric Gunnerson:

El CLR no apoyar este tipo de construcción (que tenía un tiempo difícil no usar el término "farsa" aquí ...), pero No hay, que yo sepa, ningún idiomas que tienen una sintaxis incorporada al hacer eso.

2

no sé si todavía lo hace, pero en un momento, Visual Basic le permitirá iniciar sus índices de matriz a 1 con Option Base 1.

En C#, ninguna de las colecciones System.Collections admite esto, pero siempre puede escribir una clase contenedora que realice la traducción de índice por usted.

Realmente, sin embargo, esto debe evitarse. El Option Base de VB creó más problemas de los que resolvió, me imagino porque rompió las suposiciones e hizo las cosas más confusas.

7

Una razón legítima para hacer esto es al escribir pruebas unitarias. Ciertos SDK de terceros (por ejemplo, interoperabilidad de Excel COM) esperan que trabaje con matrices que tienen índices basados ​​en una sola. Si está trabajando con un SDK de este tipo, puede y debe anular esta funcionalidad en su marco de prueba C#.

El siguiente debería funcionar:

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?"); 

No sé si es posible lanzar esto como una matriz C# estilo estándar. Probablemente no. Tampoco sé de manera limpia para codificar los valores iniciales de dicha matriz, que no sea un bucle para copiarlos de una matriz C# estándar con valores iniciales codificados. Ninguna de estas deficiencias debería ser un gran problema en el código de prueba.

+0

Noté que mi comentario se aplica a las matrices, no a ArrayLists. Oops. Espero que las personas que trabajan con Excel y VTSO aún lo encuentren útil, aunque se desvíe de la pregunta original. –

1

Aquí es algo que estoy utilizando ahora mismo para una aplicación rápida puerto de VBA para C#:

Es una clase Array que comienza con 1 y acepta múltiples dimensiones.

Aunque hay algo de trabajo adicional que hacer (IEnumerator, etc ...), es un comienzo, y es suficiente para mí, ya que solo uso indexadores.

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); 
     } 
    } 
} 
+0

¡Esto es fantástico! Estoy convirtiendo algunos viejos juegos de Spectrum Basic y esto es invaluable. – Shevek

+0

¡Me alegra que ayude! :) – Larry

0

En primer lugar, déjame aclarar esta respuesta explicando mi caso de uso: Excel interoperabilidad. Es mucho más fácil leer y escribir datos utilizando matrices, pero Excel Range.Value2 devuelve mágicamente una matriz de objetos basada en 1.

Entonces, si está escribiendo en Excel y esa es la razón por la que está haciendo esta pregunta en primer lugar ... entonces quizás deje de pelear contra C# y permita que Excel haga una instancia de la matriz por usted. Así que en lugar de:

object[,] newArray = new object[indexR, indexC]; 

Se puede utilizar:

object[,] newArray = (object[,])RangeToWriteTo.Value2; 

En mi caso, he creado una clase de contenedor para permitir que utilice una matriz para las propiedades de este modo:

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; 
      } 
     } 
... 

Entonces, en todos los casos, estoy leyendo desde el mismo índice. Entonces, para hacer la transición de un modelo que paso a un método Create, solo necesito copiar las propiedades en el modelo que usa la matriz creada desde Excel.

  insertingModel.data = (object[,])writeRange.Value2; 

      //manually copying values from one array to the other 
      insertingModel.Configuration = model.Configuration; 
      ... 

      writeRange.Value2 = insertingModel.data; 

Esto funciona para mí por ahora porque solo tengo que hacer esto en la función de creación. En update/get/delete, de todos modos obtendrá la matriz basada en Excel. Tal vez una mejora futura sería crear una fábrica de rango Excel que evite la construcción predeterminada basada en 0, pero esta solución solo me dio verdes en mis pruebas, ¡así que sigo adelante!

Cuestiones relacionadas