Cómo crear listas de tuplas para almacenar datos en memoria. Como ejemplo conectaremos con un servidor MySQL, ejecutaremos una consulta SQL y los registros que se obtienen se almacenan en una lista de tuplas (en memoria). Además, como ejemplo también se instancia una clase a la que se le pasa como parámetro la lista de tuplas y la guarda en un fichero de texto CSV.

Requisitos para trabajar con listas, tuplas y listas de tuplas en C#

El único requisito es disponer del IDE de desarrollo Visual Studio .Net, en nuestro caso usaremos la versión gratuita Visual Studio .Net Commnuty Edition. En el siguiente enlace explicamos cómo descargarlo e instalarlo:

En el caso de querer guardar el contenido de una tabla de una base de datos (o de la ejecución de una consulta SQL), evidentemente necesitaremos tener acceso al servidor de base de datos. En el ejemplo de este tutorial accederemos a un servidor de base de datos MySQL. Para ello deberemos tener la dirección IP del servidor (o el nombre DNS), un usuario y una contraseña.

Para el caso de acceso a MySQL Server desde C#, podremos seguir las instrucciones del siguiente manual:

Desarrollar aplicación C# con acceso nativo a MySQL Server mediante ADO.NET.

Las tuplas (Tuple) en C#

Una tupla es una estructura de datos que tiene un número específico y una secuencia de valores. Admite elementos de diferentes tipos de datos (string, DateTime, int, etc.).

Las tuplas se utilizan normalmente para:

  • Representar un único conjunto de datos. Por ejemplo, una tupla puede representar un registro de base de datos y sus componentes pueden representar campos (columnas) individuales del registro.
  • Facilitar el acceso y la manipulación de un conjunto de datos.
  • Devolver varios valores de un método sin usar out para paso de parámetros por referencia en C# o ByRef para paso de parámetros por referencia en Visual Basic.

En una tupla, por ejemplo, se puede almacenar el resultado de la ejecución de una consulta SQL que devuelve registros de una tabla. Esto nos permitirá utilizar esta tupla con los datos de los registros «en memoria» para otros métodos de nuestra aplicación, entre otras cosas.

Hay varias formas de crear tuplas, algua de ellas:

private void btCrearListaTuplas_Click(object sender, EventArgs e)
{
    //Crear tupla indicando los elementos que contendrá y su tipo de datos
    //Y agregando su valor al crearla
    var tuplaPrueba = new Tuple<string, int, DateTime>("Prueba de tupla con new", 125, DateTime.Now);
    //Acceder a los datos de la tupla y mostrar datos en un TextBox
    txtContenidoListaTuplas.Text = tuplaPrueba.Item1 + " | " + tuplaPrueba.Item3 +
" | " + tuplaPrueba.Item2;
    
    //Agregamos salto de línea en el TextBox
    txtContenidoListaTuplas.Text = txtContenidoListaTuplas.Text + Environment.NewLine;

    //Crear tupla directamente agregando los elementos que contendrá
    var tuplaPrueba2 = Tuple.Create("Prueba de tupla con create", 125, DateTime.Now);
    //Acceder a los datos de la tupla y mostrar datos en un TextBox
    txtContenidoListaTuplas.Text = txtContenidoListaTuplas.Text + tuplaPrueba2.Item1 +
" | " + tuplaPrueba2.Item3 + " | " + tuplaPrueba2.Item2;

    //Agregamos salto de línea en el TextBox
    txtContenidoListaTuplas.Text = txtContenidoListaTuplas.Text + Environment.NewLine;

    //Crear tupla dando nombre a cada campo
    (string MiColumna1, int MiColumna2, DateTime MiColumna3) tuplaPrueba3 =
("Prueba de tupla con nombres", 125, DateTime.Now);
    //Acceder a los datos de la tupla con nombres de campo y mostrar datos en un TextBox
    txtContenidoListaTuplas.Text = txtContenidoListaTuplas.Text + tuplaPrueba3.MiColumna1
+ " | " + tuplaPrueba3.MiColumna3 + " | " + tuplaPrueba3.MiColumna2;
}

El resultado de la ejecución del código anterior:

Las tuplas (Tuple) en C#

La declaración de una tupla admite, como máximo, ocho campos (óctupla).

Los datos contenidos en una tupla son invariables, no se pueden modificar una vez creada y asignados sus valores.

Listas (List) en C#

Representa una lista de objetos fuertemente tipados a la que se puede obtener acceso por índice. Proporciona métodos para buscar, ordenar y manipular listas.

Para el caso de este artículo usaremos una lista de tuplas, de forma que podremos almacenar un número indefinido de tuplas dentro de una lista. Esto nos permitirá, como veremos más adelante, poder almacenar el resultado de una consulta SQL en una lista de tuplas.

Un ejemplo de creación de una lista y manipulación de sus datos (agregar elementos, recorrerlos, etc.):

private void btCrearListaTuplas_Click(object sender, EventArgs e)
{
    //Creamos una lista de string
    List<string> dinosaurios = new List<string>();
    //Agreamos elementos en la lista
    dinosaurios.Add("Tiranosaurio rex");
    dinosaurios.Add("Triceratops");
    dinosaurios.Add("Diplodocus");
    dinosaurios.Add("Velociraptor");    
    //Iteramos (recorremos) la lista y mostramos su contenido en un TextBox
    foreach (string dinosaurio in dinosaurios)
    {
        //Añadimos el elemento actual de la lista a un TextBox
        txtContenidoListaTuplas.Text = txtContenidoListaTuplas.Text + dinosaurio;
        //Añadimos una nueva línea en el TextBox
        txtContenidoListaTuplas.Text = txtContenidoListaTuplas.Text + Environment.NewLine;
    }
}

El resultado de la ejecución del código anterior:

Listas (List) en C#

Lista de tuplas en C#

Tras conocer las tuplas y las listas, ahora usaremos la «potencia» de ambas estructuras de datos para «unirlas», de esta forma podremos tener listas de tuplas, como si de registros de una base de datos se tratase.

A continuación mostramos un ejemplo de declaración de una lista de tuplas con cuatro campos (columnas, elementos), le agregamos valores (registros) y los mostramos por pantalla:

private void btCrearListaTuplas_Click(object sender, EventArgs e)
{
    //Creamos una lista de tuplas con un string, una fecha-hora, y otros dos string
    var listaTuplas = new List <Tuple<string, DateTime, string, string>>();

    //Para probar, rellenamos la lista de tuplas con valores
    listaTuplas.Add(Tuple.Create("Col1 - Fila 1", DateTime.Now, 
        "Col3 - Fila 1", "Col4 - Fila 1"));
    listaTuplas.Add(Tuple.Create("Col1 - Fila 2", DateTime.Now.AddDays(1), 
        "Col3 - Fila 2", "Col4 - Fila 2"));
    listaTuplas.Add(Tuple.Create("Col1 - Fila 3", DateTime.Now.AddDays(2), 
        "Col3 - Fila 3", "Col4 - Fila 3"));
    listaTuplas.Add(Tuple.Create("Col1 - Fila 4", DateTime.Now.AddDays(3), 
        "Col3 - Fila 4", "Col4 - Fila 4"));

    //Recorremos la lista de tuplas y mostramos el contenido de la lista,
    //de cada elemento de la lista y de cada columna de la tupla
    //en un TextBox, separando cada tupla en una línea
    int numTupla = 0;
    foreach (var inci in listaTuplas) {
        numTupla = numTupla + 1;
        txtContenidoListaTuplas.Text = txtContenidoListaTuplas.Text + 
            "Tupla : " + numTupla.ToString() + " -> "  + inci.Item1 +
            " | " + inci.Item3 + " | " + inci.Item4 + " | " + inci.Item2 +
            Environment.NewLine;
    }
}

El resultado de la ejecución del código anterior:

Lista de tuplas en C#

Guardar registros de consulta SQL de tabla de base de datos en memoria en una lista de tuplas y en fichero CSV con C#

Tras conocer las tuplas, las listas y las lista de tuplas, vamos a mostrar un ejemplo de código fuente en C# que accede a una base de datos MySQL Server, ejecuta una consulta SQL y, los registros que obtiene de dicha ejecución, los almacena en una lista de tuplas (en memoria). Además, también almacena la lista de tuplas en un fichero de texto CSV.

A continuación se muestra el código fuente en C# de la clase principal formPrincipal. Dicho formulario tiene los siguientes componentes:

Guardar registros de consulta SQL de tabla de base de datos en memoria en una lista de tuplas y en fichero CSV con C#

El código fuente completo puede descargarse aquí, a continuación se lista el código fuente de la clase formPrincipal, que incluye incluye comentarios en las líneas más importantes, explicando su cometido:

using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Windows.Forms;

namespace TuplasGuardarDatosProyectoA
{
    public partial class formPrincipal : Form
    {
        //Conexión a la base de datos MySQL
        MySqlConnection conexionBD;

        public formPrincipal()
        {
            InitializeComponent();
        }

        private void btCrearListaTuplas_Click(object sender, EventArgs e)
        {
            if (txtFicheroGuardarListaTuplas.Text == "")
                MessageBox.Show("Debe elegir una carpeta y nombre de fichero en el que " +
                    "se guardará la lista de tuplas obtenida de la consulta " +
                    "SQL de la base de datos.",
                    "Elegir fichero...", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            else
            {
                //Datos de conexión al servidor de base de datos MySQL Server
                string servidor = "servidor_mysql";
                int puerto = 3306;
                string bd = "bd_proyectoa";
                string usuario = "root";
                string contrasena = "contraseña";
                string connStr =
                    String.Format("server={0};port={1};user id={2}; password={3}; " +
                    "database={4}; pooling=true;" +
                    "Allow Zero Datetime=False;Convert Zero Datetime=True",
                    servidor, puerto, usuario, contrasena, bd);
                try
                {
                    //Conectamos al servidor de MySQL Server
                    conexionBD = new MySqlConnection(connStr);
                    //Consulta SQL que se ejecutará en el servidor de MySQL
                    string sqlConsulta = "select codigo, nombre, dni, fecha" +
                        " from usuario" +
                        " where upper(nombre) like 'ALO%'";
                    MySqlCommand comandoMySQL = new MySqlCommand();
                    conexionBD.Open();
                    comandoMySQL.Connection = conexionBD;
                    comandoMySQL.CommandType = CommandType.Text;
                    comandoMySQL.CommandText = sqlConsulta;

                    //Creamos una lista de tuplas donde almacenaremos el resultado de la consulta SQL
                    var listaRegistros = new List<Tuple<int, string, string, DateTime>>();

                    //Preparamos el reader con los registros de la consulta SQL
                    MySqlDataReader readerConsultaSQL = comandoMySQL.ExecuteReader();

                    //Recorremos los registros que devuelve la consulta SQL
                    int codigo;
                    string nombre, dni;
                    DateTime fecha;
                    while (readerConsultaSQL.Read())
                    {
                        //Comprobamos los posibles nulos de cada campo de la consulta SQL
                        if (!readerConsultaSQL.IsDBNull(readerConsultaSQL.GetOrdinal("codigo")))
                            codigo = Convert.ToInt32(readerConsultaSQL["codigo"]);
                        else
                            codigo = 0;
                        if (!readerConsultaSQL.IsDBNull(readerConsultaSQL.GetOrdinal("nombre")))
                            nombre = readerConsultaSQL["nombre"].ToString();
                        else
                            nombre = "";
                        if (!readerConsultaSQL.IsDBNull(readerConsultaSQL.GetOrdinal("dni")))
                            dni = readerConsultaSQL["dni"].ToString();
                        else
                            dni = "";
                        if (!readerConsultaSQL.IsDBNull(readerConsultaSQL.GetOrdinal("fecha")))
                            fecha = Convert.ToDateTime(readerConsultaSQL["fecha"]);
                        else
                            fecha = DateTime.Now;

                        //Guardamos cada registro en una tupla de la lista
                        listaRegistros.Add(Tuple.Create(codigo, nombre, dni, fecha));
                    }

                    //Como prueba, recorremos la lista de tuplas y mostramos su contenido en un TextBox
                    int numTupla = 0;
                    string tuplas = "";
                    foreach (var tuplaActual in listaRegistros)
                    {
                        numTupla = numTupla + 1;
                        tuplas = tuplas +
                            "Tupla : " + numTupla.ToString() + " -> " + tuplaActual.Item1 +
                            " | " + tuplaActual.Item2 + " | " + tuplaActual.Item3 + " | " + tuplaActual.Item4 +
                            Environment.NewLine;
                    }
                    txtContenidoListaTuplas.Text = tuplas;

                    //Ejemplo para usar la lista de tuplas generada en otra clase
                    //Que guardará la lista en un fichero de texto
                    ListaTuplasRegistrosMemoria lsTuplasMem = new ListaTuplasRegistrosMemoria(listaRegistros);
                    bool resultado = lsTuplasMem.GuardarListaTuplasFichero(txtFicheroGuardarListaTuplas.Text);
                    if (resultado)
                        MessageBox.Show("Las tuplas se han guardado en el fichero: " +
                            txtFicheroGuardarListaTuplas.Text, "Tuplas guardadas en fichero...",
                            MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                catch (MySqlException ex)
                {
                    MessageBox.Show("Error al conectar al servidor de MySQL: " + ex.Message,
                        "Error al acceder a la BD...", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                finally
                {
                    conexionBD.Close();
                }
            }
        }

        private void btGuardarTuplas_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialogiAbrirFichero = new OpenFileDialog();
            dialogiAbrirFichero.InitialDirectory = Application.StartupPath;
            dialogiAbrirFichero.Filter = "Ficheros CSV (*.csv)|*.csv|Todos los ficheros (*.*)|*.*";
            dialogiAbrirFichero.FilterIndex = 1;
            dialogiAbrirFichero.RestoreDirectory = true;
            dialogiAbrirFichero.CheckFileExists = false;
            if (dialogiAbrirFichero.ShowDialog() == DialogResult.OK)
                txtFicheroGuardarListaTuplas.Text = dialogiAbrirFichero.FileName;
        }
    }
}

Se crea una clase llamada ListaTuplasRegistrosMemoria con el siguiente código C#:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace TuplasGuardarDatosProyectoA
{
    class ListaTuplasRegistrosMemoria
    {
        //Lista de tuplas
        List<Tuple<int, string, string, DateTime>> listaRegistros;

        //Constructor al que se le pasa la lista de tuplas como parámetro
        public ListaTuplasRegistrosMemoria(List<Tuple<int, string, string, DateTime>> listaRegistros)
        {
            this.listaRegistros = listaRegistros;
        }

        //Método para guardar los datos de la lista de tuplas en fichero de texto
        public bool GuardarListaTuplasFichero (string fichero)
        {
            try
            {
                StringBuilder datosTuplas = new StringBuilder();
                //Encabezado del fichero
                datosTuplas.AppendLine("\"Código\",\"Nombre\",\"DNI\",\"Fecha\"");
                //Recorremos cada elemento de la lista de tuplas para formatearlo en el StringBuilder
                foreach (var registroActual in listaRegistros)
                {
                    datosTuplas.AppendLine("\"" + registroActual.Item1 + "\",\"" +
                        registroActual.Item2 + "\",\"" + registroActual.Item3 +
                        "\",\"" + registroActual.Item4 + "\"");
                }
                //Guardamos el StringBuilder en el fichero de texto pasado por parámetro
                System.IO.File.WriteAllText(fichero, datosTuplas.ToString());
                return true;
            }
            catch (Exception err)
            {
                MessageBox.Show("Error al guardar las tuplas en el fichero de texto:  " +
                    fichero + err.Message, "Error al guardar tuplas...", 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
        }
    }
}

La aplicación de ejemplo en ejecución realiza las siguientes tareas:

  • Realizará una conexión con un servidor de base de datos MySQL. Para la prueba de la aplicación los datos de conexión se pasan por código.
  • Se ejecuta una consulta SQL, que también se pasa por código.
  • Los registros que devuelve la ejecución de la consulta SQL se almacenan en una lista de tuplas, en memoria.
  • Se instancia una clase de ListaTuplasRegistrosMemoria como ejemplo de paso de parámetro de la lista de tuplas en memoria.
  • Dicha clase guardará en fichero CSV (con valores separados por comillas dobles y comas) la lista de tuplas.

En la siguiente imagen podemos ver el resultado, pues la aplicación también muestra la lista de tuplas (los registros de la base de datos) por pantalla en un TextBox:

Guardar registros de consulta SQL de tabla de base de datos en memoria en una lista de tuplas y en fichero CSV con C#

Descarga de la aplicación completa de ejemplo en C#

A continuación dejamos el enlace para la descarga de la aplicación completa que accede a un servidor MySQL, guarda el resultado de la consulta SQL en una lista de tuplas y, a su vez, guarda la lista de tuplas en un fichero CSV: