🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

VBA Cell Value en Excel — leer y escribir .Value y el truco de rango a matriz

|

VBA Cell Value en Excel — leer y escribir .Value y el truco de rango a matriz

TL;DRRange("A1").Value lee o escribe una sola celda. Lo que de verdad importa es lo que pasa con muchas celdas: arr = Range("A1:C1000").Value lee todo el bloque en una matriz Variant 2D de base 1 en una sola llamada, y Range("A1:C1000").Value = arr la reescribe en una sola llamada. Tocar las celdas de una en una — Cells(i, j).Value dentro de un bucle — cruza de VBA a Excel en cada lectura, y ese cruce de frontera, no la aritmética, es lo que hace lentos a los bucles grandes.

Dim v As Variant
v = Range("A1").Value              ' lee una celda en una variable
Range("B1").Value = v * 1.08       ' escribe una celda

Dim arr As Variant
arr = Range("A1:A10000").Value     ' lee 10.000 celdas en UNA llamada -> matriz 2D
' ... procesa arr en memoria ...
Range("C1:C10000").Value = arr     ' reescribe todas en UNA llamada

Leer y escribir valores es lo primero que hace cualquiera en VBA, y para una sola celda es exactamente tan sencillo como parece. La idea sobre la que se construye esta guía aparece en cuanto tienes más de una celda: .Value sobre un rango no es un valor, es un bloque — una matriz bidimensional — y mover ese bloque en una sola llamada en lugar de celda por celda es la diferencia entre una macro que se siente instantánea y otra que se queda colgada. Retén eso y tanto las reglas de velocidad como las pequeñas trampas de tipos encajan solas.

Lo que aprenderás

  • El modelo mental — .Value es un bloque, y un rango te da una matriz Variant 2D
  • La regla más importante — lee y escribe el bloque entero en un solo viaje de ida y vuelta, no celda por celda
  • Por qué la matriz es de base 1 y 2D, incluso para una sola columna
  • El caso especial de una única celda — una celda es un escalar, no una matriz
  • .Value es la propiedad por defecto — el atajo, y por qué no conviene apoyarse en él
  • Asignar un valor reemplaza lo que hubiera en la celda, fórmula incluida

El modelo mental: .Value es un bloque

Para una sola celda, .Value es un único valor que lees o asignas:

Dim name As String
name = Range("A1").Value           ' escalar de entrada
Range("A2").Value = "Total"        ' escalar de salida

Para un rango de muchas celdas, .Value es toda la cuadrícula de una vez. Léela en un Variant y obtienes una matriz bidimensional; asígnale una matriz bidimensional y estableces todas las celdas de un solo movimiento:

Dim block As Variant
block = Range("A1:C100").Value     ' block ahora es una matriz Variant de 100 x 3

La palabra importante es una vez. Cada vez que tu código lee o escribe .Value, cruza la frontera entre el motor de VBA y la aplicación Excel — un salto relativamente caro. Una celda es un salto; un bloque entero leído en una matriz es también un solo salto. Ese único hecho — un rango es un bloque que puedes mover en un solo cruce — es lo que gobierna la regla de rendimiento de abajo y es lo más valioso que hay que saber sobre .Value.

La regla más importante: haz el viaje de ida y vuelta con la matriz, no recorras las celdas

Aquí tienes el mismo trabajo hecho de dos maneras: recorrer una columna, aumentar cada valor un 8% y escribirlo en la columna de al lado.

La forma lenta toca Excel en cada iteración:

Dim i As Long
For i = 1 To 10000
    Cells(i, 3).Value = Cells(i, 1).Value * 1.08   ' 2 cruces por fila = 20.000 saltos
Next i

La forma rápida cruza dos veces en total y hace el trabajo en memoria:

Dim src As Variant, out() As Variant, i As Long
src = Range("A1:A10000").Value          ' 1 salto de entrada
ReDim out(1 To UBound(src, 1), 1 To 1)
For i = 1 To UBound(src, 1)
    out(i, 1) = src(i, 1) * 1.08        ' VBA puro - Excel no interviene
Next i
Range("C1:C10000").Value = out          ' 1 salto de salida

La aritmética es idéntica; el bucle tiene la misma longitud. La única diferencia es que la segunda versión lee 10.000 celdas en una llamada y escribe 10.000 en una llamada, mientras que la primera hace 20.000 viajes separados a través de la frontera de VBA a Excel. Con datos reales la versión con matriz suele ser decenas de veces más rápida — a menudo la diferencia entre «instantáneo» y «mira el reloj de arena». Para una ganancia aún mayor, combínala con desactivar el recálculo y la actualización de pantalla durante la escritura; consulta VBA Calculation y VBA ScreenUpdating. El cruce de frontera es el coste, y el viaje de ida y vuelta con la matriz es cómo dejas de pagarlo por celda.

Por qué la matriz es de base 1 y 2D

La matriz Variant que obtienes de un rango sorprende a la gente dos veces. Primero, siempre es de base 1, sin importar el ajuste de Option Basearr(1, 1) es la celda superior izquierda. Segundo, siempre es bidimensional, incluso cuando el rango es una sola columna o una sola fila:

Dim col As Variant
col = Range("A1:A5").Value          ' una sola columna
Debug.Print col(3, 1)              ' NO col(3) - es (fila, columna)

Un rango de una columna es una matriz de 5 por 1, así que lo indexas col(fila, 1); un rango de una fila es de 1 por 5, indexado fila(1, columna). Escribir col(3) en lugar de col(3, 1) lanza «Subscript out of range» y es el error más común cuando alguien empieza a usar matrices. Si de verdad quieres una matriz de 1 dimensión a partir de una sola columna, envuelve la lectura en Application.Transpose — pero ten en cuenta que Transpose tiene sus propios límites (topa alrededor de 65.536 elementos y fuerza los tipos), así que para datos grandes o mixtos la matriz 2D simple es más segura. Consulta VBA Array para trabajar con el resultado.

El caso especial de una única celda

Hay un caso límite que rompe el código de matrices: si el rango es una única celda, .Value devuelve un escalar simple, no una matriz de 1 por 1.

Dim v As Variant
v = Range("A1").Value              ' A1 sola -> un escalar, no v(1, 1)
' v(1, 1) aqui lanza un error

Así que el código que lee rng.Value en una matriz y luego indexa arr(1, 1) funciona con un rng de varias celdas pero explota cuando rng resulta ser una sola celda. Si el tamaño de un rango puede reducirse hasta una celda, o bien fuerzas un tamaño mínimo, o bien compruebas rng.Cells.Count > 1 antes de tratar el resultado como una matriz. Es una pequeña incoherencia en el modelo de objetos de Excel, pero provoca bugs reales del tipo «funciona en la hoja grande, falla en la pequeña».

.Value es la propiedad por defecto — el atajo y su riesgo

.Value es la propiedad por defecto de un Range, lo que significa que Excel te deja omitirla:

x = Range("A1")                    ' funciona - .Value implicito
Range("A1") = 42                   ' funciona - .Value implicito

Se lee limpio, y mucho código lo hace. El riesgo es que el atajo esconde qué operación quieres decir. Set rng2 = Range("A1") asigna el objeto rango; x = Range("A1") asigna su valor — la presencia o ausencia de Set cambia el significado en silencio. Y If Range("A1") = Range("B1") compara valores, lo que puede ser o no lo que un lector espera cuando el código no dice nada sobre valores. El hábito que vale la pena mantener: escribe .Value de forma explícita cuando te refieres al valor. Cuesta seis caracteres y elimina toda una clase de ambigüedad del tipo «¿querían la celda o su contenido?» — sobre todo alrededor de Set, que se trata en VBA Range.

Asignar un valor reemplaza la fórmula

Una última cosa que hace tropezar a quien mezcla valores y fórmulas: escribir .Value en una celda sobrescribe lo que hubiera allí, incluida una fórmula.

Range("D2").Formula = "=B2*C2"     ' D2 ahora es una formula viva
Range("D2").Value = 100            ' D2 ahora es el numero estatico 100 - la formula desaparecio

Normalmente es lo que quieres — es exactamente como «conviertes una fórmula en su resultado» en una sola celda (asignas el valor sobre sí mismo). Pero significa que no puedes ajustar un valor sin destruir la fórmula que lo produjo. Cuando necesitas una fórmula viva en la celda, asigna .Formula, no .Value — las dos son caras distintas de la misma celda, y se tratan en VBA Formula. Y cuando te importa el número exacto que lees de vuelta — dinero, fechas, decimales largos — la elección entre .Value y .Value2 empieza a importar, que es el tema de VBA Value vs Value2 vs Text.

Cómo ayuda ExcelMaster

Los errores de .Value que cuestan tiempo de verdad no son erratas — son el bucle lento celda por celda en una hoja grande, el arr(3) que debería haber sido arr(3, 1), el código de matrices que se rompe con un rango de una sola celda, y el .Value implícito que comparó lo que no era sin avisar. Cada uno se ejecuta; solo que se ejecuta mal o lento.

ExcelMaster escribe por defecto la versión rápida y correcta. Pídele «incrementa la columna A un 8% en la columna C» y lee el bloque en una matriz Variant, lo procesa en memoria y lo reescribe en una sola asignación — con el recálculo y la actualización de pantalla gestionados alrededor de la escritura. Indexa la matriz 2D correctamente, protege el caso de una sola celda, y escribe .Value de forma explícita para que el código diga lo que quiere decir. Tú describes la transformación; él escribe el viaje de ida y vuelta que corre en un parpadeo en lugar del bucle que se arrastra.

Preguntas frecuentes

¿Cómo obtengo el valor de una celda en Excel VBA?

Lee la propiedad Value de un Range: x = Range("A1").Value o x = Cells(1, 1).Value. Para muchas celdas a la vez, lee todo el rango en un Variantarr = Range("A1:C100").Value — que te da una matriz bidimensional de base 1 (arr(fila, columna)). Leer un bloque en una matriz es mucho más rápido que leer cada celda en un bucle, porque cada acceso individual a .Value cruza de VBA a Excel.

¿Cómo escribo un valor en una celda en VBA?

Asigna a Value: Range("A1").Value = 42 o Cells(1, 1).Value = "Total". Para escribir muchas celdas a la vez, construye una matriz Variant 2D y asígnala a un rango de la misma forma: Range("A1:A100").Value = arr. Esto escribe las 100 celdas en una sola operación en lugar de recorrerlas en un bucle, lo que es muchísimo más rápido en rangos grandes.

¿Por qué el valor de mi rango en VBA es una matriz 2D?

Porque un rango de varias celdas es una cuadrícula, así que su .Value es una matriz bidimensional indexada como arr(fila, columna) y siempre de base 1 — incluso una sola columna es una matriz de N por 1, así que la indexas arr(3, 1), no arr(3). Indexar con un solo subíndice lanza «Subscript out of range». Una sola celda es la excepción: Range("A1").Value devuelve un escalar simple, no una matriz de 1 por 1.

¿Por qué es tan lento leer celdas en un bucle de VBA?

Cada acceso a Cells(i, j).Value cruza la frontera entre el motor de VBA y la aplicación Excel, y ese salto es la parte cara — no la aritmética. Un bucle sobre 10.000 filas que lee y escribe cada celda hace 20.000 cruces. Leer el rango en una matriz, procesarlo en memoria y reescribir la matriz hace solo dos cruces, y por eso a menudo es decenas de veces más rápido.

¿Cuál es la diferencia entre .Value y usar solo Range(...) en VBA?

.Value es la propiedad por defecto de un Range, así que x = Range("A1") se trata como x = Range("A1").Value. Para los valores se leen igual, pero omitir .Value esconde tu intención: Set rng = Range("A1") asigna el objeto rango, mientras que x = Range("A1") asigna su valor, y la diferencia es solo la palabra clave Set. Escribir .Value de forma explícita hace que el código no sea ambiguo.

Probado en

Probado en: Excel 365 (Windows 11), VBA 7.1 — última verificación el 31/08/2026.

Guías relacionadas: VBA Range · VBA Cells · VBA Formula · VBA Value vs Value2 vs Text · VBA Array