Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Documentación de .NET
Introducción (día 1)
Hola mundo
Tutoriales de introducción
Vídeos de .NET 101
Cómo instalar
Información general
Instalar en Windows
Instalación en macOS
Instalar en Linux
Información general
Ubuntu
Alpine
CentOS
Debian
Fedora
OpenSUSE
Red Hat Enterprise Linux
SLES
Eliminación de entornos de ejecución y SDK obsoletos
Administración de plantillas de .NET
Incidencias de certificación notarial en macOS
Cómo comprobar las versiones de .NET Core
Instalación de una versión localizada de IntelliSense
Información general (semana 1)
Paseo por .NET
Componentes de la arquitectura .NET
Bibliotecas de clases de .NET
Información general sobre .NET Core
Información general de .NET Standard
Glosario de .NET
Tutoriales
Usar Visual Studio
Crear una aplicación de consola
Depuración de una aplicación
Publicar una aplicación
Creación de una biblioteca
Prueba unitaria de una biblioteca
Instalar y usar un paquete
Crear y publicar un paquete
Usar Visual Studio Code
Crear una aplicación de consola
Depuración de una aplicación
Publicar una aplicación
Creación de una biblioteca
Prueba unitaria de una biblioteca
Instalar y usar un paquete
Crear y publicar un paquete
Uso de Visual Studio para Mac
Crear una aplicación de consola
Depuración de una aplicación
Publicar una aplicación
Creación de una biblioteca
Prueba unitaria de una biblioteca
Instalar y usar un paquete
Más tutoriales
Novedades
Evolución de .NET Core a .NET 5
Novedades de .NET Core 3.1
Novedades de .NET Core 3.0
Novedades de .NET Core 2.2
Novedades de .NET Core 2.1
Novedades de .NET Core 2.0
Novedades de .NET Standard
Herramientas y diagnósticos
Información general sobre el SDK de .NET Core
CLI de .NET Core
Información general
Referencia
dotnet
dotnet build
dotnet build-server
dotnet clean
dotnet help
dotnet migrate
dotnet msbuild
dotnet new
dotnet nuget
dotnet nuget delete
dotnet nuget locals
dotnet nuget push
dotnet nuget add source
dotnet nuget disable source
dotnet nuget enable source
dotnet nuget list source
dotnet nuget remove source
dotnet nuget update source
dotnet pack
dotnet publish
dotnet restore
dotnet run
dotnet sln
dotnet store
dotnet test
dotnet tool
dotnet tool install
dotnet tool list
dotnet tool restore
dotnet tool run
dotnet tool uninstall
dotnet tool update
dotnet vstest
Scripts de dotnet-install
Comandos de referencia del proyecto
dotnet add reference
dotnet list reference
dotnet remove reference
Comandos del paquete del proyecto
dotnet add package
dotnet list package
dotnet remove package
Herramientas globales y locales
Administración de herramientas
Herramientas de solución de problemas
Creación de herramientas para la CLI
1 - Creación de una herramienta
2 - Uso de una herramienta global
3- Uso de una herramienta local
Información general de global.json
Telemetría
Acceso elevado
Habilitación de la finalización con tabulación
Integración continua con la CLI
Desarrollo de bibliotecas con la CLI
Creación de plantillas para la CLI
Plantillas personalizadas
1 - Crear una plantilla de elemento
2 - Crear una plantilla de proyecto
3 - Crear un paquete de plantillas
MSBuild y archivos del proyecto
SDK de proyecto
Información general
Referencia
Microsoft.NET.Sdk
Microsoft.NET.Sdk.Web
Microsoft.NET.Sdk.Razor
Versiones de .NET Framework de destino
Adiciones al formato csproj
Administración de dependencias
Herramientas adicionales de .NET Core
Información general
Herramienta de desinstalación de .NET Core
Proveedor de WCF Web Service Reference
dotnet-svcutil
dotnet-svcutil.xmlserializer
Generador de serializador XML
Herramientas de diagnóstico
Información general
Depuradores administrados
Depuración de volcados de memoria de Linux
EventCounters
Registro y seguimiento
Herramientas globales de la CLI de .NET Core
dotnet-counters
dotnet-dump
dotnet-gcdump
dotnet-trace
dotnet-symbol
dotnet-sos
Tutoriales de diagnóstico de .NET Core
Obtención de métricas de rendimiento con EventCounters
Depuración de una fuga de memoria
Depuración del uso elevado de CPU
Depuración de interbloqueo
Análisis de código
Información general
Configuración de reglas
Analizador de API
Analizador de portabilidad
Modelo de ejecución
Common Language Runtime (CLR)
Proceso de ejecución administrada
Ensamblados de .NET
Metadatos y componentes autodescriptivos
Carga de dependencias
Carga de dependencias
Descripción de AssemblyLoadContext
Detalles de la carga de dependencias
Sondeo de dependencia predeterminado
Carga de ensamblados administrados
Carga de ensamblados satélite
Carga de bibliotecas no administradas
Tutoriales
Creación de una aplicación de .NET Core con complementos
Uso y depuración de la descargabilidad de ensamblado en .NET Core
Control de versiones
Información general
Selección de la versión de .NET Core
Configuración del entorno en tiempo de ejecución
Configuración
Configuración de la compilación
Depuración y configuración de perfiles
Nombre de recolector de elementos no utilizados
Configuración de la globalización
Configuración de redes
Configuración de subprocesos
Modelos de implementación
Información general
Implementación de aplicaciones con Visual Studio
Publicación de aplicaciones con la CLI
Creación de un paquete NuGet con la CLI
Puesta al día del runtime de implementación autocontenida
Implementación de archivo único y ejecutable
Recorte de implementaciones autocontenidas y ejecutables
Almacenamiento de paquetes en tiempo de ejecución
Catálogo de identificadores de tiempo de ejecución (RID)
Nombres del manifiesto de recurso
Docker
Introducción a .NET y Docker
Incluir una aplicación de .NET Core en un contenedor
Herramientas de contenedor de Visual Studio
Componentes fundamentales de la codificación
Información general de los tipos base
Common Type System y Common Language Specification
Sistema de tipos comunes
Independencia de lenguaje
Componentes independientes del lenguaje
Conversión de tipos en .NET
Tablas de conversión de tipos
Bibliotecas del marco
Introducción a la biblioteca de clases
Tipos genéricos
Información general
Introducción a los tipos genéricos
Colecciones genéricas en .NET
Delegados genéricos para manipular matrices y listas
Interfaces genéricas
Covarianza y contravarianza
Colecciones y estructuras de datos
Información general
Seleccione una clase de colección
Tipos de colección utilizados normalmente
Cuándo utilizar colecciones genéricas
Comparaciones y ordenaciones en colecciones
Tipos de colecciones ordenadas
Tipos Hashtable y Dictionary
Colecciones seguras para subprocesos
Delegados y expresiones lambda
Events
Información general
Provocación y consumo de eventos
Control de varios eventos mediante propiedades de evento
Modelo de diseño de observador
Información general
Procedimientos recomendados
Procedimiento Implementación de un proveedor
Procedimiento Implementación de un observador
Excepciones
Información general
Clase Exception y propiedades
Temas procedimentales
Uso del bloque Try/Catch para detectar excepciones
Uso de excepciones específicas en un bloque Catch
Inicio de excepciones explícitamente
Creación de excepciones definidas por el usuario
Creación de excepciones definidas por el usuario con mensajes de excepción
localizados
Uso de bloques Finally
Uso de controladores de excepciones filtradas por el usuario
Control de excepciones de interoperabilidad COM
Procedimientos recomendados
Tipos numéricos
Fechas, horas y zonas horarias
Formato de números, fechas y otros tipos
Información general
Cadenas con formato numérico estándar
Cadenas con formato numérico personalizado
Cadenas con formato de fecha y hora estándar
Cadenas con formato de fecha y hora personalizado
Cadenas de formato TimeSpan estándar
Cadenas de formato TimeSpan personalizado
Cadenas de formato de enumeración
Formatos compuestos
Temas procedimentales
Relleno de un número con ceros a la izquierda
Extracción del día de la semana de una fecha
Uso de proveedores de formato numérico personalizado
Aplicación de acciones de ida y vuelta a valores de fecha y hora
Visualización de los milisegundos en valores de fecha y hora
Visualización de las fechas en calendarios no gregorianos
Manipulación de las cadenas
Codificación de caracteres de .NET
Procedimiento para usar las clases de codificación de caracteres
Procedimientos recomendados para el uso de cadenas
Operaciones básicas de cadenas
Información general
Creación de nuevas cadenas
Recorte y eliminación de caracteres
Relleno de cadenas
Comparación de cadenas
Cambio de mayúsculas y minúsculas
Uso de la clase StringBuilder
Procedimiento para realizar manipulaciones básicas de cadena
Expresiones regulares en .NET
Información general
Referencia del lenguaje
Información general
Escapes de carácter
Clase de caracteres
Delimitadores
Construcciones de agrupamiento
Cuantificadores
Construcciones de referencia inversa
Construcciones de alternancia
Sustituciones
Opciones de expresiones regulares
Construcciones misceláneas
Procedimientos recomendados con expresiones regulares
Modelo de objetos de expresiones regulares
Comportamiento de expresiones regulares
Información general
Retroceso
Compilación y reutilización
Seguridad para subprocesos
Ejemplos
Búsqueda de HREF
Cambio de formatos de fecha
Extracción de un protocolo y un número de puerto de una URL
Eliminación de caracteres no válidos de una cadena
comprobar que las cadenas están en un formato de correo electrónico válido
Análisis de las cadenas
Información general
Análisis de cadenas numéricas
Análisis de cadenas de fecha y hora
Análisis de otras cadenas
Atributos
Información general
Aplicación de atributos
Escritura de atributos personalizados
Recuperación de la información almacenada en atributos
Elección entre tipos de tupla y anónimos
Extensiones de Microsoft
Configuración
Inserción de dependencias
Registro
Acceso a datos
LINQ
Documentos y datos XML
Microsoft.Data.Sqlite
Procesamiento paralelo, simultaneidad y asincronía
Información general
Programación asincrónica
Información general
Programación asincrónica en detalle
Patrones para la programación asincrónica
Programación en paralelo
Información general
Biblioteca de procesamiento paralelo basado en tareas (TPL)
Paralelismo de datos
Procedimiento para escribir un bucle Parallel.For simple
Procedimiento para escribir un bucle Parallel.ForEach sencillo
Procedimiento para escribir un bucle Parallel.For con variables locales de
subproceso
Procedimiento para escribir un bucle Parallel.ForEach con variables locales de
partición
Procedimiento para cancelar un bucle Parallel.For o ForEach
Procedimiento para controlar excepciones en bucles paralelos
Procedimiento para acelerar cuerpos de bucle pequeños
Procedimiento Iteración de directorios de archivos con la clase Parallel
Programación asincrónica basada en tareas
Encadenar tareas mediante tareas de continuación
Tareas secundarias asociadas y desasociadas
Cancelación de tareas
Control de excepciones
Procedimiento para usar Parallel.Invoke para ejecutar operaciones en paralelo
Procedimiento para devolver un valor a partir de una tarea
Procedimiento para cancelar una tarea y sus elementos secundarios
Procedimiento Creación de tareas precalculadas
Procedimiento para recorrer un árbol binario con tareas en paralelo
Procedimiento para desencapsular una tarea anidada
Procedimiento Evitar que una tarea secundaria se adjunte a su elemento primario
Flujo de datos
Procedimiento Escritura y lectura de mensajes en un bloque de flujo de datos
Procedimiento Implementación de un modelo de flujo de datos productor-
consumidor
Procedimiento Toma de medidas cuando un bloque de flujos de datos recibe
datos
Tutorial: Creación de una canalización de flujos de datos
Procedimiento Desvinculación de bloques de flujos de datos
Tutorial: Uso de flujos de datos en aplicaciones de Windows Forms
Procedimiento para cancelar un bloque de flujo de datos
Tutorial: Creación de tipos de bloques de flujos de datos personalizados
Procedimiento Uso de JoinBlock para leer datos de diferentes orígenes
Procedimiento Especificación del grado de paralelismo en un bloque de flujos de
datos
Procedimiento para especificar un programador de tareas en un bloque de flujos
de datos
Tutorial: Uso de BatchBlock y BatchedJoinBlock para mejorar la eficacia
Usar TPL con otros patrones asincrónicos
TPL y la programación asincrónica tradicional de .NET Framework
Procedimiento para encapsular patrones de EAP en una tarea
Problemas potenciales en el paralelismo de datos y tareas
Parallel LINQ (PLINQ)
Introducción a PLINQ
Introducción a la velocidad en PLINQ
Conservar el orden en PLINQ
Opciones de fusión mediante combinación en PLINQ
Posibles problemas con PLINQ
Procedimiento para crear y ejecutar una consulta PLINQ simple
Procedimiento para controlar la ordenación en una consulta PLINQ
Procedimiento para combinar consultas LINQ paralelas y secuenciales
Procedimiento para controlar excepciones en una consulta PLINQ
Procedimiento para cancelar una consulta PLINQ
Procedimiento para escribir una función de agregado personalizada de PLINQ
Procedimiento para especificar el modo de ejecución en PLINQ
Procedimiento para especificar opciones de fusión mediante combinación en
PLINQ
Procedimiento para iterar directorios con PLINQ
Procedimiento para medir el rendimiento de consultas PLINQ
Ejemplo de datos de PLINQ
Estructuras de datos para la programación paralela
Herramientas de diagnóstico paralelo
Particionadores personalizados para PLINQ y TPL
Información general
Procedimiento para implementar particiones dinámicas
Procedimiento para implementar un particionador para particionamiento estático
Expresiones lambda en PLINQ y TPL
Información adicional
Subprocesos
Serialización
Información general
Serialización de JSON
Información general
Cómo serializar y deserializar JSON
Procedimientos para escribir convertidores personalizados
Migración desde Newtonsoft.json
Serialización binaria
Información general
Guía de seguridad de BinaryFormatter
Conceptos de serialización
Serialización básica
Serialización selectiva
Serialización personalizada
Pasos del proceso de serialización
Serialización tolerante a versiones
Directrices de serialización
Procedimiento Fragmentación de datos serializados
Procedimiento para determinar si un objeto de .NET Standard es serializable
Ejemplo
Serialización de SOAP y XML
Información general
Serialización XML en profundidad
Ejemplos
Herramienta de definición de esquemas XML
Control de la serialización XML mediante atributos
Atributos que controlan la serialización XML
Serialización XML con servicios web XML
Atributos que controlan la serialización SOAP codificada
Temas procedimentales
Serialización de un objeto
Deserialización de un objeto
Uso de la herramienta de definición de esquemas XML para generar clases y
documentos de esquema XML
Control de la serialización de clases derivadas
Especificación de un nombre de elemento alternativo para una secuencia XML
Calificación del elemento XML y los nombres del atributo XML
Serialización de un objeto como secuencia XML con codificación SOAP
Invalidación de la serialización XML SOAP codificada
Elementos de serialización XML
system.xml.serialization
dateTimeSerialization
schemaImporterExtensions
agregar elemento para schemaImporterExtensions
xmlSerializer
Herramientas
Herramienta Generador de serializador XML (Sgen.exe)
Herramienta Definición de esquemas XML (Xsd.exe)
E/S de archivos y secuencias
Información general
Formatos de ruta de acceso de archivo en los sistemas Windows
Tareas de E/S comunes
Procedimiento para copiar directorios
Procedimiento para enumerar directorios y archivos
Procedimiento para leer y escribir en un archivo de datos recién creado
Procedimiento para abrir y anexar a un archivo de registro
Procedimiento para escribir texto en un archivo
Procedimiento para leer texto de un archivo
Procedimiento para leer caracteres de una cadena
Procedimiento para escribir caracteres en una cadena
Procedimiento para agregar o quitar entradas de la lista de control de acceso
Procedimiento para comprimir y extraer archivos
Crear secuencias
Procedimiento para convertir flujos de .NET Framework en flujos de Windows
Runtime y viceversa
E/S de archivos asincrónica
Control de errores de E/S de disco
Almacenamiento aislado
Tipos de aislamiento
Procedimiento para obtener los almacenes de almacenamiento aislado
Procedimiento para enumerar los almacenes de almacenamiento aislado
Procedimiento para eliminar almacenes de almacenamiento aislado
Procedimiento para prever condiciones de espacio insuficiente con almacenamiento
aislado
Procedimiento para crear archivos y directorios en almacenamiento aislado
Procedimiento para buscar archivos y directorios existentes en almacenamiento
aislado
Procedimiento para leer y escribir en archivos en almacenamiento aislado
Procedimiento para eliminar archivos y directorios en almacenamiento aislado
Canalizaciones
Procedimiento para usar canalizaciones anónimas para la comunicación local entre
procesos
Procedimiento para usar canalizaciones con nombre para la comunicación de red
entre procesos
Canalizaciones
Trabajo con búferes
Archivos asignados a memoria
Pruebas unitarias
Información general
Procedimientos recomendados para pruebas unitarias
xUnit
Pruebas unitarias de C#
Pruebas unitarias de F#
Pruebas unitarias de VB
Organización de un proyecto y prueba con xUnit
NUnit
Pruebas unitarias de C#
Pruebas unitarias de F#
Pruebas unitarias de VB
MSTest
Pruebas unitarias de C#
Pruebas unitarias de F#
Pruebas unitarias de VB
Ejecución de pruebas unitarias selectivas
Ordenación de pruebas unitarias
Cobertura de código de prueba unitaria
Salida publicada de una prueba unitaria
Pruebas unitarias dinámicas de proyectos de .NET Core con Visual Studio
Seguridad
Temas avanzados
Rendimiento
Administración de memoria
¿Qué es el "código administrado"?
Administración de memoria automática
Limpieza de recursos no administrados
Información general
Implementación de un método Dispose
Implementación de un método DisposeAsync
Uso de objetos que implementan IDisposable
Recolección de elementos no utilizados
Información general
Aspectos básicos
Estación de trabajo y GC de servidor
GC en segundo plano
El montón de objetos grandes
Recolección de elementos no utilizados y rendimiento
Recolecciones inducidas
Modos de latencia
Optimización de hospedaje web compartido
Notificaciones de recolección de elementos no utilizados
Supervisión de recursos de dominio de aplicación
Referencias parciales
Tipos relacionados con el intervalo y la memoria
Información general
Instrucciones de uso de Memory<T> y Span<T>
Tipos habilitados para SIMD
Interoperabilidad nativa
Información general
P/Invoke
Serialización de tipos
Personalización de la serialización de estructuras
Personalización de la serialización de parámetros
Guía de interoperabilidad
Juegos de caracteres y serialización
Exposición de los componentes de .NET Core a COM
Hospedaje de .NET Core desde código nativo
Interoperabilidad COM
Información general
Contenedores COM
Información general
Contenedor al que se puede llamar en tiempo de ejecución
Contenedor CCW (COM callable wrapper)
Habilitación de tipos de .NET para la interoperabilidad COM
Aplicación de atributos de interoperabilidad
Excepciones
Empaquetado de distribución de .NET Core
Globalización y localización
Guía de la biblioteca de código abierto
Instrucciones de diseño de los marcos
Información general
Directrices de nomenclatura
Normas referentes al uso de minúsculas y mayúsculas
Convenciones generales de nomenclatura
Nombres de ensamblados y DLL
Nombres de espacios de nombres
Nombres de clases, estructuras e interfaces
Nombres de miembros de tipos
Asignación de nombres a parámetros
Asignación de nombres a recursos
Instrucciones de diseño de tipos
Elección entre clases y estructuras
Diseño de clases abstractas
Diseño de clases estáticas
Diseño de interfaces
Diseño de estructuras
Diseño de enumeraciones
Tipos anidados
Instrucciones de diseño de miembros
Sobrecarga de miembros
Diseño de propiedades
Diseño de constructores
Diseño de eventos
Diseño de campos
Métodos de extensión
Sobrecargas de operadores
Diseño de parámetros
Diseño para la extensibilidad
Clases no selladas
Miembros protegidos
Eventos y devoluciones de llamadas
Miembros virtuales
Abstracciones (tipos e interfaces abstractos)
Clases base para la implementación de abstracciones
Sellar
Instrucciones de diseño de excepciones
Generación de excepciones
Uso de tipos de excepciones estándar
Excepciones y rendimiento
Instrucciones de uso
Matrices
Atributos
Colecciones
Serialización
Uso de System.Xml
Operadores de igualdad
Patrones de diseño comunes
Propiedades de dependencia
Guía de migración
Últimos cambios
Migración
.NET Core 2.0 a 2.1
Migración desde project.json
Asignación entre project.json y csproj
Cambios en la información general de la CLI
Migración desde DNX
Portabilidad de .NET Framework
Información general
Análisis de las dependencias de terceros
Portabilidad de las bibliotecas
Organización de proyectos para .NET Core
Tecnologías no disponibles
Herramientas
Uso del paquete de compatibilidad de Windows
Portabilidad de los proyectos de Windows Forms
Portabilidad de los proyectos de WPF
Portabilidad de los proyectos de C++/CLI
Selección entre .NET Core y .NET Framework para aplicaciones de servidor
Introducción a .NET Core
16/09/2020 • 3 minutes to read • Edit Online
En este artículo se proporciona información sobre cómo comenzar con .NET Core. .NET Core se puede instalar en
Windows, Linux y macOS. Puede programar en su editor de texto preferido y crear aplicaciones y bibliotecas
multiplataforma.
Si no está seguro de qué es .NET Core o cómo se relaciona con otras tecnologías .NET, comience con la información
general ¿Qué es .NET?. En resumen, .NET Core es una implementación multiplataforma de código abierto de .NET.
Hello World!
¡Enhorabuena! Ha creado una sencilla aplicación .NET Core. También puede usar Visual Studio Code, Visual Studio
(solo Windows) o Visual Studio para Mac (solo macOS), para crear una aplicación .NET Core.
Tutoriales
Para comenzar a desarrollar aplicaciones .NET Core, puede seguir estos tutoriales paso a paso:
Windows
Linux
macOS
Creación de su primera aplicación de consola con .NET Core en Visual Studio 2019
Compilación de una biblioteca de clases con .NET Standard en Visual Studio
Tutorial: Depuración de una aplicación de consola de .NET Core con Visual Studio Code
Vea el artículo Dependencias y requisitos de .NET Core para obtener una lista de las versiones de Windows
admitidas.
Primeros pasos
09/04/2020 • 2 minutes to read • Edit Online
Hay varias maneras de empezar a trabajar con .NET. Dado que .NET es una plataforma masiva, existen varios
artículos en esta documentación que pueden ayudar a trabajar con. NET, cada uno desde una perspectiva distinta.
En este artículo obtendrá información sobre cómo instalar .NET Core en Windows. .NET Core está formado por el
entorno de ejecución y el SDK. El tiempo de ejecución se usa para ejecutar una aplicación de .NET Core, y puede o
no incluirse con la aplicación. El SDK se usa para crear aplicaciones y bibliotecas de .NET Core. El entorno de
ejecución de .NET Core siempre se instala con el SDK.
La versión más reciente de .NET Core es la 3.1.
DESCARGAR .NET
CORE
Versiones compatibles
En la tabla siguiente se muestra una lista de versiones de .NET Core actualmente compatibles y las versiones de
Windows en las que se admiten. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core
llega al fin del soporte técnico o la versión de Windows llega al final del ciclo de vida.
Las fechas de fin de servicio de Windows 10 están segmentadas por edición. En la tabla que hay a continuación
solo se tienen en cuenta las ediciones Home , Pro , Pro Education y Pro for Workstations . Para ver detalles
específicos, consulte la hoja informativa sobre el ciclo de vida de Windows.
Con la marca ✔
️ se indica que todavía se admite la versión de Windows o .NET Core.
Con la marca ❌ se indica que la versión de Windows o .NET Core no se admite en esa versión de Windows.
Cuando una versión de Windows y una versión de .NET Core tienen una marca ✔️ , significa que se admite esa
combinación de sistema operativo y .NET.
. N ET 5 ( VERSIÓ N
SIST EM A O P ERAT IVO . N ET C O RE 2. 1 . N ET C O RE 3. 1 P REL IM IN A R)
✔ Windows 10,
️ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
versión 2004
✔ Windows 10,
️ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
versión 1909
✔ Windows 10,
️ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
versión 1903
✔ Windows 10,
️ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
versión 1809
Versiones no admitidas
Las siguientes versiones de .NET Core ya no se admiten ❌. Las descargas de estas siguen estando publicadas:
3.0
2.2
2.0
Dependencias
.NET Core 3.1
.NET Core 3.0
.NET Core 2.2
.NET Core 2.1
Las versiones siguientes de Windows son compatibles con .NET Core 3.1:
NOTE
Un símbolo + representa la versión mínima.
Para obtener más información sobre los sistemas operativos compatibles con .NET Core 3.1, las distribuciones y la
directiva del ciclo de vida, vea Versiones de SO compatibles con .NET Core 3.1.
Windows 7 / Vista / 8.1 / Server 2008 R2 / Server 2012 R2
Se necesitan dependencias adicionales en caso de instalar el SDK o el entorno de ejecución de .NET en las
versiones siguientes de Windows:
❌ Windows 7 SP1
❌ Windows Vista SP2
️ Windows 8.1
✔
️ Windows Server 2008 R2
✔
️ Windows Server 2012 R2
✔
Instale el software siguiente:
Microsoft Visual C++ 2015 Redistributable Update 3.
KB2533623
Los requisitos anteriores también son necesarios si se encuentra con uno de los errores siguientes:
Instale el SDK omitiendo el modificador -Runtime . El modificador -Channel de este ejemplo está establecido en
Current , con lo que se instala la versión admitida más reciente.
Si ya tiene Visual Studio instalado, puede comprobar la versión siguiendo los pasos que se detallan a
continuación.
1. Abra Visual Studio.
2. Seleccione Ayuda > Acerca de Microsoft Visual Studio .
3. Lea el número de versión en el cuadro de diálogo Acerca de .
Visual Studio puede instalar el SDK y el entorno de ejecución de .NET Core más recientes.
DESCARGUE .
V IS U A L S TU D IO
set DOTNET_ROOT=%USERPROFILE%\dotnet
set PATH=%USERPROFILE%\dotnet;%PATH%
set DOTNET_MULTILEVEL_LOOKUP=0
Este enfoque permite instalar varias versiones en ubicaciones independientes y elegir explícitamente qué
ubicación de instalación debe usar una aplicación mediante la ejecución de la aplicación con variables de entorno
que apuntan a esa ubicación.
Cuando DOTNET_MULTILEVEL_LOOKUP se establece en 0 , .NET Core ignora cualquier versión de .NET Core instalada
globalmente. Elimine esa configuración de entorno para que .NET Core tenga en cuenta la ubicación de instalación
global predeterminada al seleccionar el mejor marco para ejecutar la aplicación. La ubicación predeterminada
suele ser C:\Program Files\dotnet , que es la ruta de instalación predeterminada de .NET Core.
Docker
Los contenedores proporcionan una manera ligera de aislar la aplicación del resto del sistema host. Los
contenedores de la misma máquina comparten solo el kernel y usan los recursos proporcionados a la aplicación.
.NET Core puede ejecutarse en un contenedor de Docker. Las imágenes oficiales de Docker en .NET Core se
publican en el registro de contenedor de Microsoft (MCR) y se pueden encontrar en el repositorio de Docker Hub
para Microsoft .NET Core. Cada repositorio contiene imágenes para diferentes combinaciones de .NET (SDK o
Runtime) y del sistema operativo que puede usar.
Microsoft ofrece imágenes que se adaptan a escenarios específicos. Por ejemplo, el repositorio de ASP.NET Core
proporciona imágenes que se compilan para ejecutar aplicaciones de ASP.NET Core en producción.
Para obtener más información sobre el uso de .NET Core en un contenedor de Docker, vea Introducción a .NET y
Docker y Ejemplos.
Pasos siguientes
Cómo comprobar que .NET Core ya está instalado.
Tutorial: Tutorial Hola mundo.
Tutorial: Creación de una aplicación con Visual Studio Code.
Tutorial: Inclusión de una aplicación de .NET Core en un contenedor.
Instalación de .NET Core en macOS
16/09/2020 • 14 minutes to read • Edit Online
En este artículo obtendrá información sobre cómo instalar .NET Core en macOS. .NET Core está formado por el
entorno de ejecución y el SDK. El tiempo de ejecución se usa para ejecutar una aplicación de .NET Core, y puede o
no incluirse con la aplicación. El SDK se usa para crear aplicaciones y bibliotecas de .NET Core. El entorno de
ejecución de .NET Core siempre se instala con el SDK.
La versión más reciente de .NET Core es la 3.1.
DESCARGAR .NET
CORE
Versiones compatibles
En la tabla siguiente se muestra una lista de versiones de .NET Core actualmente compatibles y las versiones de
macOS que se admiten. Estas versiones se siguen admitiendo hasta que la versión de .NET Core alcance la
finalización del soporte técnico.
Con la marca ✔
️ se indica que la versión de .NET Core se sigue admitiendo.
Con la marca ❌ se indica que la versión de .NET Core no se admite.
. N ET 5 ( VERSIÓ N
SIST EM A O P ERAT IVO . N ET C O RE 2. 1 . N ET C O RE 3. 1 P REL IM IN A R)
Versiones no admitidas
Las siguientes versiones de .NET Core ya no se admiten ❌. aunque sus descargas siguen estando publicadas:
3.0 (Notas de la versión)
2.2 (Notas de la versión)
2.0 (Notas de la versión)
Dependencias
.NET Core es compatible con las versiones siguientes de macOS:
NOTE
Un símbolo + representa la versión mínima.
A partir de macOS Catalina (versión 10.15), se debe conceder la certificación a todo el software creado después
del 1 de junio de 2019 que se distribuye con el identificador de desarrollador. Este requisito se aplica al runtime de
.NET Core, al SDK de .NET Core y al software creado con .NET Core.
Desde el 18 de febrero de 2020, se ha concedido la certificación a los instaladores de las versiones 3.1, 3.0 y 2.1 de
.NET Core (tanto el runtime como el SDK). A las versiones publicadas anteriores no se les ha concedido la
certificación. Si ejecuta una aplicación sin certificación, verá un error similar al de la imagen siguiente:
Para obtener más información sobre cómo afecta la certificación forzada a .NET Core (y a las aplicaciones de .NET
Core), vea Trabajo con la certificación de macOS Catalina.
libgdiplus
Las aplicaciones .NET Core que usan el ensamblado System.Drawing.Common requieren la instalación de
libgdiplus.
Una manera fácil de obtener libgdiplus es usar el administrador de paquetes Homebrew ("brew") para macOS.
Después de instalar brew , instale libgdiplus mediante la ejecución de los comandos siguientes en un símbolo del
sistema de Terminal (comando):
brew update
brew install mono-libgdiplus
TIP
Los comandos export anteriores solo hacen que los comandos de la CLI de .NET Core estén disponibles para la sesión de
terminal en la que se ha ejecutado.
Puede editar el perfil del shell para agregar los comandos de forma permanente. Hay una serie de shells distintos disponibles
para Linux, y cada uno de ellos tiene un perfil diferente. Por ejemplo:
Shell de Bash : ~/.bash_profile, ~/.bashrc
Shell de Korn : ~/.kshrc or .profile
Shell de Z : ~/.zshrc or .zprofile
Edite el archivo de origen adecuado para el shell y agregue :$HOME/dotnet al final de la instrucción PATH existente. Si no
se incluye ninguna instrucción PATH , agregue una nueva línea con export PATH=$PATH:$HOME/dotnet .
Además, agregue export DOTNET_ROOT=$HOME/dotnet al final del archivo.
Este enfoque le permite instalar diferentes versiones en ubicaciones independientes y elegir explícitamente cuál
usará cada aplicación.
NOTE
El comando anterior instala el entorno de ejecución de ASP.NET Core para obtener la máxima compatibilidad. El entorno de
ejecución de ASP.NET Core también incluye el estándar de .NET Core.
Docker
Los contenedores proporcionan una manera ligera de aislar la aplicación del resto del sistema host. Los
contenedores de la misma máquina comparten solo el kernel y usan los recursos proporcionados a la aplicación.
.NET Core puede ejecutarse en un contenedor de Docker. Las imágenes oficiales de Docker en .NET Core se
publican en el registro de contenedor de Microsoft (MCR) y se pueden encontrar en el repositorio de Docker Hub
para Microsoft .NET Core. Cada repositorio contiene imágenes para diferentes combinaciones de .NET (SDK o
Runtime) y del sistema operativo que puede usar.
Microsoft ofrece imágenes que se adaptan a escenarios específicos. Por ejemplo, el repositorio de ASP.NET Core
proporciona imágenes que se compilan para ejecutar aplicaciones de ASP.NET Core en producción.
Para obtener más información sobre el uso de .NET Core en un contenedor de Docker, vea Introducción a .NET y
Docker y Ejemplos.
Pasos siguientes
Cómo comprobar que .NET Core ya está instalado.
Trabajo con la certificación de macOS Catalina.
Tutorial: Introducción a macOS.
Tutorial: Creación de una aplicación con Visual Studio Code.
Tutorial: Inclusión de una aplicación de .NET Core en un contenedor.
Instalación de .NET Core en Linux
16/09/2020 • 14 minutes to read • Edit Online
.NET Core está disponible en diferentes distribuciones de Linux. La mayoría de las plataformas y distribuciones
de Linux tienen una versión principal cada año, y la mayoría proporcionan un administrador de paquetes que
se usa para instalar .NET Core. En este artículo se describe lo que se admite actualmente y el administrador de
paquetes que se usa.
El resto de este artículo es un desglose de cada una de las principales distribuciones de Linux que admite .NET
Core. Todas estas versiones siguen siendo compatibles hasta que la versión de .NET Core llega al fin del
soporte técnico o la distribución de Linux llega al final del ciclo de vida.
Para conseguir la mejor compatibilidad, elija una versión de lanzamiento a largo plazo (LTS).
Versiones no admitidas
Las siguientes versiones de .NET Core ya no se admiten ❌. Las descargas de estas siguen estando publicadas:
3.0
2.2
2.0
Estas versiones no admitidas no se detallan en las secciones siguientes y los resultados pueden variar si intenta
instalarlas.
Alpine
No hay instaladores para Alpine. Debe usar el script de instalación o seguir las instrucciones de instalación
manual.
En la tabla siguiente se muestra una lista de versiones de .NET Core actualmente compatibles y las versiones de
Alpine en las que se admiten. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core
llega al fin del soporte técnico o la versión de Alpine llega al final del ciclo de vida.
Una ✔️ indica que todavía se admite la versión de Alpine o de .NET Core.
Una ❌ indica que la versión de Alpine o de .NET Core no se admite en esa versión de Alpine.
Cuando una versión de Alpine y una versión de .NET Core tienen una ✔️ , se admite esa combinación de
sistema operativo y .NET.
. N ET 5 ( VERSIÓ N
A L P IN E . N ET C O RE 2. 1 . N ET C O RE 3. 1 P REL IM IN A R)
️ 3.12
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 3.11
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 3.10
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 3.9
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
❌ 3.8 ️ 2.1
✔ ❌ 3.1 ❌ 5.0 (versión preliminar)
Para más información, consulte Instalación de .NET Core en Alpine.
CentOS
CentOS 7 usa Yum como administrador de paquetes y CentOS 8 emplea DNF.
En la tabla siguiente se muestra una lista de las versiones de .NET Core admitidas actualmente en CentOS 7 y
CentOS 8. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core llegue al final del
soporte técnico o ya no se admita la versión de CentOS.
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO
C EN TO S . N ET C O RE 2. 1 . N ET C O RE 3. 1 IN STA L A C IÓ N M A N UA L )
️ 8
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 7
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
Debian
Debian usa APT (herramienta avanzada de paquetes) como administrador de paquetes.
En la tabla siguiente se muestra una lista de versiones de .NET Core actualmente compatibles y las versiones de
Debian en las que se admiten. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core
llegue al final del soporte técnico o la versión de Debian llegue al final del ciclo de vida.
Una ✔️ indica que todavía se admite la versión de Debian o de .NET Core.
Una ❌ indica que la versión de Debian o de .NET Core no se admite en esa versión de Debian.
Cuando una versión de Debian y una versión de .NET Core tienen una ✔️ , se admite esa combinación de
sistema operativo y .NET.
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO
DEB IA N . N ET C O RE 2. 1 . N ET C O RE 3. 1 IN STA L A C IÓ N M A N UA L )
️ 10
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 9
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
❌8 ️ 2.1
✔ ❌ 3.1 ❌ 5.0 (versión preliminar)
Fedora
Fedora usa DNF como administrador de paquetes.
En la tabla siguiente se muestra una lista de versiones de .NET Core actualmente compatibles y las versiones de
Fedora en las que se admiten. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core
llegue al final del soporte técnico o la versión de Fedora llegue al final del ciclo de vida.
Una ✔
️ indica que todavía se admite la versión de Fedora o de .NET Core.
Una ❌ indica que la versión de Fedora o de .NET Core no se admite en esa versión de Fedora.
Cuando una versión de Fedora y una versión de .NET Core tienen una ✔
️ , se admite esa combinación de
sistema operativo y .NET.
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO
F EDO RA . N ET C O RE 2. 1 . N ET C O RE 3. 1 IN STA L A C IÓ N M A N UA L )
️ 32
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 31
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
❌ 30 ️ 2.1
✔ ️ 3.1
✔ ❌ 5.0 (versión preliminar)
❌ 29 ️ 2.1
✔ ️ 3.1
✔ ❌ 5.0 (versión preliminar)
❌ 28 ️ 2.1
✔ ❌ 3.1 ❌ 5.0 (versión preliminar)
❌ 27 ️ 2.1
✔ ❌ 3.1 ❌ 5.0 (versión preliminar)
openSUSE
openSUSE usa zypper como administrador de paquetes.
En la tabla siguiente se muestra una lista de las versiones de .NET Core compatibles actualmente con
openSUSE 15. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core llegue al final del
soporte técnico o ya no se admita la versión de openSUSE.
Una ✔️ indica que todavía se admite la versión de openSUSE o de .NET Core.
Una ❌ indica que la versión de openSUSE o de .NET Core no se admite en esa versión de openSUSE.
Cuando una versión de openSUSE y una versión de .NET Core tienen una ✔️ , se admite esa combinación de
sistema operativo y .NET.
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO
O P EN SUSE . N ET C O RE 2. 1 . N ET C O RE 3. 1 IN STA L A C IÓ N M A N UA L )
️ 15
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
Red Hat
Red Hat Enterprise Linux (RHEL) usa yum (RHEL 7) y DNF (RHEL 8) como administrador de paquetes.
En la tabla siguiente se muestra una lista de las versiones de .NET Core compatibles actualmente con RHEL 7 y
RHEL 8. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core llegue al final del soporte
técnico o ya no se admita la versión de RHEL.
Una ✔️ indica que todavía se admite la versión de RHEL o de .NET Core.
Una ❌ indica que la versión de RHEL o de .NET Core no se admite en esa versión de RHEL.
Cuando una versión de RHEL y una versión de .NET Core tienen una ✔️ , se admite esa combinación de
sistema operativo y .NET.
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO
RH EL . N ET C O RE 2. 1 . N ET C O RE 3. 1 IN STA L A C IÓ N M A N UA L )
️ 8
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 7
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
SLES
SLES usa zypper como administrador de paquetes.
En la tabla siguiente se muestra una lista de las versiones de .NET Core compatibles actualmente en SLES 12 SP
2 y SLES 15. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core llegue al final del
soporte técnico o ya no se admita la versión de SLES.
Una ✔️ indica que todavía se admite la versión de SLES o de .NET Core.
Una ❌ indica que la versión de SLES o de .NET Core no se admite en esa versión de SLES.
Cuando una versión de SLES y una versión de .NET Core tienen una ✔️ , se admite esa combinación de
sistema operativo y .NET.
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO
SL ES . N ET C O RE 2. 1 . N ET C O RE 3. 1 IN STA L A C IÓ N M A N UA L )
️ 15
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 12 SP2
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
Ubuntu
Ubuntu usa APT (herramienta avanzada de paquetes) como administrador de paquetes.
En la tabla siguiente se representa el estado de compatibilidad de Ubuntu y .NET Core.
Una ✔️ indica que todavía se admite la versión de Ubuntu o de .NET Core.
Una ❌ indica que la versión de Ubuntu o de .NET Core no se admite en esa versión de Ubuntu.
Cuando una versión de Ubuntu y una versión de .NET Core tienen una ✔️ , se admite esa combinación de
sistema operativo y .NET.
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO
UB UN T U . N ET C O RE 2. 1 . N ET C O RE 3. 1 IN STA L A C IÓ N M A N UA L )
️ 20.04 (LTS)
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
❌ 19.10 ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
❌ 19.04 ️ 2.1
✔ ️ 3.1
✔ ❌ 5.0 (versión preliminar)
❌ 18.10 ️ 2.1
✔ ❌ 3.1 ❌ 5.0 (versión preliminar)
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO
UB UN T U . N ET C O RE 2. 1 . N ET C O RE 3. 1 IN STA L A C IÓ N M A N UA L )
️ 18.04 (LTS)
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
❌ 17.10 ️ 2.1
✔ ❌ 3.1 ❌ 5.0 (versión preliminar)
❌ 17.04 ️ 2.1
✔ ❌ 3.1 ❌ 5.0 (versión preliminar)
️ 16.04 (LTS)
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
Pasos siguientes
Cómo comprobar que .NET Core ya está instalado.
Tutorial: Creación de una aplicación con Visual Studio Code.
Tutorial: Inclusión de una aplicación de .NET Core en un contenedor.
Instalación del SDK de .NET Core o de .NET Core
Runtime en Ubuntu
16/09/2020 • 42 minutes to read • Edit Online
.NET Core es compatible con Ubuntu. En este artículo se describe cómo instalar .NET Core en Ubuntu. Cuando una
versión de Ubuntu no es compatible, .NET Core deja de ser compatible con esa versión. Sin embargo, estas
instrucciones pueden ayudarle a conseguir que .NET Core se ejecute en esas versiones, aunque no se admita.
Instale el SDK (que incluye el entorno de ejecución) si quiere desarrollar aplicaciones .NET. O bien, si solo necesita
ejecutar aplicaciones, instale el entorno de ejecución. Si va a instalar el entorno de ejecución, le recomendamos que
instale el entorno de ejecución de ASP.NET Core , ya que incluye los de .NET Core y ASP.NET Core.
Si ya ha instalado el SDK o el entorno de ejecución, use los comandos dotnet --list-sdks y
dotnet --list-runtimes para ver qué versiones están instaladas. Para más información, consulte Cómo comprobar
que .NET Core ya está instalado.
Las instalaciones del administrador de paquetes solo se admiten en la arquitectura x64 . Otras arquitecturas, como
ARM , deben instalar manualmente el SDK de .NET Core o .NET Core Runtime. Para más información, consulte la
sección Instalación manual a continuación.
Distribuciones admitidas
En la tabla siguiente se muestra una lista de versiones de .NET Core actualmente compatibles y las versiones de
Ubuntu en las que se admiten. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core llegue
al fin del soporte técnico o la versión de Ubuntu llegue al final del ciclo de vida.
Una ✔️ indica que todavía se admite la versión de Ubuntu o de .NET Core.
Una ❌ indica que la versión de Ubuntu o de .NET Core no se admite en esa versión de Ubuntu.
Cuando una versión de Ubuntu y una versión de .NET Core tienen una ✔️ , se admite esa combinación de
sistema operativo y .NET.
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO IN STA L A C IÓ N
UB UN T U . N ET C O RE 2. 1 . N ET C O RE 3. 1 M A N UA L )
️ 20.04 (LTS)
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
❌ 19.10 ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
❌ 19.04 ️ 2.1
✔ ️ 3.1
✔ ❌ 5.0 (versión preliminar)
❌ 18.10 ️ 2.1
✔ ❌ 3.1 ❌ 5.0 (versión preliminar)
️ 18.04 (LTS)
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
❌ 17.10 ️ 2.1
✔ ❌ 3.1 ❌ 5.0 (versión preliminar)
❌ 17.04 ️ 2.1
✔ ❌ 3.1 ❌ 5.0 (versión preliminar)
️ 16.04 (LTS)
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
Las siguientes versiones de .NET Core ya no se admiten. aunque sus descargas siguen estando publicadas:
3.0
2.2
2.0
20.04 ✔
️
La instalación con APT puede realizarse con unos pocos comandos. Antes de instalar .NET, ejecute los siguientes
comandos para agregar la clave de la firma del paquete de Microsoft a la lista de claves de confianza y agregar el
repositorio de paquetes.
Abra un terminal y ejecute los comandos siguientes:
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete dotnet-sdk-3.1 , consulte la sección Solución
de problemas de APT.
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete aspnetcore-runtime-3.1 , consulte la
sección Solución de problemas de APT.
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-3.1 en los comandos anteriores por
dotnet-runtime-3.1 .
19.10 ❌
❌ Tenga en cuenta que ya no se admite esta versión de Ubuntu.
La instalación con APT puede realizarse con unos pocos comandos. Antes de instalar .NET, ejecute los siguientes
comandos para agregar la clave de la firma del paquete de Microsoft a la lista de claves de confianza y agregar el
repositorio de paquetes.
Abra un terminal y ejecute los comandos siguientes:
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete dotnet-sdk-3.1 , consulte la sección Solución
de problemas de APT.
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete aspnetcore-runtime-3.1 , consulte la
sección Solución de problemas de APT.
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-3.1 en los comandos anteriores por
dotnet-runtime-3.1 .
19.04 ❌
❌ Tenga en cuenta que ya no se admite esta versión de Ubuntu.
La instalación con APT puede realizarse con unos pocos comandos. Antes de instalar .NET, ejecute los siguientes
comandos para agregar la clave de la firma del paquete de Microsoft a la lista de claves de confianza y agregar el
repositorio de paquetes.
Abra un terminal y ejecute los comandos siguientes:
wget https://packages.microsoft.com/config/ubuntu/19.04/packages-microsoft-prod.deb -O packages-microsoft-
prod.deb
sudo dpkg -i packages-microsoft-prod.deb
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete dotnet-sdk-3.1 , consulte la sección Solución
de problemas de APT.
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete aspnetcore-runtime-3.1 , consulte la
sección Solución de problemas de APT.
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-3.1 en los comandos anteriores por
dotnet-runtime-3.1 .
18.10 ❌
❌ Tenga en cuenta que ya no se admite esta versión de Ubuntu.
La instalación con APT puede realizarse con unos pocos comandos. Antes de instalar .NET, ejecute los siguientes
comandos para agregar la clave de la firma del paquete de Microsoft a la lista de claves de confianza y agregar el
repositorio de paquetes.
Abra un terminal y ejecute los comandos siguientes:
wget https://packages.microsoft.com/config/ubuntu/18.10/packages-microsoft-prod.deb -O packages-microsoft-
prod.deb
sudo dpkg -i packages-microsoft-prod.deb
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete dotnet-sdk-2.1 , consulte la sección Solución
de problemas de APT.
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete aspnetcore-runtime-2.1 , consulte la
sección Solución de problemas de APT.
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-2.1 en los comandos anteriores por
dotnet-runtime-2.1 .
18.04 ✔
️
La instalación con APT puede realizarse con unos pocos comandos. Antes de instalar .NET, ejecute los siguientes
comandos para agregar la clave de la firma del paquete de Microsoft a la lista de claves de confianza y agregar el
repositorio de paquetes.
Abra un terminal y ejecute los comandos siguientes:
wget https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-
prod.deb
sudo dpkg -i packages-microsoft-prod.deb
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete dotnet-sdk-3.1 , consulte la sección Solución
de problemas de APT.
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete aspnetcore-runtime-3.1 , consulte la
sección Solución de problemas de APT.
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-3.1 en los comandos anteriores por
dotnet-runtime-3.1 .
17.10 ❌
❌ Tenga en cuenta que ya no se admite esta versión de Ubuntu.
La instalación con APT puede realizarse con unos pocos comandos. Antes de instalar .NET, ejecute los siguientes
comandos para agregar la clave de la firma del paquete de Microsoft a la lista de claves de confianza y agregar el
repositorio de paquetes.
Abra un terminal y ejecute los comandos siguientes:
wget https://packages.microsoft.com/config/ubuntu/17.10/packages-microsoft-prod.deb -O packages-microsoft-
prod.deb
sudo dpkg -i packages-microsoft-prod.deb
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete dotnet-sdk-2.1 , consulte la sección Solución
de problemas de APT.
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete aspnetcore-runtime-2.1 , consulte la
sección Solución de problemas de APT.
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-2.1 en los comandos anteriores por
dotnet-runtime-2.1 .
17.04 ❌
❌ Tenga en cuenta que ya no se admite esta versión de Ubuntu.
La instalación con APT puede realizarse con unos pocos comandos. Antes de instalar .NET, ejecute los siguientes
comandos para agregar la clave de la firma del paquete de Microsoft a la lista de claves de confianza y agregar el
repositorio de paquetes.
Abra un terminal y ejecute los comandos siguientes:
wget https://packages.microsoft.com/config/ubuntu/17.04/packages-microsoft-prod.deb -O packages-microsoft-
prod.deb
sudo dpkg -i packages-microsoft-prod.deb
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete dotnet-sdk-2.1 , consulte la sección Solución
de problemas de APT.
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete aspnetcore-runtime-2.1 , consulte la
sección Solución de problemas de APT.
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-2.1 en los comandos anteriores por
dotnet-runtime-2.1 .
16.10 ❌
❌ Tenga en cuenta que ya no se admite esta versión de Ubuntu.
La instalación con APT puede realizarse con unos pocos comandos. Antes de instalar .NET, ejecute los siguientes
comandos para agregar la clave de la firma del paquete de Microsoft a la lista de claves de confianza y agregar el
repositorio de paquetes.
Abra un terminal y ejecute los comandos siguientes:
wget https://packages.microsoft.com/config/ubuntu/16.10/packages-microsoft-prod.deb -O packages-microsoft-
prod.deb
sudo dpkg -i packages-microsoft-prod.deb
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete dotnet-sdk-2.1 , consulte la sección Solución
de problemas de APT.
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete aspnetcore-runtime-2.1 , consulte la
sección Solución de problemas de APT.
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-2.1 en los comandos anteriores por
dotnet-runtime-2.1 .
16.04 ✔
️
La instalación con APT puede realizarse con unos pocos comandos. Antes de instalar .NET, ejecute los siguientes
comandos para agregar la clave de la firma del paquete de Microsoft a la lista de claves de confianza y agregar el
repositorio de paquetes.
Abra un terminal y ejecute los comandos siguientes:
wget https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb -O packages-microsoft-
prod.deb
sudo dpkg -i packages-microsoft-prod.deb
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete dotnet-sdk-3.1 , consulte la sección Solución
de problemas de APT.
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete aspnetcore-runtime-3.1 , consulte la
sección Solución de problemas de APT.
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-3.1 en los comandos anteriores por
dotnet-runtime-3.1 .
{os-version}
Representa la versión de Linux en la que está. Se usa en el comando wget siguiente.
Primero, pruebe a purgar la lista de paquetes:
Después, intente instalar .NET Core de nuevo. Si eso no funciona, puede ejecutar una instalación manual con los
comandos siguientes:
Snap
.NET Core está disponible desde el almacén de snaps.
Un snap es una agrupación de una aplicación y sus dependencias que funcionan sin modificaciones en muchas
distribuciones de Linux diferentes. Los snaps se reconocen y se instalan desde el almacén de snaps. Para más
información sobre Snap, consulte Introducción a Snap.
Solo las versiones admitidas de .NET Core están disponibles mediante Snap.
Instalación del SDK
Los paquetes Snap para el SDK de .NET Core se publican con el mismo identificador: dotnet-sdk . Se puede instalar
una versión específica del SDK mediante la especificación del canal. El SDK incluye el entorno de ejecución
correspondiente. En la tabla siguiente se enumeran los canales:
VERSIÓ N DE . N ET C O RE PA Q UET E SN A P
Use el comando snap install para instalar un paquete Snap del SDK de .NET Core. Use el parámetro --channel
para indicar qué versión se va a instalar. Si se omite este parámetro, se usa latest/stable . En este ejemplo, se
especifica 3.1 :
A continuación, registre el comando dotnet del sistema con el comando snap alias :
Este comando tiene el formato sudo snap alias {package}.{command} {alias} . Puede elegir cualquier nombre de
{alias} que prefiera. Por ejemplo, puede asignar un nombre al comando después de la versión específica
instalada por el snap sudo snap alias dotnet-sdk.dotnet dotnet31 . Cuando use el comando dotnet31 , invocará
esta versión específica de .NET. Sin embargo, esta operación no es compatible con la mayoría de los tutoriales y
ejemplos, donde se espera que esté disponible un comando dotnet .
Instalación de la instancia en tiempo de ejecución
Los paquetes Snap de .NET Core Runtime se publican con su propio identificador de paquete. En la tabla siguiente
se muestra una lista de los identificadores de paquete:
VERSIÓ N DE . N ET C O RE PA Q UET E SN A P
3.0 dotnet-runtime-30
2.2 dotnet-runtime-22
Use el comando snap install para instalar un paquete Snap de .NET Core Runtime. En este ejemplo, se instala
.NET Core 3.1:
A continuación, registre el comando dotnet del sistema con el comando snap alias :
export SSL_CERT_FILE=[path-to-certificate-file]
export SSL_CERT_DIR=/dev/null
La ubicación del certificado variará en función de la distribución. Estas son las ubicaciones de las distribuciones en
las que hemos experimentado el problema.
Fedora: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
OpenSUSE: /etc/ssl/ca-bundle.pem
Solus: /etc/ssl/certs/ca-certificates.crt
Dependencias
Al realizar la instalación con un administrador de paquetes, estas bibliotecas se instalan automáticamente. Sin
embargo, si instala manualmente .NET Core o publica una aplicación independiente, deberá asegurarse de que
estas bibliotecas estén instaladas:
libc6
libgcc1
libgssapi-krb5-2
libicu52 (para 14.x)
libicu55 (para 16.x)
libicu60 (para 18.x)
libicu66 (para 20.x)
libssl1.0.0 (para 14.x, 16.x)
libssl1.1 (para 18.x, 20.x)
libstdc++6
zlib1g
En el caso de las aplicaciones de .NET Core que utilizan el ensamblado System.Drawing.Common, también se
necesita la dependencia siguiente:
libgdiplus (versión 6.0.1 o posteriores)
WARNING
Puede instalar una versión reciente de libgdiplus agregando el repositorio Mono al sistema. Para obtener más
información, vea https://www.mono-project.com/download/stable/.
./dotnet-install.sh -c Current
Para instalar .NET Core Runtime en lugar del SDK, use el parámetro --runtime .
Para instalar una versión específica, modifique el parámetro -c para indicar la versión específica. El siguiente
comando instala el SDK de .NET Core 3.1.
./dotnet-install.sh -c 3.1
Instalación manual
Como alternativa a los administradores de paquetes, puede descargar e instalar manualmente el SDK y el entorno
de ejecución. La instalación manual se suele llevar a cabo durante las pruebas de integración continua o en
distribuciones de Linux no admitidas. Para un desarrollador o usuario, generalmente es mejor usar un
administrador de paquetes.
Si instala el SDK de .NET Core, no necesita instalar el entorno de ejecución correspondiente. En primer lugar,
descargue una versión binaria del SDK o del entorno de ejecución de uno de los siguientes sitios:
✔ Descargas de la versión preliminar de .NET 5.0
️
️ Descargas de .NET Core 3.1
✔
️ Descargas de .NET Core 2.1
✔
Todas las descargas de .NET Core
A continuación, extraiga el archivo descargado y use el comando export para establecer las variables que se
utilizan en .NET Core. Luego, asegúrese de que .NET Core esté en PATH.
Para extraer el entorno de ejecución y hacer que los comandos de la CLI de .NET Core estén disponibles en el
terminal, en primer lugar, descargue una versión binaria de .NET Core. Luego, abra un terminal y ejecute los
siguientes comandos desde el directorio donde se guardó el archivo. El nombre del archivo puede ser distinto en
función de lo que haya descargado.
Use el comando siguiente para extraer el entorno de ejecución :
mkdir -p "$HOME/dotnet" && tar zxf aspnetcore-runtime-3.1.0-linux-x64.tar.gz -C "$HOME/dotnet"
export DOTNET_ROOT=$HOME/dotnet
export PATH=$PATH:$HOME/dotnet
TIP
Los comandos export anteriores solo hacen que los comandos de la CLI de .NET Core estén disponibles para la sesión de
terminal en la que se ha ejecutado.
Puede editar el perfil del shell para agregar los comandos de forma permanente. Hay una serie de shells distintos disponibles
para Linux, y cada uno de ellos tiene un perfil diferente. Por ejemplo:
Shell de Bash : ~/.bash_profile, ~/.bashrc
Shell de Korn : ~/.kshrc or .profile
Shell de Z : ~/.zshrc or .zprofile
Edite el archivo de origen adecuado para el shell y agregue :$HOME/dotnet al final de la instrucción PATH existente. Si no
se incluye ninguna instrucción PATH , agregue una nueva línea con export PATH=$PATH:$HOME/dotnet .
Además, agregue export DOTNET_ROOT=$HOME/dotnet al final del archivo.
Este enfoque le permite instalar diferentes versiones en ubicaciones independientes y elegir explícitamente cuál
usará cada aplicación.
Pasos siguientes
Tutorial: Creación de una aplicación de consola con el SDK de .NET Core mediante Visual Studio Code
Instalación del SDK de .NET Core o .NET Core
Runtime en Alpine
16/09/2020 • 8 minutes to read • Edit Online
En este artículo se explica cómo instalar .NET Core en Alpine. Cuando una versión de Alpine no es compatible,
.NET Core deja de ser compatible con esa versión. Pero estas instrucciones pueden ayudarle a conseguir que
.NET Core se ejecute en esas versiones, aunque no se admita.
Instale el SDK (que incluye el entorno de ejecución) si quiere desarrollar aplicaciones .NET. O bien, si solo
necesita ejecutar aplicaciones, instale el entorno de ejecución. Si va a instalar el entorno de ejecución, le
recomendamos que instale el entorno de ejecución de ASP.NET Core , ya que incluye los de .NET Core y
ASP.NET Core.
Si ya ha instalado el SDK o el entorno de ejecución, use los comandos dotnet --list-sdks y
dotnet --list-runtimes para ver qué versiones están instaladas. Para más información, consulte Cómo
comprobar que .NET Core ya está instalado.
No hay instaladores para Alpine. Debe usar el script de instalación o seguir las instrucciones de instalación
manual.
Distribuciones admitidas
En la tabla siguiente se muestra una lista de versiones de .NET Core actualmente compatibles y las versiones de
Alpine en las que se admiten. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core llega
al fin del soporte técnico o la versión de Alpine llega al final del ciclo de vida.
Una ✔️ indica que todavía se admite la versión de Alpine o de .NET Core.
Una ❌ indica que la versión de Alpine o de .NET Core no se admite en esa versión de Alpine.
Cuando una versión de Alpine y una versión de .NET Core tienen una ✔️ , se admite esa combinación de
sistema operativo y .NET.
. N ET 5 ( VERSIÓ N
A L P IN E . N ET C O RE 2. 1 . N ET C O RE 3. 1 P REL IM IN A R)
️ 3.12
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 3.11
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 3.10
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 3.9
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
❌ 3.8 ️ 2.1
✔ ❌ 3.1 ❌ 5.0 (versión preliminar)
Las siguientes versiones de .NET Core ya no se admiten. aunque sus descargas siguen estando publicadas:
3.0
2.2
2.0
Dependencias
.NET Core en Alpine Linux exige que estén instaladas las siguientes dependencias:
icu-libs
krb5-libs
libgcc
libintl
libssl1.1 (Alpine 3.9 o superior)
libssl1.0 (Alpine 3.8 o inferior)
libstdc++
zlib
./dotnet-install.sh -c Current
Para instalar .NET Core Runtime en lugar del SDK, use el parámetro --runtime .
Para instalar una versión específica, modifique el parámetro -c para indicar la versión específica. El siguiente
comando instala el SDK de .NET Core 3.1.
./dotnet-install.sh -c 3.1
Instalación manual
Como alternativa a los administradores de paquetes, puede descargar e instalar manualmente el SDK y el
entorno de ejecución. La instalación manual se suele llevar a cabo durante las pruebas de integración continua o
en distribuciones de Linux no admitidas. Para un desarrollador o usuario, generalmente es mejor usar un
administrador de paquetes.
Si instala el SDK de .NET Core, no necesita instalar el entorno de ejecución correspondiente. En primer lugar,
descargue una versión binaria del SDK o del entorno de ejecución de uno de los siguientes sitios:
✔ Descargas de la versión preliminar de .NET 5.0
️
️ Descargas de .NET Core 3.1
✔
️ Descargas de .NET Core 2.1
✔
Todas las descargas de .NET Core
A continuación, extraiga el archivo descargado y use el comando export para establecer las variables que se
utilizan en .NET Core. Luego, asegúrese de que .NET Core esté en PATH.
Para extraer el entorno de ejecución y hacer que los comandos de la CLI de .NET Core estén disponibles en el
terminal, en primer lugar, descargue una versión binaria de .NET Core. Luego, abra un terminal y ejecute los
siguientes comandos desde el directorio donde se guardó el archivo. El nombre del archivo puede ser distinto
en función de lo que haya descargado.
Use el comando siguiente para extraer el entorno de ejecución :
TIP
Los comandos export anteriores solo hacen que los comandos de la CLI de .NET Core estén disponibles para la sesión
de terminal en la que se ha ejecutado.
Puede editar el perfil del shell para agregar los comandos de forma permanente. Hay una serie de shells distintos
disponibles para Linux, y cada uno de ellos tiene un perfil diferente. Por ejemplo:
Shell de Bash : ~/.bash_profile, ~/.bashrc
Shell de Korn : ~/.kshrc or .profile
Shell de Z : ~/.zshrc or .zprofile
Edite el archivo de origen adecuado para el shell y agregue :$HOME/dotnet al final de la instrucción PATH existente. Si
no se incluye ninguna instrucción PATH , agregue una nueva línea con export PATH=$PATH:$HOME/dotnet .
Además, agregue export DOTNET_ROOT=$HOME/dotnet al final del archivo.
Este enfoque le permite instalar diferentes versiones en ubicaciones independientes y elegir explícitamente cuál
usará cada aplicación.
Pasos siguientes
Tutorial: Creación de una aplicación de consola con el SDK de .NET Core mediante Visual Studio Code
Instalación del SDK de .NET Core o .NET Core
Runtime en CentOS
16/09/2020 • 20 minutes to read • Edit Online
.NET Core es compatible con CentOS. En este artículo se describe cómo instalar .NET Core en CentOS.
Instale el SDK (que incluye el entorno de ejecución) si quiere desarrollar aplicaciones .NET. O bien, si solo necesita
ejecutar aplicaciones, instale el entorno de ejecución. Si va a instalar el entorno de ejecución, le recomendamos que
instale el entorno de ejecución de ASP.NET Core , ya que incluye los de .NET Core y ASP.NET Core.
Si ya ha instalado el SDK o el entorno de ejecución, use los comandos dotnet --list-sdks y
dotnet --list-runtimes para ver qué versiones están instaladas. Para más información, consulte Cómo comprobar
que .NET Core ya está instalado.
Las instalaciones del administrador de paquetes solo se admiten en la arquitectura x64 . Otras arquitecturas, como
ARM , deben instalar manualmente el SDK de .NET Core o .NET Core Runtime. Para más información, consulte la
sección Instalación manual a continuación.
Distribuciones admitidas
En la tabla siguiente se muestra una lista de las versiones de .NET Core admitidas actualmente en CentOS 7 y
CentOS 8. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core llegue al final del soporte
técnico o ya no se admita la versión de CentOS.
Una ✔️ indica que todavía se admite la versión de CentOS o de .NET Core.
Una ❌ indica que la versión de CentOS o de .NET Core no se admite en esa versión de CentOS.
Cuando una versión de CentOS y una versión de .NET Core tienen una ✔️ , se admite esa combinación de
sistema operativo y .NET.
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO IN STA L A C IÓ N
C EN TO S . N ET C O RE 2. 1 . N ET C O RE 3. 1 M A N UA L )
️ 8
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 7
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
Las siguientes versiones de .NET Core ya no se admiten. Las descargas de estas siguen estando publicadas:
3.0
2.2
2.0
Las instalaciones del administrador de paquetes solo se admiten en la arquitectura x64 . Otras arquitecturas, como
ARM , deben instalar manualmente el SDK de .NET Core o .NET Core Runtime. Para más información, consulte la
sección Instalación manual a continuación.
CentOS 8 ✔
️
.NET Core 3.1 está disponible en los repositorios de paquetes predeterminados de CentOS 8.
Instalación del SDK
El SDK de .NET Core permite desarrollar aplicaciones con .NET Core. Si instala el SDK de .NET Core, no necesita
instalar el entorno de ejecución correspondiente. Para instalar el SDK de .NET Core, ejecute los siguientes
comandos:
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-3.1 en los comandos anteriores por
dotnet-runtime-3.1 .
CentOS 7 ✔
️
Antes de instalar .NET, ejecute los siguientes comandos para agregar la clave de la firma del paquete de Microsoft a
la lista de claves de confianza y agregar el repositorio de paquetes de Microsoft. Abra un terminal y ejecute los
comandos siguientes:
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-2.1 en los comandos anteriores por
dotnet-runtime-3.1 .
Snap
.NET Core está disponible desde el almacén de snaps.
Un snap es una agrupación de una aplicación y sus dependencias que funcionan sin modificaciones en muchas
distribuciones de Linux diferentes. Los snaps se reconocen y se instalan desde el almacén de snaps. Para más
información sobre Snap, consulte Introducción a Snap.
Solo las versiones admitidas de .NET Core están disponibles mediante Snap.
Instalación del SDK
Los paquetes Snap para el SDK de .NET Core se publican con el mismo identificador: dotnet-sdk . Se puede instalar
una versión específica del SDK mediante la especificación del canal. El SDK incluye el entorno de ejecución
correspondiente. En la tabla siguiente se enumeran los canales:
VERSIÓ N DE . N ET C O RE PA Q UET E SN A P
Use el comando snap install para instalar un paquete Snap del SDK de .NET Core. Use el parámetro --channel
para indicar qué versión se va a instalar. Si se omite este parámetro, se usa latest/stable . En este ejemplo, se
especifica 3.1 :
A continuación, registre el comando dotnet del sistema con el comando snap alias :
Este comando tiene el formato sudo snap alias {package}.{command} {alias} . Puede elegir cualquier nombre de
{alias} que prefiera. Por ejemplo, puede asignar un nombre al comando después de la versión específica
instalada por el snap sudo snap alias dotnet-sdk.dotnet dotnet31 . Cuando use el comando dotnet31 , invocará
esta versión específica de .NET. Sin embargo, esta operación no es compatible con la mayoría de los tutoriales y
ejemplos, donde se espera que esté disponible un comando dotnet .
Instalación de la instancia en tiempo de ejecución
Los paquetes Snap de .NET Core Runtime se publican con su propio identificador de paquete. En la tabla siguiente
se muestra una lista de los identificadores de paquete:
VERSIÓ N DE . N ET C O RE PA Q UET E SN A P
3.0 dotnet-runtime-30
2.2 dotnet-runtime-22
Use el comando snap install para instalar un paquete Snap de .NET Core Runtime. En este ejemplo, se instala
.NET Core 3.1:
sudo snap install dotnet-runtime-31 --classic
A continuación, registre el comando dotnet del sistema con el comando snap alias :
Este comando tiene el formato sudo snap alias {package}.{command} {alias} . Puede elegir cualquier nombre de
{alias} que prefiera. Por ejemplo, puede asignar un nombre al comando después de la versión específica
instalada por el snap sudo snap alias dotnet-runtime-31.dotnet dotnet31 . Cuando use el comando dotnet31 ,
invocará esta versión específica de .NET. Sin embargo, esta operación no es compatible con la mayoría de los
tutoriales y ejemplos, donde se espera que esté disponible un comando dotnet .
Errores de certificado SSL
Cuando .NET se instala mediante Snap, es posible que en algunas distribuciones no se encuentren los certificados
SSL de .NET y que reciba un error similar al siguiente durante la acción restore :
export SSL_CERT_FILE=[path-to-certificate-file]
export SSL_CERT_DIR=/dev/null
La ubicación del certificado variará en función de la distribución. Estas son las ubicaciones de las distribuciones en
las que hemos experimentado el problema.
Fedora: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
OpenSUSE: /etc/ssl/ca-bundle.pem
Solus: /etc/ssl/certs/ca-certificates.crt
Dependencias
Al realizar la instalación con un administrador de paquetes, estas bibliotecas se instalan automáticamente. Sin
embargo, si instala manualmente .NET Core o publica una aplicación independiente, deberá asegurarse de que
estas bibliotecas estén instaladas:
krb5-libs
libicu
openssl-libs
Si la versión de OpenSSL del entorno de tiempo de ejecución de destino es 1.1 o más reciente, deberá instalar
compat-openssl10 .
Para obtener más información sobre las dependencias, vea Aplicaciones de Linux independientes.
En el caso de las aplicaciones de .NET Core que utilizan el ensamblado System.Drawing.Common, también se
necesitará la dependencia siguiente:
libgdiplus (versión 6.0.1 o posterior)
WARNING
Puede instalar una versión reciente de libgdiplus agregando el repositorio Mono al sistema. Para obtener más
información, vea https://www.mono-project.com/download/stable/.
./dotnet-install.sh -c Current
Para instalar .NET Core Runtime en lugar del SDK, use el parámetro --runtime .
Para instalar una versión específica, modifique el parámetro -c para indicar la versión específica. El siguiente
comando instala el SDK de .NET Core 3.1.
./dotnet-install.sh -c 3.1
Instalación manual
Como alternativa a los administradores de paquetes, puede descargar e instalar manualmente el SDK y el entorno
de ejecución. La instalación manual se suele llevar a cabo durante las pruebas de integración continua o en
distribuciones de Linux no admitidas. Para un desarrollador o usuario, generalmente es mejor usar un
administrador de paquetes.
Si instala el SDK de .NET Core, no necesita instalar el entorno de ejecución correspondiente. En primer lugar,
descargue una versión binaria del SDK o del entorno de ejecución de uno de los siguientes sitios:
✔ Descargas de la versión preliminar de .NET 5.0
️
️ Descargas de .NET Core 3.1
✔
️ Descargas de .NET Core 2.1
✔
Todas las descargas de .NET Core
A continuación, extraiga el archivo descargado y use el comando export para establecer las variables que se
utilizan en .NET Core. Luego, asegúrese de que .NET Core esté en PATH.
Para extraer el entorno de ejecución y hacer que los comandos de la CLI de .NET Core estén disponibles en el
terminal, en primer lugar, descargue una versión binaria de .NET Core. Luego, abra un terminal y ejecute los
siguientes comandos desde el directorio donde se guardó el archivo. El nombre del archivo puede ser distinto en
función de lo que haya descargado.
Use el comando siguiente para extraer el entorno de ejecución :
TIP
Los comandos export anteriores solo hacen que los comandos de la CLI de .NET Core estén disponibles para la sesión de
terminal en la que se ha ejecutado.
Puede editar el perfil del shell para agregar los comandos de forma permanente. Hay una serie de shells distintos disponibles
para Linux, y cada uno de ellos tiene un perfil diferente. Por ejemplo:
Shell de Bash : ~/.bash_profile, ~/.bashrc
Shell de Korn : ~/.kshrc or .profile
Shell de Z : ~/.zshrc or .zprofile
Edite el archivo de origen adecuado para el shell y agregue :$HOME/dotnet al final de la instrucción PATH existente. Si no
se incluye ninguna instrucción PATH , agregue una nueva línea con export PATH=$PATH:$HOME/dotnet .
Además, agregue export DOTNET_ROOT=$HOME/dotnet al final del archivo.
Este enfoque le permite instalar diferentes versiones en ubicaciones independientes y elegir explícitamente cuál
usará cada aplicación.
Pasos siguientes
Tutorial: Creación de una aplicación de consola con el SDK de .NET Core mediante Visual Studio Code
Instalación del SDK de .NET Core o de .NET Core
Runtime en Debian
16/09/2020 • 26 minutes to read • Edit Online
En este artículo se describe cómo instalar .NET Core en Debian. Cuando una versión de Debian no es compatible,
.NET Core deja de ser compatible con esa versión. Sin embargo, estas instrucciones pueden ayudarle a conseguir
que .NET Core se ejecute en esas versiones, aunque no se admita.
Instale el SDK (que incluye el entorno de ejecución) si quiere desarrollar aplicaciones .NET. O bien, si solo necesita
ejecutar aplicaciones, instale el entorno de ejecución. Si va a instalar el entorno de ejecución, le recomendamos que
instale el entorno de ejecución de ASP.NET Core , ya que incluye los de .NET Core y ASP.NET Core.
Si ya ha instalado el SDK o el entorno de ejecución, use los comandos dotnet --list-sdks y
dotnet --list-runtimes para ver qué versiones están instaladas. Para más información, consulte Cómo comprobar
que .NET Core ya está instalado.
Las instalaciones del administrador de paquetes solo se admiten en la arquitectura x64 . Otras arquitecturas, como
ARM , deben instalar manualmente el SDK de .NET Core o .NET Core Runtime. Para más información, consulte la
sección Instalación manual a continuación.
Distribuciones admitidas
En la tabla siguiente se muestra una lista de versiones de .NET Core actualmente compatibles y las versiones de
Debian en las que se admiten. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core llegue
al final del soporte técnico o la versión de Debian llegue al final del ciclo de vida.
Una ✔️ indica que todavía se admite la versión de Debian o de .NET Core.
Una ❌ indica que la versión de Debian o de .NET Core no se admite en esa versión de Debian.
Cuando una versión de Debian y una versión de .NET Core tienen una ✔️ , se admite esa combinación de
sistema operativo y .NET.
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO IN STA L A C IÓ N
DEB IA N . N ET C O RE 2. 1 . N ET C O RE 3. 1 M A N UA L )
️ 10
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 9
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
❌8 ️ 2.1
✔ ❌ 3.1 ❌ 5.0 (versión preliminar)
Las siguientes versiones de .NET Core ya no se admiten. aunque sus descargas siguen estando publicadas:
3.0
2.2
2.0
Debian 10 ✔
️
La instalación con APT puede realizarse con unos pocos comandos. Antes de instalar .NET, ejecute los siguientes
comandos para agregar la clave de la firma del paquete de Microsoft a la lista de claves de confianza y agregar el
repositorio de paquetes.
Abra un terminal y ejecute los comandos siguientes:
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete dotnet-sdk-3.1 , consulte la sección Solución
de problemas de APT.
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete aspnetcore-runtime-3.1 , consulte la
sección Solución de problemas de APT.
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-3.1 en los comandos anteriores por
dotnet-runtime-3.1 .
Debian 9 ✔
️
La instalación con APT puede realizarse con unos pocos comandos. Antes de instalar .NET, ejecute los siguientes
comandos para agregar la clave de la firma del paquete de Microsoft a la lista de claves de confianza y agregar el
repositorio de paquetes.
Abra un terminal y ejecute los comandos siguientes:
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete dotnet-sdk-3.1 , consulte la sección Solución
de problemas de APT.
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete aspnetcore-runtime-3.1 , consulte la
sección Solución de problemas de APT.
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-3.1 en los comandos anteriores por
dotnet-runtime-3.1 .
Debian 8 ❌
❌ Tenga en cuenta que esta versión de Debian ya no se admite.
La instalación con APT puede realizarse con unos pocos comandos. Antes de instalar .NET, ejecute los siguientes
comandos para agregar la clave de la firma del paquete de Microsoft a la lista de claves de confianza y agregar el
repositorio de paquetes.
Abra un terminal y ejecute los comandos siguientes:
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete dotnet-sdk-2.1 , consulte la sección Solución
de problemas de APT.
IMPORTANT
Si recibe un mensaje de error similar a No se puede encontrar el paquete aspnetcore-runtime-2.1 , consulte la
sección Solución de problemas de APT.
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-2.1 en los comandos anteriores por
dotnet-runtime-2.1 .
{os-version}
Representa la versión de Linux en la que está. Se usa en el comando wget siguiente.
Después, intente instalar .NET Core de nuevo. Si eso no funciona, puede ejecutar una instalación manual con los
comandos siguientes:
Snap
.NET Core está disponible desde el almacén de snaps.
Un snap es una agrupación de una aplicación y sus dependencias que funcionan sin modificaciones en muchas
distribuciones de Linux diferentes. Los snaps se reconocen y se instalan desde el almacén de snaps. Para más
información sobre Snap, consulte Introducción a Snap.
Solo las versiones admitidas de .NET Core están disponibles mediante Snap.
Instalación del SDK
Los paquetes Snap para el SDK de .NET Core se publican con el mismo identificador: dotnet-sdk . Se puede instalar
una versión específica del SDK mediante la especificación del canal. El SDK incluye el entorno de ejecución
correspondiente. En la tabla siguiente se enumeran los canales:
VERSIÓ N DE . N ET C O RE PA Q UET E SN A P
Use el comando snap install para instalar un paquete Snap del SDK de .NET Core. Use el parámetro --channel
para indicar qué versión se va a instalar. Si se omite este parámetro, se usa latest/stable . En este ejemplo, se
especifica 3.1 :
A continuación, registre el comando dotnet del sistema con el comando snap alias :
Este comando tiene el formato sudo snap alias {package}.{command} {alias} . Puede elegir cualquier nombre de
{alias} que prefiera. Por ejemplo, puede asignar un nombre al comando después de la versión específica
instalada por el snap sudo snap alias dotnet-sdk.dotnet dotnet31 . Cuando use el comando dotnet31 , invocará
esta versión específica de .NET. Sin embargo, esta operación no es compatible con la mayoría de los tutoriales y
ejemplos, donde se espera que esté disponible un comando dotnet .
Instalación de la instancia en tiempo de ejecución
Los paquetes Snap de .NET Core Runtime se publican con su propio identificador de paquete. En la tabla siguiente
se muestra una lista de los identificadores de paquete:
VERSIÓ N DE . N ET C O RE PA Q UET E SN A P
3.0 dotnet-runtime-30
2.2 dotnet-runtime-22
Use el comando snap install para instalar un paquete Snap de .NET Core Runtime. En este ejemplo, se instala
.NET Core 3.1:
A continuación, registre el comando dotnet del sistema con el comando snap alias :
Este comando tiene el formato sudo snap alias {package}.{command} {alias} . Puede elegir cualquier nombre de
{alias} que prefiera. Por ejemplo, puede asignar un nombre al comando después de la versión específica
instalada por el snap sudo snap alias dotnet-runtime-31.dotnet dotnet31 . Cuando use el comando dotnet31 ,
invocará esta versión específica de .NET. Sin embargo, esta operación no es compatible con la mayoría de los
tutoriales y ejemplos, donde se espera que esté disponible un comando dotnet .
Errores de certificado SSL
Cuando .NET se instala mediante Snap, es posible que en algunas distribuciones no se encuentren los certificados
SSL de .NET y que reciba un error similar al siguiente durante la acción restore :
export SSL_CERT_FILE=[path-to-certificate-file]
export SSL_CERT_DIR=/dev/null
La ubicación del certificado variará en función de la distribución. Estas son las ubicaciones de las distribuciones en
las que hemos experimentado el problema.
Fedora: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
OpenSUSE: /etc/ssl/ca-bundle.pem
Solus: /etc/ssl/certs/ca-certificates.crt
Dependencias
Al realizar la instalación con un administrador de paquetes, estas bibliotecas se instalan automáticamente. Sin
embargo, si instala manualmente .NET Core o publica una aplicación independiente, deberá asegurarse de que
estas bibliotecas estén instaladas:
libc6
libgcc1
libgssapi-krb5-2
libicu52 (para 8.x)
libicu57 (para 9.x)
libicu63 (para 10.x)
libicu67 (para 11.x)
libssl1.0.0 (para 8.x)
libssl1.1 (para 9.x-11.x)
libstdc++6
zlib1g
En el caso de las aplicaciones de .NET Core que utilizan el ensamblado System.Drawing.Common, también se
necesita la dependencia siguiente:
libgdiplus (versión 6.0.1 o posteriores)
WARNING
Puede instalar una versión reciente de libgdiplus agregando el repositorio Mono al sistema. Para obtener más
información, vea https://www.mono-project.com/download/stable/.
Instalación con script
Los scripts de dotnet-install se usan para la automatización y las instalaciones que no son de administrador del
SDK y del Runtime . Puede descargar el script de https://dot.net/v1/dotnet-install.sh.
El valor predeterminado del script es instalar la versión más reciente del SDK de soporte técnico a largo plazo
(LTS), que actualmente es .NET Core 3.1. Para instalar la versión actual, que puede no ser una versión (LTS), use el
parámetro -c Current .
./dotnet-install.sh -c Current
Para instalar .NET Core Runtime en lugar del SDK, use el parámetro --runtime .
Para instalar una versión específica, modifique el parámetro -c para indicar la versión específica. El siguiente
comando instala el SDK de .NET Core 3.1.
./dotnet-install.sh -c 3.1
Instalación manual
Como alternativa a los administradores de paquetes, puede descargar e instalar manualmente el SDK y el entorno
de ejecución. La instalación manual se suele llevar a cabo durante las pruebas de integración continua o en
distribuciones de Linux no admitidas. Para un desarrollador o usuario, generalmente es mejor usar un
administrador de paquetes.
Si instala el SDK de .NET Core, no necesita instalar el entorno de ejecución correspondiente. En primer lugar,
descargue una versión binaria del SDK o del entorno de ejecución de uno de los siguientes sitios:
✔ Descargas de la versión preliminar de .NET 5.0
️
️ Descargas de .NET Core 3.1
✔
️ Descargas de .NET Core 2.1
✔
Todas las descargas de .NET Core
A continuación, extraiga el archivo descargado y use el comando export para establecer las variables que se
utilizan en .NET Core. Luego, asegúrese de que .NET Core esté en PATH.
Para extraer el entorno de ejecución y hacer que los comandos de la CLI de .NET Core estén disponibles en el
terminal, en primer lugar, descargue una versión binaria de .NET Core. Luego, abra un terminal y ejecute los
siguientes comandos desde el directorio donde se guardó el archivo. El nombre del archivo puede ser distinto en
función de lo que haya descargado.
Use el comando siguiente para extraer el entorno de ejecución :
TIP
Los comandos export anteriores solo hacen que los comandos de la CLI de .NET Core estén disponibles para la sesión de
terminal en la que se ha ejecutado.
Puede editar el perfil del shell para agregar los comandos de forma permanente. Hay una serie de shells distintos disponibles
para Linux, y cada uno de ellos tiene un perfil diferente. Por ejemplo:
Shell de Bash : ~/.bash_profile, ~/.bashrc
Shell de Korn : ~/.kshrc or .profile
Shell de Z : ~/.zshrc or .zprofile
Edite el archivo de origen adecuado para el shell y agregue :$HOME/dotnet al final de la instrucción PATH existente. Si no
se incluye ninguna instrucción PATH , agregue una nueva línea con export PATH=$PATH:$HOME/dotnet .
Además, agregue export DOTNET_ROOT=$HOME/dotnet al final del archivo.
Este enfoque le permite instalar diferentes versiones en ubicaciones independientes y elegir explícitamente cuál
usará cada aplicación.
Pasos siguientes
Tutorial: Creación de una aplicación de consola con el SDK de .NET Core mediante Visual Studio Code
Instalación del SDK o de .NET Core Runtime en
Fedora
16/09/2020 • 28 minutes to read • Edit Online
.NET Core es compatible con Fedora. En este artículo se describe cómo instalar .NET Core en Fedora. Cuando una
versión de Fedora no es compatible, .NET Core deja de ser compatible con esa versión. Sin embargo, estas
instrucciones pueden ayudarle a conseguir que .NET Core se ejecute en esas versiones, aunque no se admita.
Instale el SDK (que incluye el entorno de ejecución) si quiere desarrollar aplicaciones .NET. O bien, si solo necesita
ejecutar aplicaciones, instale el entorno de ejecución. Si va a instalar el entorno de ejecución, le recomendamos que
instale el entorno de ejecución de ASP.NET Core , ya que incluye los de .NET Core y ASP.NET Core.
Si ya ha instalado el SDK o el entorno de ejecución, use los comandos dotnet --list-sdks y
dotnet --list-runtimes para ver qué versiones están instaladas. Para más información, consulte Cómo comprobar
que .NET Core ya está instalado.
Las instalaciones del administrador de paquetes solo se admiten en la arquitectura x64 . Otras arquitecturas, como
ARM , deben instalar manualmente el SDK de .NET Core o .NET Core Runtime. Para más información, consulte la
sección Instalación manual a continuación.
Distribuciones admitidas
En la tabla siguiente se muestra una lista de versiones de .NET Core actualmente compatibles y las versiones de
Fedora en las que se admiten. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core llegue
al final del soporte técnico o la versión de Fedora llegue al final del ciclo de vida.
Una ✔️ indica que todavía se admite la versión de Fedora o de .NET Core.
Una ❌ indica que la versión de Fedora o de .NET Core no se admite en esa versión de Fedora.
Cuando una versión de Fedora y una versión de .NET Core tienen una ✔️ , se admite esa combinación de
sistema operativo y .NET.
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO IN STA L A C IÓ N
F EDO RA . N ET C O RE 2. 1 . N ET C O RE 3. 1 M A N UA L )
️ 32
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 31
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
❌ 30 ️ 2.1
✔ ️ 3.1
✔ ❌ 5.0 (versión preliminar)
❌ 29 ️ 2.1
✔ ️ 3.1
✔ ❌ 5.0 (versión preliminar)
❌ 28 ️ 2.1
✔ ❌ 3.1 ❌ 5.0 (versión preliminar)
❌ 27 ️ 2.1
✔ ❌ 3.1 ❌ 5.0 (versión preliminar)
Las siguientes versiones de .NET Core ya no se admiten. Las descargas de estas siguen estando publicadas:
3.0
2.2
2.0
Fedora 32 ✔
️
.NET Core 3.1 está disponible en los repositorios de paquetes predeterminados para Fedora 32.
Instalación del SDK
El SDK de .NET Core permite desarrollar aplicaciones con .NET Core. Si instala el SDK de .NET Core, no necesita
instalar el entorno de ejecución correspondiente. Para instalar el SDK de .NET Core, ejecute los siguientes
comandos:
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-3.1 en los comandos anteriores por
dotnet-runtime-3.1 .
Fedora 31 ✔
️
Antes de instalar .NET, ejecute los siguientes comandos para agregar la clave de la firma del paquete de Microsoft a
la lista de claves de confianza y agregar el repositorio de paquetes de Microsoft. Abra un terminal y ejecute los
comandos siguientes:
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-3.1 en los comandos anteriores por
dotnet-runtime-3.1 .
Fedora 30 ❌
❌ Tenga en cuenta que esta versión de Fedora ya no se admite.
Antes de instalar .NET, ejecute los siguientes comandos para agregar la clave de la firma del paquete de Microsoft a
la lista de claves de confianza y agregar el repositorio de paquetes de Microsoft. Abra un terminal y ejecute los
comandos siguientes:
sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
sudo wget -O /etc/yum.repos.d/microsoft-prod.repo https://packages.microsoft.com/config/fedora/30/prod.repo
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-3.1 en los comandos anteriores por
dotnet-runtime-3.1 .
Fedora 29 ❌
❌ Tenga en cuenta que esta versión de Fedora ya no se admite.
Antes de instalar .NET, ejecute los siguientes comandos para agregar la clave de la firma del paquete de Microsoft a
la lista de claves de confianza y agregar el repositorio de paquetes de Microsoft. Abra un terminal y ejecute los
comandos siguientes:
Fedora 28 ❌
❌ Tenga en cuenta que esta versión de Fedora ya no se admite.
Antes de instalar .NET, ejecute los siguientes comandos para agregar la clave de la firma del paquete de Microsoft a
la lista de claves de confianza y agregar el repositorio de paquetes de Microsoft. Abra un terminal y ejecute los
comandos siguientes:
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-2.0 en los comandos anteriores por
dotnet-runtime-2.0 .
Fedora 27 ❌
❌ Tenga en cuenta que esta versión de Fedora ya no se admite.
Antes de instalar .NET, ejecute los siguientes comandos para agregar la clave de la firma del paquete de Microsoft a
la lista de claves de confianza y agregar el repositorio de paquetes de Microsoft. Abra un terminal y ejecute los
comandos siguientes:
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-2.0 en los comandos anteriores por
dotnet-runtime-2.0 .
Snap
.NET Core está disponible desde el almacén de snaps.
Un snap es una agrupación de una aplicación y sus dependencias que funcionan sin modificaciones en muchas
distribuciones de Linux diferentes. Los snaps se reconocen y se instalan desde el almacén de snaps. Para más
información sobre Snap, consulte Introducción a Snap.
Solo las versiones admitidas de .NET Core están disponibles mediante Snap.
Instalación del SDK
Los paquetes Snap para el SDK de .NET Core se publican con el mismo identificador: dotnet-sdk . Se puede instalar
una versión específica del SDK mediante la especificación del canal. El SDK incluye el entorno de ejecución
correspondiente. En la tabla siguiente se enumeran los canales:
VERSIÓ N DE . N ET C O RE PA Q UET E SN A P
Use el comando snap install para instalar un paquete Snap del SDK de .NET Core. Use el parámetro --channel
para indicar qué versión se va a instalar. Si se omite este parámetro, se usa latest/stable . En este ejemplo, se
especifica 3.1 :
A continuación, registre el comando dotnet del sistema con el comando snap alias :
Este comando tiene el formato sudo snap alias {package}.{command} {alias} . Puede elegir cualquier nombre de
{alias} que prefiera. Por ejemplo, puede asignar un nombre al comando después de la versión específica
instalada por el snap sudo snap alias dotnet-sdk.dotnet dotnet31 . Cuando use el comando dotnet31 , invocará
esta versión específica de .NET. Sin embargo, esta operación no es compatible con la mayoría de los tutoriales y
ejemplos, donde se espera que esté disponible un comando dotnet .
Instalación de la instancia en tiempo de ejecución
Los paquetes Snap de .NET Core Runtime se publican con su propio identificador de paquete. En la tabla siguiente
se muestra una lista de los identificadores de paquete:
VERSIÓ N DE . N ET C O RE PA Q UET E SN A P
3.0 dotnet-runtime-30
2.2 dotnet-runtime-22
Use el comando snap install para instalar un paquete Snap de .NET Core Runtime. En este ejemplo, se instala
.NET Core 3.1:
A continuación, registre el comando dotnet del sistema con el comando snap alias :
Este comando tiene el formato sudo snap alias {package}.{command} {alias} . Puede elegir cualquier nombre de
{alias} que prefiera. Por ejemplo, puede asignar un nombre al comando después de la versión específica
instalada por el snap sudo snap alias dotnet-runtime-31.dotnet dotnet31 . Cuando use el comando dotnet31 ,
invocará esta versión específica de .NET. Sin embargo, esta operación no es compatible con la mayoría de los
tutoriales y ejemplos, donde se espera que esté disponible un comando dotnet .
Errores de certificado SSL
Cuando .NET se instala mediante Snap, es posible que en algunas distribuciones no se encuentren los certificados
SSL de .NET y que reciba un error similar al siguiente durante la acción restore :
export SSL_CERT_FILE=[path-to-certificate-file]
export SSL_CERT_DIR=/dev/null
La ubicación del certificado variará en función de la distribución. Estas son las ubicaciones de las distribuciones en
las que hemos experimentado el problema.
Fedora: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
OpenSUSE: /etc/ssl/ca-bundle.pem
Solus: /etc/ssl/certs/ca-certificates.crt
Dependencias
Al realizar la instalación con un administrador de paquetes, estas bibliotecas se instalan automáticamente. Sin
embargo, si instala manualmente .NET Core o publica una aplicación independiente, deberá asegurarse de que
estas bibliotecas estén instaladas:
krb5-libs
libicu
openssl-libs
Si la versión de OpenSSL del entorno de tiempo de ejecución de destino es 1.1 o más reciente, deberá instalar
compat-openssl10 .
Para obtener más información sobre las dependencias, vea Aplicaciones de Linux independientes.
En el caso de las aplicaciones de .NET Core que utilizan el ensamblado System.Drawing.Common, también se
necesitará la dependencia siguiente:
libgdiplus (versión 6.0.1 o posterior)
WARNING
Puede instalar una versión reciente de libgdiplus agregando el repositorio Mono al sistema. Para obtener más
información, vea https://www.mono-project.com/download/stable/.
./dotnet-install.sh -c Current
Para instalar .NET Core Runtime en lugar del SDK, use el parámetro --runtime .
Para instalar una versión específica, modifique el parámetro -c para indicar la versión específica. El siguiente
comando instala el SDK de .NET Core 3.1.
./dotnet-install.sh -c 3.1
Instalación manual
Como alternativa a los administradores de paquetes, puede descargar e instalar manualmente el SDK y el entorno
de ejecución. La instalación manual se suele llevar a cabo durante las pruebas de integración continua o en
distribuciones de Linux no admitidas. Para un desarrollador o usuario, generalmente es mejor usar un
administrador de paquetes.
Si instala el SDK de .NET Core, no necesita instalar el entorno de ejecución correspondiente. En primer lugar,
descargue una versión binaria del SDK o del entorno de ejecución de uno de los siguientes sitios:
✔ Descargas de la versión preliminar de .NET 5.0
️
️ Descargas de .NET Core 3.1
✔
️ Descargas de .NET Core 2.1
✔
Todas las descargas de .NET Core
A continuación, extraiga el archivo descargado y use el comando export para establecer las variables que se
utilizan en .NET Core. Luego, asegúrese de que .NET Core esté en PATH.
Para extraer el entorno de ejecución y hacer que los comandos de la CLI de .NET Core estén disponibles en el
terminal, en primer lugar, descargue una versión binaria de .NET Core. Luego, abra un terminal y ejecute los
siguientes comandos desde el directorio donde se guardó el archivo. El nombre del archivo puede ser distinto en
función de lo que haya descargado.
Use el comando siguiente para extraer el entorno de ejecución :
Este enfoque le permite instalar diferentes versiones en ubicaciones independientes y elegir explícitamente cuál
usará cada aplicación.
Pasos siguientes
Tutorial: Creación de una aplicación de consola con el SDK de .NET Core mediante Visual Studio Code
Instalación del SDK de .NET Core o de .NET Core
Runtime en openSUSE
16/09/2020 • 18 minutes to read • Edit Online
.NET Core es compatible con openSUSE. En este artículo se describe cómo instalar .NET Core en openSUSE.
Instale el SDK (que incluye el entorno de ejecución) si quiere desarrollar aplicaciones .NET. O bien, si solo necesita
ejecutar aplicaciones, instale el entorno de ejecución. Si va a instalar el entorno de ejecución, le recomendamos que
instale el entorno de ejecución de ASP.NET Core , ya que incluye los de .NET Core y ASP.NET Core.
Si ya ha instalado el SDK o el entorno de ejecución, use los comandos dotnet --list-sdks y
dotnet --list-runtimes para ver qué versiones están instaladas. Para más información, consulte Cómo comprobar
que .NET Core ya está instalado.
Las instalaciones del administrador de paquetes solo se admiten en la arquitectura x64 . Otras arquitecturas, como
ARM , deben instalar manualmente el SDK de .NET Core o .NET Core Runtime. Para más información, consulte la
sección Instalación manual a continuación.
Distribuciones admitidas
En la tabla siguiente se muestra una lista de las versiones de .NET Core compatibles actualmente con openSUSE
15. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core llegue al final del soporte técnico
o ya no se admita la versión de openSUSE.
Una ✔️ indica que todavía se admite la versión de openSUSE o de .NET Core.
Una ❌ indica que la versión de openSUSE o de .NET Core no se admite en esa versión de openSUSE.
Cuando una versión de openSUSE y una versión de .NET Core tienen una ✔️ , se admite esa combinación de
sistema operativo y .NET.
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO IN STA L A C IÓ N
O P EN SUSE . N ET C O RE 2. 1 . N ET C O RE 3. 1 M A N UA L )
️ 15
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
Las siguientes versiones de .NET Core ya no se admiten. aunque sus descargas siguen estando publicadas:
3.0
2.2
2.0
openSUSE 15 ✔
️
Antes de instalar .NET, ejecute los siguientes comandos para agregar la clave de la firma del paquete de Microsoft a
la lista de claves de confianza y agregar el repositorio de paquetes de Microsoft. Abra un terminal y ejecute los
comandos siguientes:
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-2.1 en los comandos anteriores por
dotnet-runtime-3.1 .
Snap
.NET Core está disponible desde el almacén de snaps.
Un snap es una agrupación de una aplicación y sus dependencias que funcionan sin modificaciones en muchas
distribuciones de Linux diferentes. Los snaps se reconocen y se instalan desde el almacén de snaps. Para más
información sobre Snap, consulte Introducción a Snap.
Solo las versiones admitidas de .NET Core están disponibles mediante Snap.
Instalación del SDK
Los paquetes Snap para el SDK de .NET Core se publican con el mismo identificador: dotnet-sdk . Se puede instalar
una versión específica del SDK mediante la especificación del canal. El SDK incluye el entorno de ejecución
correspondiente. En la tabla siguiente se enumeran los canales:
VERSIÓ N DE . N ET C O RE PA Q UET E SN A P
Use el comando snap install para instalar un paquete Snap del SDK de .NET Core. Use el parámetro --channel
para indicar qué versión se va a instalar. Si se omite este parámetro, se usa latest/stable . En este ejemplo, se
especifica 3.1 :
A continuación, registre el comando dotnet del sistema con el comando snap alias :
sudo snap alias dotnet-sdk.dotnet dotnet
Este comando tiene el formato sudo snap alias {package}.{command} {alias} . Puede elegir cualquier nombre de
{alias} que prefiera. Por ejemplo, puede asignar un nombre al comando después de la versión específica
instalada por el snap sudo snap alias dotnet-sdk.dotnet dotnet31 . Cuando use el comando dotnet31 , invocará
esta versión específica de .NET. Sin embargo, esta operación no es compatible con la mayoría de los tutoriales y
ejemplos, donde se espera que esté disponible un comando dotnet .
Instalación de la instancia en tiempo de ejecución
Los paquetes Snap de .NET Core Runtime se publican con su propio identificador de paquete. En la tabla siguiente
se muestra una lista de los identificadores de paquete:
VERSIÓ N DE . N ET C O RE PA Q UET E SN A P
3.0 dotnet-runtime-30
2.2 dotnet-runtime-22
Use el comando snap install para instalar un paquete Snap de .NET Core Runtime. En este ejemplo, se instala
.NET Core 3.1:
A continuación, registre el comando dotnet del sistema con el comando snap alias :
Este comando tiene el formato sudo snap alias {package}.{command} {alias} . Puede elegir cualquier nombre de
{alias} que prefiera. Por ejemplo, puede asignar un nombre al comando después de la versión específica
instalada por el snap sudo snap alias dotnet-runtime-31.dotnet dotnet31 . Cuando use el comando dotnet31 ,
invocará esta versión específica de .NET. Sin embargo, esta operación no es compatible con la mayoría de los
tutoriales y ejemplos, donde se espera que esté disponible un comando dotnet .
Errores de certificado SSL
Cuando .NET se instala mediante Snap, es posible que en algunas distribuciones no se encuentren los certificados
SSL de .NET y que reciba un error similar al siguiente durante la acción restore :
La ubicación del certificado variará en función de la distribución. Estas son las ubicaciones de las distribuciones en
las que hemos experimentado el problema.
Fedora: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
OpenSUSE: /etc/ssl/ca-bundle.pem
Solus: /etc/ssl/certs/ca-certificates.crt
Dependencias
Al realizar la instalación con un administrador de paquetes, estas bibliotecas se instalan automáticamente. Sin
embargo, si instala manualmente .NET Core o publica una aplicación independiente, deberá asegurarse de que
estas bibliotecas estén instaladas:
krb5
libicu
libopenssl1_0_0
Si la versión de OpenSSL del entorno de tiempo de ejecución de destino es 1.1 o más reciente, deberá instalar
compat-openssl10 .
Para obtener más información sobre las dependencias, vea Aplicaciones de Linux independientes.
En el caso de las aplicaciones de .NET Core que utilizan el ensamblado System.Drawing.Common, también se
necesitará la dependencia siguiente:
libgdiplus (versión 6.0.1 o posterior)
WARNING
Puede instalar una versión reciente de libgdiplus agregando el repositorio Mono al sistema. Para obtener más
información, vea https://www.mono-project.com/download/stable/.
./dotnet-install.sh -c Current
Para instalar .NET Core Runtime en lugar del SDK, use el parámetro --runtime .
Para instalar una versión específica, modifique el parámetro -c para indicar la versión específica. El siguiente
comando instala el SDK de .NET Core 3.1.
./dotnet-install.sh -c 3.1
Instalación manual
Como alternativa a los administradores de paquetes, puede descargar e instalar manualmente el SDK y el entorno
de ejecución. La instalación manual se suele llevar a cabo durante las pruebas de integración continua o en
distribuciones de Linux no admitidas. Para un desarrollador o usuario, generalmente es mejor usar un
administrador de paquetes.
Si instala el SDK de .NET Core, no necesita instalar el entorno de ejecución correspondiente. En primer lugar,
descargue una versión binaria del SDK o del entorno de ejecución de uno de los siguientes sitios:
✔ Descargas de la versión preliminar de .NET 5.0
️
️ Descargas de .NET Core 3.1
✔
️ Descargas de .NET Core 2.1
✔
Todas las descargas de .NET Core
A continuación, extraiga el archivo descargado y use el comando export para establecer las variables que se
utilizan en .NET Core. Luego, asegúrese de que .NET Core esté en PATH.
Para extraer el entorno de ejecución y hacer que los comandos de la CLI de .NET Core estén disponibles en el
terminal, en primer lugar, descargue una versión binaria de .NET Core. Luego, abra un terminal y ejecute los
siguientes comandos desde el directorio donde se guardó el archivo. El nombre del archivo puede ser distinto en
función de lo que haya descargado.
Use el comando siguiente para extraer el entorno de ejecución :
TIP
Los comandos export anteriores solo hacen que los comandos de la CLI de .NET Core estén disponibles para la sesión de
terminal en la que se ha ejecutado.
Puede editar el perfil del shell para agregar los comandos de forma permanente. Hay una serie de shells distintos disponibles
para Linux, y cada uno de ellos tiene un perfil diferente. Por ejemplo:
Shell de Bash : ~/.bash_profile, ~/.bashrc
Shell de Korn : ~/.kshrc or .profile
Shell de Z : ~/.zshrc or .zprofile
Edite el archivo de origen adecuado para el shell y agregue :$HOME/dotnet al final de la instrucción PATH existente. Si no
se incluye ninguna instrucción PATH , agregue una nueva línea con export PATH=$PATH:$HOME/dotnet .
Además, agregue export DOTNET_ROOT=$HOME/dotnet al final del archivo.
Este enfoque le permite instalar diferentes versiones en ubicaciones independientes y elegir explícitamente cuál
usará cada aplicación.
Pasos siguientes
Tutorial: Creación de una aplicación de consola con el SDK de .NET Core mediante Visual Studio Code
Instalación del SDK de .NET Core o de .NET Core
Runtime en RHEL
16/09/2020 • 18 minutes to read • Edit Online
.NET Core es compatible con RHEL. En este artículo se describe cómo instalar .NET Core en RHEL.
Instale el SDK (que incluye el entorno de ejecución) si quiere desarrollar aplicaciones .NET. O bien, si solo necesita
ejecutar aplicaciones, instale el entorno de ejecución. Si va a instalar el entorno de ejecución, le recomendamos que
instale el entorno de ejecución de ASP.NET Core , ya que incluye los de .NET Core y ASP.NET Core.
Si ya ha instalado el SDK o el entorno de ejecución, use los comandos dotnet --list-sdks y
dotnet --list-runtimes para ver qué versiones están instaladas. Para más información, consulte Cómo comprobar
que .NET Core ya está instalado.
Distribuciones admitidas
En la tabla siguiente se muestra una lista de las versiones de .NET Core compatibles actualmente con RHEL 7 y
RHEL 8. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core llegue al final del soporte
técnico o ya no se admita la versión de RHEL.
Una ✔
️ indica que todavía se admite la versión de RHEL o de .NET Core.
Una ❌ indica que la versión de RHEL o de .NET Core no se admite en esa versión de RHEL.
Cuando una versión de RHEL y una versión de .NET Core tienen una ✔
️ , se admite esa combinación de sistema
operativo y .NET.
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO IN STA L A C IÓ N
RH EL . N ET C O RE 2. 1 . N ET C O RE 3. 1 M A N UA L )
️ 8
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 7
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
Las siguientes versiones de .NET Core ya no se admiten. Las descargas de estas siguen estando publicadas:
3.0
2.2
2.0
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-3.1 en los comandos anteriores por
dotnet-runtime-3.1 .
RHEL 7 ✔
️
Antes de instalar .NET, ejecute los siguientes comandos para agregar la clave de la firma del paquete de Microsoft a
la lista de claves de confianza y agregar el repositorio de paquetes de Microsoft. Abra un terminal y ejecute los
comandos siguientes:
El siguiente comando instala el paquete scl-utils :
Red Hat no recomienda habilitar rh-dotnet31 de forma permanente porque puede afectar a otros programas. Por
ejemplo, rh-dotnet31 incluye una versión de libcurl que varía con respecto a la versión base de RHEL. Esto
puede dar lugar a problemas en programas que no esperan una versión diferente de libcurl . Si quiere habilitar
rh-dotnet de forma permanente, agregue la siguiente línea al archivo ~/.bashrc.
Red Hat no recomienda habilitar rh-dotnet31-aspnetcore-runtime-3.1 de forma permanente porque puede afectar
a otros programas. Por ejemplo, rh-dotnet31-aspnetcore-runtime-3.1 incluye una versión de libcurl que varía
con respecto a la versión base de RHEL. Esto puede dar lugar a problemas en programas que no esperan una
versión diferente de libcurl . Si quiere habilitar rh-dotnet31-aspnetcore-runtime-3.1 de forma permanente,
agregue la siguiente línea al archivo ~/.bashrc.
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace rh-dotnet31-aspnetcore-runtime-3.1 en los comandos
anteriores por rh-dotnet31-dotnet-runtime-3.1 .
Snap
.NET Core está disponible desde el almacén de snaps.
Un snap es una agrupación de una aplicación y sus dependencias que funcionan sin modificaciones en muchas
distribuciones de Linux diferentes. Los snaps se reconocen y se instalan desde el almacén de snaps. Para más
información sobre Snap, consulte Introducción a Snap.
Solo las versiones admitidas de .NET Core están disponibles mediante Snap.
Instalación del SDK
Los paquetes Snap para el SDK de .NET Core se publican con el mismo identificador: dotnet-sdk . Se puede instalar
una versión específica del SDK mediante la especificación del canal. El SDK incluye el entorno de ejecución
correspondiente. En la tabla siguiente se enumeran los canales:
VERSIÓ N DE . N ET C O RE PA Q UET E SN A P
Use el comando snap install para instalar un paquete Snap del SDK de .NET Core. Use el parámetro --channel
para indicar qué versión se va a instalar. Si se omite este parámetro, se usa latest/stable . En este ejemplo, se
especifica 3.1 :
A continuación, registre el comando dotnet del sistema con el comando snap alias :
sudo snap alias dotnet-sdk.dotnet dotnet
Este comando tiene el formato sudo snap alias {package}.{command} {alias} . Puede elegir cualquier nombre de
{alias} que prefiera. Por ejemplo, puede asignar un nombre al comando después de la versión específica
instalada por el snap sudo snap alias dotnet-sdk.dotnet dotnet31 . Cuando use el comando dotnet31 , invocará
esta versión específica de .NET. Sin embargo, esta operación no es compatible con la mayoría de los tutoriales y
ejemplos, donde se espera que esté disponible un comando dotnet .
Instalación de la instancia en tiempo de ejecución
Los paquetes Snap de .NET Core Runtime se publican con su propio identificador de paquete. En la tabla siguiente
se muestra una lista de los identificadores de paquete:
VERSIÓ N DE . N ET C O RE PA Q UET E SN A P
3.0 dotnet-runtime-30
2.2 dotnet-runtime-22
Use el comando snap install para instalar un paquete Snap de .NET Core Runtime. En este ejemplo, se instala
.NET Core 3.1:
A continuación, registre el comando dotnet del sistema con el comando snap alias :
Este comando tiene el formato sudo snap alias {package}.{command} {alias} . Puede elegir cualquier nombre de
{alias} que prefiera. Por ejemplo, puede asignar un nombre al comando después de la versión específica
instalada por el snap sudo snap alias dotnet-runtime-31.dotnet dotnet31 . Cuando use el comando dotnet31 ,
invocará esta versión específica de .NET. Sin embargo, esta operación no es compatible con la mayoría de los
tutoriales y ejemplos, donde se espera que esté disponible un comando dotnet .
Errores de certificado SSL
Cuando .NET se instala mediante Snap, es posible que en algunas distribuciones no se encuentren los certificados
SSL de .NET y que reciba un error similar al siguiente durante la acción restore :
La ubicación del certificado variará en función de la distribución. Estas son las ubicaciones de las distribuciones en
las que hemos experimentado el problema.
Fedora: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
OpenSUSE: /etc/ssl/ca-bundle.pem
Solus: /etc/ssl/certs/ca-certificates.crt
Dependencias
Al realizar la instalación con un administrador de paquetes, estas bibliotecas se instalan automáticamente. Sin
embargo, si instala manualmente .NET Core o publica una aplicación independiente, deberá asegurarse de que
estas bibliotecas estén instaladas:
krb5-libs
libicu
openssl-libs
Si la versión de OpenSSL del entorno de tiempo de ejecución de destino es 1.1 o más reciente, deberá instalar
compat-openssl10 .
Para obtener más información sobre las dependencias, vea Aplicaciones de Linux independientes.
En el caso de las aplicaciones de .NET Core que utilizan el ensamblado System.Drawing.Common, también se
necesitará la dependencia siguiente:
libgdiplus (versión 6.0.1 o posterior)
WARNING
Puede instalar una versión reciente de libgdiplus agregando el repositorio Mono al sistema. Para obtener más
información, vea https://www.mono-project.com/download/stable/.
./dotnet-install.sh -c Current
Para instalar .NET Core Runtime en lugar del SDK, use el parámetro --runtime .
Para instalar una versión específica, modifique el parámetro -c para indicar la versión específica. El siguiente
comando instala el SDK de .NET Core 3.1.
./dotnet-install.sh -c 3.1
Instalación manual
Como alternativa a los administradores de paquetes, puede descargar e instalar manualmente el SDK y el entorno
de ejecución. La instalación manual se suele llevar a cabo durante las pruebas de integración continua o en
distribuciones de Linux no admitidas. Para un desarrollador o usuario, generalmente es mejor usar un
administrador de paquetes.
Si instala el SDK de .NET Core, no necesita instalar el entorno de ejecución correspondiente. En primer lugar,
descargue una versión binaria del SDK o del entorno de ejecución de uno de los siguientes sitios:
✔ Descargas de la versión preliminar de .NET 5.0
️
️ Descargas de .NET Core 3.1
✔
️ Descargas de .NET Core 2.1
✔
Todas las descargas de .NET Core
A continuación, extraiga el archivo descargado y use el comando export para establecer las variables que se
utilizan en .NET Core. Luego, asegúrese de que .NET Core esté en PATH.
Para extraer el entorno de ejecución y hacer que los comandos de la CLI de .NET Core estén disponibles en el
terminal, en primer lugar, descargue una versión binaria de .NET Core. Luego, abra un terminal y ejecute los
siguientes comandos desde el directorio donde se guardó el archivo. El nombre del archivo puede ser distinto en
función de lo que haya descargado.
Use el comando siguiente para extraer el entorno de ejecución :
TIP
Los comandos export anteriores solo hacen que los comandos de la CLI de .NET Core estén disponibles para la sesión de
terminal en la que se ha ejecutado.
Puede editar el perfil del shell para agregar los comandos de forma permanente. Hay una serie de shells distintos disponibles
para Linux, y cada uno de ellos tiene un perfil diferente. Por ejemplo:
Shell de Bash : ~/.bash_profile, ~/.bashrc
Shell de Korn : ~/.kshrc or .profile
Shell de Z : ~/.zshrc or .zprofile
Edite el archivo de origen adecuado para el shell y agregue :$HOME/dotnet al final de la instrucción PATH existente. Si no
se incluye ninguna instrucción PATH , agregue una nueva línea con export PATH=$PATH:$HOME/dotnet .
Además, agregue export DOTNET_ROOT=$HOME/dotnet al final del archivo.
Este enfoque le permite instalar diferentes versiones en ubicaciones independientes y elegir explícitamente cuál
usará cada aplicación.
Pasos siguientes
Tutorial: Creación de una aplicación de consola con el SDK de .NET Core mediante Visual Studio Code
Instalación del SDK o de .NET Core Runtime en SLES
16/09/2020 • 15 minutes to read • Edit Online
.NET Core es compatible con SLES. En este artículo se describe cómo instalar .NET Core en SLES.
Instale el SDK (que incluye el entorno de ejecución) si quiere desarrollar aplicaciones .NET. O bien, si solo necesita
ejecutar aplicaciones, instale el entorno de ejecución. Si va a instalar el entorno de ejecución, le recomendamos que
instale el entorno de ejecución de ASP.NET Core , ya que incluye los de .NET Core y ASP.NET Core.
Si ya ha instalado el SDK o el entorno de ejecución, use los comandos dotnet --list-sdks y
dotnet --list-runtimes para ver qué versiones están instaladas. Para más información, consulte Cómo comprobar
que .NET Core ya está instalado.
Distribuciones admitidas
En la tabla siguiente se muestra una lista de las versiones de .NET Core compatibles actualmente en SLES 12 SP 2 y
SLES 15. Estas versiones siguen siendo compatibles hasta que la versión de .NET Core llegue al final del soporte
técnico o ya no se admita la versión de SLES.
Una ✔
️ indica que todavía se admite la versión de SLES o de .NET Core.
Una ❌ indica que la versión de SLES o de .NET Core no se admite en esa versión de SLES.
Cuando una versión de SLES y una versión de .NET Core tienen una ✔
️ , se admite esa combinación de sistema
operativo y .NET.
VERSIÓ N P REL IM IN A R DE
. N ET 5 ( SO LO IN STA L A C IÓ N
SL ES . N ET C O RE 2. 1 . N ET C O RE 3. 1 M A N UA L )
️ 15
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
️ 12 SP2
✔ ️ 2.1
✔ ️ 3.1
✔ ️ 5.0 (versión preliminar)
✔
Las siguientes versiones de .NET Core ya no se admiten. aunque sus descargas siguen estando publicadas:
3.0
2.2
2.0
SLES 15 ✔
️
Antes de instalar .NET, ejecute los siguientes comandos para agregar la clave de la firma del paquete de Microsoft a
la lista de claves de confianza y agregar el repositorio de paquetes de Microsoft. Abra un terminal y ejecute los
comandos siguientes:
Actualmente, el paquete de instalación del repositorio de Microsoft de SLES 15 instala el archivo microsoft-
prod.repo en el directorio incorrecto, lo que impide que zypper pueda encontrar los paquetes de .NET Core. Para
corregir este problema, cree un symlink en el directorio apropiado.
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-2.1 en los comandos anteriores por
dotnet-runtime-3.1 .
SLES 12 ✔
️
.NET Core necesita SP2 como mínimo para la familia SLES 12.
Antes de instalar .NET, ejecute los siguientes comandos para agregar la clave de la firma del paquete de Microsoft a
la lista de claves de confianza y agregar el repositorio de paquetes de Microsoft. Abra un terminal y ejecute los
comandos siguientes:
Una alternativa al entorno de ejecución de ASP.NET Core es instalar la instancia de .NET Core Runtime que no
incluye compatibilidad con ASP.NET Core. Reemplace aspnetcore-runtime-2.1 en los comandos anteriores por
dotnet-runtime-3.1 .
Dependencias
Al realizar la instalación con un administrador de paquetes, estas bibliotecas se instalan automáticamente. Sin
embargo, si instala manualmente .NET Core o publica una aplicación independiente, deberá asegurarse de que
estas bibliotecas estén instaladas:
krb5
libicu
libopenssl1_1
Si la versión de OpenSSL del entorno de tiempo de ejecución de destino es 1.1 o más reciente, deberá instalar
compat-openssl10 .
Para obtener más información sobre las dependencias, vea Aplicaciones de Linux independientes.
En el caso de las aplicaciones de .NET Core que utilizan el ensamblado System.Drawing.Common, también se
necesitará la dependencia siguiente:
libgdiplus (versión 6.0.1 o posterior)
WARNING
Puede instalar una versión reciente de libgdiplus agregando el repositorio Mono al sistema. Para obtener más
información, vea https://www.mono-project.com/download/stable/.
./dotnet-install.sh -c Current
Para instalar .NET Core Runtime en lugar del SDK, use el parámetro --runtime .
Para instalar una versión específica, modifique el parámetro -c para indicar la versión específica. El siguiente
comando instala el SDK de .NET Core 3.1.
./dotnet-install.sh -c 3.1
Instalación manual
Como alternativa a los administradores de paquetes, puede descargar e instalar manualmente el SDK y el entorno
de ejecución. La instalación manual se suele llevar a cabo durante las pruebas de integración continua o en
distribuciones de Linux no admitidas. Para un desarrollador o usuario, generalmente es mejor usar un
administrador de paquetes.
Si instala el SDK de .NET Core, no necesita instalar el entorno de ejecución correspondiente. En primer lugar,
descargue una versión binaria del SDK o del entorno de ejecución de uno de los siguientes sitios:
✔ Descargas de la versión preliminar de .NET 5.0
️
️ Descargas de .NET Core 3.1
✔
️ Descargas de .NET Core 2.1
✔
Todas las descargas de .NET Core
A continuación, extraiga el archivo descargado y use el comando export para establecer las variables que se
utilizan en .NET Core. Luego, asegúrese de que .NET Core esté en PATH.
Para extraer el entorno de ejecución y hacer que los comandos de la CLI de .NET Core estén disponibles en el
terminal, en primer lugar, descargue una versión binaria de .NET Core. Luego, abra un terminal y ejecute los
siguientes comandos desde el directorio donde se guardó el archivo. El nombre del archivo puede ser distinto en
función de lo que haya descargado.
Use el comando siguiente para extraer el entorno de ejecución :
TIP
Los comandos export anteriores solo hacen que los comandos de la CLI de .NET Core estén disponibles para la sesión de
terminal en la que se ha ejecutado.
Puede editar el perfil del shell para agregar los comandos de forma permanente. Hay una serie de shells distintos disponibles
para Linux, y cada uno de ellos tiene un perfil diferente. Por ejemplo:
Shell de Bash : ~/.bash_profile, ~/.bashrc
Shell de Korn : ~/.kshrc or .profile
Shell de Z : ~/.zshrc or .zprofile
Edite el archivo de origen adecuado para el shell y agregue :$HOME/dotnet al final de la instrucción PATH existente. Si no
se incluye ninguna instrucción PATH , agregue una nueva línea con export PATH=$PATH:$HOME/dotnet .
Además, agregue export DOTNET_ROOT=$HOME/dotnet al final del archivo.
Este enfoque le permite instalar diferentes versiones en ubicaciones independientes y elegir explícitamente cuál
usará cada aplicación.
Pasos siguientes
Tutorial: Creación de una aplicación de consola con el SDK de .NET Core mediante Visual Studio Code
Cómo quitar los componentes .NET Core Runtime y
SDK
16/09/2020 • 12 minutes to read • Edit Online
Con el tiempo, a medida que instale versiones actualizadas del runtime y el SDK de .NET Core, es posible que
quiera quitar del equipo versiones obsoletas de .NET Core. Al quitar versiones anteriores de runtime puede
cambiar el runtime elegido para ejecutar aplicaciones de marco compartidas, tal como se detalla en el artículo
sobre selección de la versión de .NET Core.
Determinación de lo instalado
A partir de .NET Core 2.1, la CLI de .NET tiene opciones que puede utilizar para enumerar las versiones del SDK y
de runtime que están instaladas en el equipo. Use dotnet --list-sdks para ver la lista de los SDK instalados en el
equipo. Use dotnet --list-runtimes para ver la lista de los runtimes instalados en el equipo. Para más
información, consulte Cómo comprobar que .NET Core ya está instalado.
IMPORTANT
Para obtener información sobre las instalaciones y desinstalaciones de .NET Core en Red Hat, consulte la Guía de introducción
a Red Hat.
A partir de .NET Core 2.1, no hace falta desinstalar el SDK de .NET Core al actualizarlo mediante un administrador
de paquetes. Los comandos update o refresh del administrador de paquetes quitarán automáticamente la
versión anterior tras la instalación correcta de una versión más reciente.
Si ha instalado .NET Core con un administrador de paquetes, use ese mismo administrador de paquetes para
desinstalar el SDK o el runtime de .NET. Las instalaciones de .NET Core admiten los administradores de paquetes
más populares. Consulte la documentación del administrador de paquetes de su distribución para conocer la
sintaxis exacta en su entorno:
apt-get(8) se utiliza en sistemas basados en Debian, incluido Ubuntu.
yum(8) se utiliza en Fedora, CentOS y Oracle Linux.
zypper(8) se utiliza en openSUSE y SUSE Linux Enterprise Server (SLES).
DNF(8) se utiliza en Fedora.
En casi todos los casos, el comando para quitar un paquete es remove .
El nombre del paquete para la instalación del SDK de .NET Core para la mayoría de administradores de paquetes es
dotnet-sdk , seguido por el número de versión. A partir de la versión 2.1.300 del SDK de .NET Core y de la versión
2.1 del runtime, solo se requieren el primer y el segundo número de versión: por ejemplo, puede hacer referencia
a la versión 2.1.300 del SDK de .NET Core como el paquete dotnet-sdk-2.1 . Para las versiones anteriores se
requiere la cadena de versión completa: por ejemplo, se requeriría dotnet-sdk-2.1.200 para la versión 2.1.200 del
SDK de .NET Core.
En los equipos en los que solo se ha instalado el runtime, y no el SDK, el nombre del paquete es
dotnet-runtime-<version> para .NET Core Runtime y aspnetcore-runtime-<version> para la pila de runtime entera.
Al instalar las versiones de .NET Core anteriores a 2.0 no se desinstaló la aplicación host al desinstalar el SDK
mediante el administrador de paquetes. Al usar apt-get , el comando es:
version="1.0.1"
sudo rm -rf /usr/local/share/dotnet/sdk/$version
sudo rm -rf /usr/local/share/dotnet/shared/Microsoft.NETCore.App/$version
sudo rm -rf /usr/local/share/dotnet/shared/Microsoft.AspNetCore.All/$version
sudo rm -rf /usr/local/share/dotnet/shared/Microsoft.AspNetCore.App/$version
sudo rm -rf /usr/local/share/dotnet/host/fxr/$version
Los directorios primarios para el SDK y runtime se enumeran en el resultado de los comandos dotnet --list-sdks
y dotnet --list-runtimes , como se muestra en la tabla anterior.
En Mac, debe quitar los SDK y runtimes por separado, quitando los directorios con versiones. De esta manera, se
elimina el SDK y el runtime del disco. Por ejemplo, para quitar el runtime y el SDK 1.0.1, tendrá que usar los
siguientes comandos de bash:
version="1.0.1"
sudo rm -rf /usr/local/share/dotnet/sdk/$version
sudo rm -rf /usr/local/share/dotnet/shared/Microsoft.NETCore.App/$version
sudo rm -rf /usr/local/share/dotnet/shared/Microsoft.AspNetCore.All/$version
sudo rm -rf /usr/local/share/dotnet/shared/Microsoft.AspNetCore.App/$version
sudo rm -rf /usr/local/share/dotnet/host/fxr/$version
Los directorios primarios para el SDK y runtime se enumeran en el resultado de los comandos dotnet --list-sdks
y dotnet --list-runtimes , como se muestra en la tabla anterior.
Visual Studio 2019, versión 16.2 SDK de .NET Core 2.2.4xx, 2.1.8xx
Visual Studio 2019, versión 16.1 SDK de .NET Core 2.2.3xx, 2.1.7xx
Visual Studio 2019, versión 16.0 SDK de .NET Core 2.2.2xx, 2.1.6xx
Visual Studio 2017, versión 15.9 SDK de .NET Core 2.2.1xx, 2.1.5xx
A partir de la versión 16.3 de Visual Studio 2019, Visual Studio se encarga de su propia copia de la SDK de
.NET Core. Por ese motivo, ya no verá las versiones del SDK en el cuadro de diálogo Aplicaciones y
características .
.NET Core proporciona un sistema de plantillas que permite a los usuarios instalar o desinstalar plantillas de NuGet,
un archivo de paquete NuGet o un directorio del sistema de archivos. En este artículo se describe cómo administrar
plantillas de .NET Core a través de la CLI del SDK de .NET Core.
Para obtener más información sobre la creación de plantillas, consulte Tutorial: Creación de plantillas.
Instalación de plantillas
Las plantillas se instalan mediante el comando dotnet new del SDK con el parámetro -i . Puede proporcionar el
identificador de paquete NuGet de una plantilla o una carpeta que contenga los archivos de plantilla.
Paquete hospedado en NuGet
Las plantillas de la CLI de .NET se cargan en NuGet para una distribución amplia. Las plantillas también se pueden
instalar desde una fuente privada. En lugar de cargar una plantilla en una fuente de NuGet, los archivos de plantilla
de nupkg se pueden distribuir e instalar manualmente, tal como se describe en la sección Paquete NuGet local.
Para obtener más información sobre cómo configurar las fuentes de NuGet, vea dotnet nuget add source.
Para instalar un paquete de plantillas desde la fuente de NuGet predeterminada, use el comando
dotnet new -i {package-id} :
Para instalar un paquete de plantillas desde la fuente de NuGet predeterminada con una versión específica, use el
comando dotnet new -i {package-id}::{version} :
Carpeta
Como alternativa a la instalación de una plantilla desde un archivo nupkg, también puede instalar plantillas desde
una carpeta directamente con el comando dotnet new -i {folder-path} . La carpeta especificada se trata como el
identificador del paquete de plantillas para cualquier plantilla que se encuentre. Se instalarán todas las plantillas
que estén en la jerarquía de la carpeta especificada.
dotnet new -i c:\code\nuget-packages\some-folder\
La ruta {folder-path} especificada en el comando se convierte en el identificador del paquete de plantillas para
todas las plantillas que se encuentren. Tal como se especifica en la sección Enumeración de las plantillas, puede
obtener una lista de las plantillas instaladas con el comando dotnet new -u . En este ejemplo, el identificador del
paquete de plantillas se muestra como la carpeta que se ha usado para la instalación:
dotnet new -u
Template Instantiation Commands for .NET Core CLI
c:\code\nuget-packages\some-folder
Templates:
A Template Console Class (templateconsole) C#
Project for some technology (contosoproject) C#
Uninstall Command:
dotnet new -u c:\code\nuget-packages\some-folder
dotnet new -u
Template Instantiation Commands for .NET Core CLI
/home/username/code/templates
Templates:
A Template Console Class (templateconsole) C#
Project for some technology (contosoproject) C#
Uninstall Command:
dotnet new -u /home/username/code/templates
Desinstalación de plantillas
Las plantillas se desinstalan mediante el comando dotnet new del SDK con el parámetro -u . Puede proporcionar el
identificador de paquete NuGet de una plantilla o una carpeta que contenga los archivos de plantilla.
Detección de
Una vez instalado el paquete de plantillas de NuGet, ya sea desde una fuente de NuGet o un archivo nupkg, puede
desinstalarlo mediante una referencia al identificador de paquete NuGet.
Para desinstalar un paquete de plantillas, use el comando dotnet new -u {package-id} :
Carpeta
Cuando las plantillas se instalan a través de una ruta de acceso de carpeta, dicha ruta se convierte en el
identificador del paquete de plantillas.
Para desinstalar un paquete de plantillas, use el comando dotnet new -u {package-folder-path} :
dotnet new -u c:\code\nuget-packages\some-folder
Enumeración de plantillas
Al usar el comando de desinstalación estándar sin un identificador de paquete, puede ver una lista de las plantillas
instaladas, junto con el comando que desinstala cada plantilla.
dotnet new -u
Template Instantiation Commands for .NET Core CLI
c:\code\nuget-packages\some-folder
Templates:
A Template Console Class (templateconsole) C#
Project for some technology (contosoproject) C#
Uninstall Command:
dotnet new -u c:\code\nuget-packages\some-folder
Por ejemplo, el SDK de .NET Core incluye plantillas para una aplicación de consola que tiene como destino .NET
Core 2.1 y .NET Core 3.1. Si quiere que el destino sea .NET Core 3.0, deberá instalar las plantillas de 3.0.
1. Pruebe a crear una aplicación destinada a .NET Core 3.0.
No se pudo encontrar una plantilla instalada que coincida con la entrada. Buscando en línea una que
coincida…
Vea también
Tutorial: Creación de una plantilla de elemento.
dotnet new
dotnet nuget add source
Certificación de macOS Catalina y el impacto en las
descargas y proyectos de .NET Core
16/09/2020 • 9 minutes to read • Edit Online
A partir de macOS Catalina (versión 10.15), se debe conceder la certificación a todo el software creado después
del 1 de junio de 2019 y que se distribuya con el identificador de desarrollador. Este requisito se aplica al runtime
de .NET Core, al SDK de .NET Core y al software creado con .NET Core. En este artículo se describen los escenarios
comunes con los que puede encontrarse con la certificación de .NET Core y macOS.
<PropertyGroup>
<UseAppHost>true</UseAppHost>
</PropertyGroup>
Cuando se invoca una aplicación que usa appHost, la partición de certificado a la que accede la aplicación es
diferente del host predeterminado certificado. Si la aplicación tiene que acceder a los certificados instalados a
través del host predeterminado, use el comando dotnet run para ejecutarla desde su archivo del proyecto, o bien
use el comando dotnet <filename.dll> para iniciar la aplicación directamente.
En la sección ASP.NET Core y macOS y los certificados se proporciona más información sobre este escenario.
Para obtener más información sobre cómo solucionar problemas de certificados de ASP.NET Core, vea Aplicación
de HTTPS en ASP.NET Core.
Derechos predeterminados
El host predeterminado de .NET Core (el comando dotnet ) tiene un conjunto de derechos predeterminados.
Estos derechos son necesarios para el funcionamiento correcto de .NET Core. Es posible que la aplicación necesite
derechos adicionales, en cuyo caso tendrá que generar y usar un archivo appHost y, después, agregar los
derechos necesarios de forma local.
Conjunto predeterminado de derechos para .NET Core:
com.apple.security.cs.allow-jit
com.apple.security.cs.allow-unsigned-executable-memory
com.apple.security.cs.allow-dyld-environment-variables
com.apple.security.cs.disable-library-validation
Pasos siguientes
Dependencias y requisitos de .NET Core.
Instalación del runtime y SDK de .NET Core.
Cómo comprobar que .NET Core ya está instalado
16/09/2020 • 4 minutes to read • Edit Online
En este artículo se explica cómo comprobar las versiones del entorno de ejecución y el SDK de .NET Core que
están instaladas en el equipo. Es posible que .NET Core ya se haya instalado si tiene un entorno de desarrollo
integrado, como Visual Studio o Visual Studio para Mac.
Al instalar un SDK, se instala el entorno de ejecución correspondiente.
Si se produce un error en alguno de los comandos de este artículo, no tendrá instalado el entorno de
ejecución o el SDK. Para obtener más información, consulte los artículos de instalación para Windows,
macOS o Linux.
dotnet --list-sdks
2.1.500 [/home/user/dotnet/sdk]
2.1.502 [/home/user/dotnet/sdk]
2.1.504 [/home/user/dotnet/sdk]
2.1.600 [/home/user/dotnet/sdk]
2.1.602 [/home/user/dotnet/sdk]
2.2.101 [/home/user/dotnet/sdk]
3.0.100 [/home/user/dotnet/sdk]
3.1.100 [/home/user/dotnet/sdk]
2.1.500 [/usr/local/share/dotnet/sdk]
2.1.502 [/usr/local/share/dotnet/sdk]
2.1.504 [/usr/local/share/dotnet/sdk]
2.1.600 [/usr/local/share/dotnet/sdk]
2.1.602 [/usr/local/share/dotnet/sdk]
2.2.101 [/usr/local/share/dotnet/sdk]
3.0.100 [/usr/local/share/dotnet/sdk]
3.1.100 [/usr/local/share/dotnet/sdk]
dotnet --list-runtimes
Más información
Se pueden ver las versiones del SDK y del entorno de ejecución con el comando dotnet --info . También
obtendrá otra información relacionada con el entorno, como la versión del sistema operativo y el
identificador del entorno de ejecución (RID).
Pasos siguientes
Instalación del runtime y SDK de .NET Core.
Procedimiento para instalar archivos de IntelliSense
localizados para .NET Core
18/03/2020 • 7 minutes to read • Edit Online
IntelliSense es una característica que ayuda a completar código y que está disponible en diferentes entornos de
desarrollo integrado (IDE), como Visual Studio. De manera predeterminada, al desarrollar proyectos de .NET Core,
el SDK solo incluye la versión en inglés de los archivos de IntelliSense. En este artículo, se explica lo siguiente:
Procedimiento para instalar la versión localizada de estos archivos.
Procedimiento para modificar la instalación de Visual Studio para usar otro idioma.
Requisitos previos
SDK de .NET Core 3.0 o una versión posterior.
Versión 16.3 de Visual Studio 2019 u otra posterior.
T IP O DE SDK PAT H
c. Vaya a la versión para la que quiera instalar el archivo de IntelliSense localizado. Por ejemplo, la 3.1.0.
d. Abra la carpeta ref.
e. Abra la carpeta del moniker. Por ejemplo, netcoreapp3.1.
Así pues, la ruta de acceso completa tendrá un aspecto similar al siguiente: C:\Archivos de
programa\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1.
5. Cree una subcarpeta en la carpeta del moniker que acaba de abrir. El nombre de la carpeta indicará el idioma
que quiere usar. En la siguiente tabla, se especifican las diferentes opciones:
L EN GUA JE N O M B RE DE C A RP ETA
Francés fr
Alemán de
Italiano it
Japonés ja
Coreano ko
Ruso ru
Español es
6. Copie en esta nueva carpeta los archivos .xml que ha extraído en el paso 3. Los archivos .xml se mostrarán
agrupados según las diferentes carpetas de SDK, de modo que debe copiarlos en la del SDK que haya
elegido en el paso 4.
IMPORTANT
Para instalar, actualizar o modificar Visual Studio, debe iniciar sesión con una cuenta que tenga permisos de administrador.
Para obtener más información, consulte Permisos de usuario y Visual Studio.
Es posible que tenga que actualizar el instalador antes de continuar. De ser así, siga las indicaciones.
2. En el instalador, busque la edición de Visual Studio a la que quiera agregar el paquete de idioma y, luego,
elija Modificar .
IMPORTANT
Si no ve el botón Modificar , pero sí el botón Actualizar , significa que necesita actualizar su versión de Visual Studio
para poder modificar su instalación. Cuando haya finalizado la actualización, aparecerá el botón Modificar .
3. En la pestaña Paquetes de idioma , seleccione o anule la selección de los idiomas que quiera instalar o
desinstalar.
Vea también
IntelliSense en Visual Studio
Paseo por .NET
18/03/2020 • 18 minutes to read • Edit Online
.NET es una plataforma de desarrollo de uso general. Tiene varias características clave, como la compatibilidad con
varios lenguajes de programación, modelos de programación asincrónica y simultánea e interoperabilidad nativa,
que permiten una amplia variedad de escenarios en diversas plataformas.
En este artículo, se ofrece un paseo guiado por algunas de las características clave de .NET. Consulte el tema
Componentes de la arquitectura .NET para obtener más información sobre las piezas de arquitectura de .NET y para
qué se usan.
Lenguajes de programación
.NET admite varios lenguajes de programación. Las implementaciones de .NET implementan Common Language
Infrastructure (CLI), que, entre otras cosas, especifica un entorno de ejecución independiente del lenguaje y la
interoperabilidad del lenguaje. Esto significa que elige cualquier lenguaje .NET para crear aplicaciones y servicios en
.NET.
Microsoft desarrolla activamente y admite tres lenguajes .NET: C#, F# y Visual Basic.
C# es simple, eficaz, incluye seguridad de tipos y está orientado a objetos, al mismo tiempo que mantiene la
expresividad y elegancia de los lenguajes de estilo C. Cualquiera que esté familiarizado con C y lenguajes
similares, encuentra pocos problemas para adaptarse a C#. Consulte la Guía de C# para más información
sobre C#.
F# es un lenguaje de programación multiplataforma, principalmente funcional, que también admite la
programación tradicional imperativa y orientada en objetos. Consulte la Guía de F# para más información
sobre F#.
Visual Basic es un lenguaje fácil de aprender que se usa para crear una gran variedad de aplicaciones que se
ejecutan en .NET. Entré los lenguajes .NET, la sintaxis de Visual Basic es la más cercana al lenguaje humano
normal, lo que a menudo facilita el trabajo a las personas sin experiencia en el desarrollo de software.
using System.IO;
Cuando el bloque using se completa, el entorno de ejecución .NET llama automáticamente al método Dispose()
del objeto stream , que libera el identificador de archivo. El entorno de ejecución también sigue el mismo
procedimiento en caso de que una excepción provoque que el control abandone el bloque.
Para obtener más información, consulte los siguientes temas:
Para C#, vea el tema using (Instrucción, Referencia de C#).
En F#, consulte Administración de recursos: la palabra clave use.
Para VB, vea el tema Using (Instrucción, Visual Basic).
Seguridad de tipos
Un objeto es una instancia de un tipo específico. Las únicas operaciones permitidas para un objeto determinado
son las de su tipo. Un tipo Dog puede tener métodos Jump y WagTail , pero no un método SumTotal . Un
programa solo llama a los métodos que pertenecen a un tipo determinado. Todas las demás llamadas producirán
un error en tiempo de compilación o una excepción en tiempo de ejecución (en el caso de usar características
dinámicas u object ).
Los lenguajes .NET están orientados a objetos, con las jerarquías de clases base y derivadas. El entorno de ejecución
.NET solo permite llamadas y conversiones de objetos que se alineen con la jerarquía de objetos. Recuerde que
cada tipo definido en cualquier lenguaje .NET se deriva del tipo Object base.
La seguridad de tipos también se usa para ayudar a aplicar la encapsulación a través de la garantía de la fidelidad
de las palabras clave del descriptor de acceso. Las palabras clave del descriptor de acceso son artefactos que
controlan el acceso a los miembros de un tipo determinado a través de otro código. Normalmente se usan para
distintos tipos de datos dentro de un tipo, que se usan para administrar su comportamiento.
C#, Visual Basic y F# admiten inferencia de tipos local. La inferencia de tipos significa que el compilador deduce el
tipo de la expresión en el lado izquierdo a partir de la expresión en el lado derecho. Esto no significa que la
seguridad de tipos se divida o evite. El tipo resultante tiene un tipo seguro con todo lo que ello implica. En el
ejemplo anterior, se vuelve a escribir dog para introducir la inferencia de tipos, y el resto del ejemplo no se
modifica:
F# tiene incluso más funcionalidades de inferencia de tipos que la inferencia de tipos method-local encontrada en
C# y Visual Basic. Para obtener más información, consulte Inferencia de tipos.
Genéricos
Los genéricos permiten al programador introducir un parámetro de tipo al diseñar sus clases, que permite al
código de cliente (los usuarios del tipo) especificar el tipo exacto que se debe usar en lugar del parámetro de tipo.
Los genéricos se agregaron para ayudar a los programadores a implementar estructuras de datos genéricos. Antes
de su llegada, para que un tipo como List fuera genérico, habría que trabajar con elementos que fueran de tipo
object . Como consecuencia, había problemas de rendimiento y semántica, junto con posibles errores sutiles en
tiempo de ejecución. Un error en tiempo de ejecución común es cuando una estructura de datos contiene, por
ejemplo, enteros y cadenas, y se produce una excepción InvalidCastException al procesar los miembros de la lista.
En el siguiente ejemplo, se muestra una ejecución básica de programa mediante una instancia de tipos List<T>:
using System;
using System.Collections.Generic;
namespace GenericsSampleShort
{
public static void Main(string[] args)
{
// List<string> is the client way of specifying the actual type for the type parameter T
List<string> listOfStrings = new List<string> { "First", "Second", "Third" };
Para obtener más información, consulte el tema Información general (genéricos) de tipos genéricos.
Programación asincrónica
La programación asincrónica es un concepto de primera clase en .NET, con compatibilidad asincrónica en el
entorno de ejecución, las bibliotecas del marco y las construcciones de lenguaje .NET. Internamente, se basa en
objetos (como Task ) que sacan partido del sistema operativo para realizar trabajos dependientes de E/S de la
forma más eficaz posible.
Para obtener más información sobre la programación asincrónica en .NET, comience con el tema Async en
profundidad.
Interoperabilidad nativa
Cada sistema operativo incluye una interfaz de programación de aplicaciones (API) que proporciona servicios del
sistema. .NET proporciona varias maneras de llamar a esas API.
La manera principal de crear interoperabilidad nativa es a través de la "invocación de plataforma" o P/Invoke para
abreviar, que se admite en las plataformas Linux y Windows. Una manera de crear interoperabilidad nativa
exclusiva de Windows se conoce como "Interoperabilidad COM", que se usa para trabajar con componentes COM
en código administrado. Se basa en la infraestructura de P/Invoke, pero funciona de forma ligeramente diferente.
La mayoría de la compatibilidad de interoperabilidad de Mono (y, por tanto, de Xamarin) para Java y Objective-C se
compila de forma similar, es decir, usan los mismos principios.
Para obtener más información sobre la interoperabilidad nativa, consulte el artículo Interoperabilidad nativa.
Código no seguro
Según la compatibilidad con el lenguaje, CLR le permite tener acceso a memoria nativa y usar la aritmética de
punteros a través de código unsafe . Estas operaciones son necesarias para determinados algoritmos y para la
interoperabilidad del sistema. Aunque es eficaz, se desaconseja el uso de código no seguro a menos que sea
necesario para la interoperabilidad con las API del sistema o para implementar el algoritmo más eficaz. Es posible
que el código no seguro no se ejecute del mismo modo en entornos diferentes y que también pierda las ventajas
de un recolector de elementos no utilizados y de la seguridad de tipos. Se recomienda limitar y centralizar el código
no seguro lo máximo posible, y probar el código a conciencia.
El ejemplo siguiente es una versión modificada del método ToString() desde la clase StringBuilder . Ilustra cómo
mediante el código unsafe se puede implementar de forma eficiente un algoritmo desplazándose por los
fragmentos de memoria directamente:
Pasos siguientes
Si está interesado en un paseo por las características de C#, consulte Paseo por C#.
Si está interesado en un paseo por las características de F#, consulte Paseo por F#.
Si quiere empezar a escribir su propio código, consulte Introducción.
Para más información sobre los principales componentes de. NET, consulte Componentes de la arquitectura .NET.
Componentes de la arquitectura .NET
16/09/2020 • 10 minutes to read • Edit Online
Una aplicación de .NET se desarrolla y se ejecuta en una o varias implementaciones de .NET. Las implementaciones
de .NET incluyen .NET Framework, .NET Core y Mono. Hay una especificación de API común a todas las
implementaciones de .NET que se denomina .NET Standard. En este artículo, se ofrece una breve introducción a
cada uno de estos conceptos.
.NET Standard
.NET Standard es un conjunto de API que se implementan mediante la biblioteca de clases base de una
implementación de .NET. Más formalmente, es una especificación de API de .NET que constituyen un conjunto
uniforme de contratos contra los que se compila el código. Estos contratos se implementan en cada
implementación de .NET. Esto permite la portabilidad entre diferentes implementaciones de .NET, de forma que el
código se puede ejecutar en cualquier parte.
.NET Standard es también una plataforma de destino. Si el código tiene como destino una versión de .NET
Standard, se puede ejecutar en cualquier implementación de .NET que sea compatible con esa versión de .NET
Standard.
Para obtener más información sobre .NET Standard y cómo tenerlo como destino, consulte .NET Standard.
Implementaciones de .NET
Cada implementación de .NET incluye los siguientes componentes:
Uno o varios entornos de ejecución. Ejemplos: CLR para .NET Framework, CoreCLR y CoreRT para .NET Core.
Una biblioteca de clases que implementa .NET Standard y puede implementar API adicionales. Ejemplos:
biblioteca de clases base de .NET Framework, biblioteca de clases base de .NET Core.
Opcionalmente, uno o varios marcos de trabajo de la aplicación. Ejemplos: ASP.NET, Windows Forms y
Windows Presentation Foundation (WPF) se incluyen en .NET Framework y .NET Core.
Opcionalmente, herramientas de desarrollo. Algunas herramientas de desarrollo se comparten entre varias
implementaciones.
Hay cuatro implementaciones principales de .NET que Microsoft desarrolla y mantiene activamente: .NET Core,
.NET Framework, Mono y UWP.
.NET Core
.NET Core es una implementación multiplataforma de .NET diseñada para controlar cargas de trabajo de servidor
y en la nube a escala. Se ejecuta en Windows, macOS y Linux. Implementa .NET Standard, de forma que cualquier
código que tenga como destino .NET Standard se puede ejecutar en .NET Core. ASP.NET Core, Windows Forms y
Windows Presentation Foundation (WPF) se ejecutan todos en .NET Core.
Para obtener más información sobre .NET Core, consulte Guía de .NET Core y Selección entre .NET Core y
.NET Framework para aplicaciones de servidor.
.NET Framework
.NET Framework es la implementación de .NET original que existe desde 2002. Las versiones 4.5 y posteriores
implementan .NET Standard, de forma que el código que tiene como destino .NET Standard se puede ejecutar en
esas versiones de .NET Framework. Contiene API específicas de Windows adicionales, como API para el desarrollo
de escritorio de Windows con Windows Forms y WPF. .NET Framework está optimizado para crear aplicaciones de
escritorio de Windows.
Para más información sobre .NET Framework, consulte la Guía de .NET Framework.
Mono
Mono es una implementación de .NET que se usa principalmente cuando se requiere un entorno de ejecución
pequeño. Es el entorno de ejecución que activa las aplicaciones Xamarin en Android, macOS, iOS, tvOS y watchOS,
y se centra principalmente en una superficie pequeña. Mono también proporciona juegos creados con el motor de
Unity.
Admite todas las versiones de .NET Standard publicadas actualmente.
Históricamente, Mono implementaba la API de .NET Framework más grande y emulaba algunas de las funciones
más populares en Unix. A veces, se usa para ejecutar aplicaciones de .NET que se basan en estas capacidades en
Unix.
Mono se suele usar con un compilador Just-In-Time, pero también incluye un compilador estático completo
(compilación Ahead Of Time) que se usa en plataformas como iOS.
Para más información sobre Mono, consulte la documentación de Mono.
Plataforma universal de Windows (UWP)
UWP es una implementación de .NET que se usa para compilar aplicaciones Windows modernas y táctiles y
software para Internet de las cosas (IoT). Se ha diseñado para unificar los diferentes tipos de dispositivos de
destino, incluidos equipos, tabletas, teléfonos e incluso la consola Xbox. UWP proporciona muchos servicios, como
una tienda de aplicaciones centralizada, un entorno de ejecución (AppContainer) y un conjunto de API de
Windows para usar en lugar de Win32 (WinRT). Pueden escribirse aplicaciones en C++, C#, Visual Basic y
JavaScript. Al usar C# y Visual Basic, .NET Core proporciona las API de .NET.
Para obtener más información sobre UWP, vea Introducción a la Plataforma universal de Windows.
Estándares aplicables
El lenguaje C# y las especificaciones de Common Language Infrastructure (CLI) se normalizan a través de Ecma
International®. Las primeras ediciones de estos estándares las publicó ECMA en diciembre de 2001.
Las revisiones posteriores de los estándares las han desarrollado los grupos de tareas TC49-TG2 (C#) y TC49-TG3
(CLI) en el Comité Técnico de Lenguajes de Programación (TC49) y adoptadas por la Asamblea general de ECMA y,
posteriormente, por ISO/IEC JTC 1 a través del proceso Fast-Track de ISO.
Estándares más recientes
Los siguientes documentos oficiales de ECMA están disponibles para C# y la CLI (TR-84):
El estándar del lenguaje C# (versión 5.0) : ECMA-334.pdf
Common Language Infrastructure : disponible en los formatos pdf y zip.
Información derivada del archivo XML de la par te IV : disponible en los formatos pdf y zip.
Los documentos ISO/IEC oficiales están disponibles en la página ISO/IEC Estándares disponibles públicamente.
Estos vínculos son directos de esa página:
Tecnología de la información: lenguajes de programación, C# : ISO/IEC 23270:2018
Tecnologías de la información: Common Language Infrastructure (CLI), par tes I a VI : ISO/IEC
23271:2012
Tecnología de la información: Common Language Infrastructure (CLI); informe técnico sobre la
información derivada del archivo XML de la par te IV : ISO/IEC TR 23272:2011
Vea también
Selección entre .NET Core y .NET Framework para aplicaciones de servidor
Introducción a .NET Standard
Introducción a .NET Core
Guía de .NET Framework
Guía de C#
Guía de F#
Guía de Visual Basic
Bibliotecas de clases de .NET
16/09/2020 • 7 minutes to read • Edit Online
Las bibliotecas de clases son el concepto de biblioteca compartida de .NET. Le permiten dividir funcionalidades
útiles en módulos que pueden usar varias aplicaciones. También se pueden usar para cargar la funcionalidad no
necesaria o no conocida al inicio de la aplicación. Las bibliotecas de clases se describen mediante el formato de
archivo de Ensamblado de .NET.
Hay tres tipos de bibliotecas de clases que puede usar:
Las bibliotecas de clases específicas de la plataforma tienen acceso a todas las API de una plataforma
determinada (por ejemplo, .NET Framework, Xamarin iOS), pero solo las pueden usar las aplicaciones y
bibliotecas destinadas a esa plataforma.
Las bibliotecas de clases por tables tienen acceso a un subconjunto de API y las pueden usar las aplicaciones y
bibliotecas que tienen como destino varias plataformas.
Las bibliotecas de clases de .NET Standard son una fusión del concepto de biblioteca específica de la
plataforma y portable en un único modelo que ofrece lo mejor de ambas.
.NET Core es una plataforma de desarrollo de código abierto para uso general. Puede crear aplicaciones de
.NET Core para Windows, macOS y Linux para procesadores x64, x86, ARM32 y ARM64 mediante varios lenguajes
de programación. Se proporcionan marcos y API para la nube, IoT, la Interfaz de usuario de cliente y el aprendizaje
automático.
Descargue el SDK de .NET Core para probar .NET Core en el equipo. La última versión es .NET Core 3.1.
Hello World!
Contribuir
.NET Core es una plataforma abierta. Todo el mundo puede participar.
Registre las incidencias y preguntas sobre el producto en Developer Community.
Las contribuciones al producto se deben realizar en uno de los repositorios del proyecto, como dotnet/runtime,
dotnet/sdk, dotnet/rosyln o aspnetcore. Para obtener más información, vea Repositorios de .NET Core.
Soporte técnico
.NET Core es compatible con Microsoft en Windows, macOS y Linux, y con Red Hat en Red Hat Enterprise Linux.
Pasos siguientes
Tutoriales de .NET Core
Prueba de .NET Core en el explorador
.NET Standard
16/09/2020 • 21 minutes to read • Edit Online
.NET Standard es una especificación formal de las API de .NET que se prevé que estén disponibles en todas las
implementaciones de .NET. La finalidad de .NET Standard es establecer una mayor uniformidad en el
ecosistema de .NET. ECMA 335 sigue estableciendo uniformidad para el comportamiento de la
implementación de .NET y, aunque ECMA 335 especifica un pequeño conjunto de bibliotecas estándar, la
especificación de .NET Standard abarca una gama más amplia de API de .NET.
.NET Standard habilita los escenarios clave siguientes:
Define un conjunto uniforme de API de BCL para todas las implementaciones de .NET que se van a
implementar, independientemente de la carga de trabajo.
Permite a los desarrolladores generar bibliotecas portátiles que se pueden usar en las implementaciones
de .NET con este mismo conjunto de API.
Reduce o incluso elimina la compilación condicional de código fuente compartido debido a las API de .NET,
solo para API de sistema operativo.
Las diversas implementaciones de .NET tienen como destino versiones concretas de .NET Standard. Cada
implementación de .NET anuncia la última versión más alta de .NET Standard que admite, indicación de que
también es compatible con versiones anteriores. Por ejemplo, .NET Framework 4.6 implementa .NET Standard
1.3, lo que significa que expone todas las API definidas en las versiones de .NET Standard de 1.0 a 1.3. De
forma similar, .NET Framework 4.6.1 implementa .NET Standard 1.4, mientras que .NET Core 1.0 implementa
.NET Standard 1.6.
. N ET
STA N D
A RD 1. 0 1. 1 1. 2 1. 3 1. 4 1. 5 1. 6 2. 0 2. 1
.NET 1.0 1.0 1.0 1.0 1.0 1.0 1.0 2.0 3.0
Core
.NET 4.5 4.5 4.5.1 4.6 4.6.1 4.6.1 2 4.6.1 2 4.6.1 2 N/A3
Framew
ork 1
Mono 4.6 4.6 4.6 4.6 4.6 4.6 4.6 5.4 6.4
Xamarin 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.14 12.16
.iOS
Xamarin 3.0 3.0 3.0 3.0 3.0 3.0 3.0 3.8 5.16
.Mac
. N ET
STA N D
A RD 1. 0 1. 1 1. 2 1. 3 1. 4 1. 5 1. 6 2. 0 2. 1
Xamarin 7.0 7.0 7.0 7.0 7.0 7.0 7.0 8.0 10.0
.Androi
d
Platafor 10.0 10.0 10.0 10.0 10.0 10.0.16 10.0.16 10.0.16 TBD
ma 299 299 299
univers
al de
Window
s
Unity 2018.1 2018.1 2018.1 2018.1 2018.1 2018.1 2018.1 2018.1 TBD
1 Las versiones que se muestran de .NET Framework se aplican al SDK de .NET Core 2.0 y versiones posteriores de la herramienta. Las versiones
anteriores usaban una asignación diferente para .NET Standard 1.5 y versiones posteriores. Puede descargar herramientas para .NET Core para
Visual Studio 2015 si no se puede actualizar a Visual Studio 2017 ni a una versión posterior.
2 Las versiones siguientes representan las reglas que usa NuGet para determinar si una determinada biblioteca de .NET Standard es aplicable.
Aunque NuGet considera a .NET Framework 4.6.1 compatible con .NET Standard (versiones 1.5 a 2.0) hay varios problemas con el consumo de
bibliotecas de .NET Standard que se compilaron para esas versiones desde proyectos de .NET Framework 4.6.1. Para los proyectos de .NET
Framework que tengan que usar estas bibliotecas, se recomienda actualizar el proyecto para destinarlo a .NET Framework 4.7.2 o una versión
posterior.
3 .NET Framework no admitirá .NET Standard 2.1 o versiones posteriores. Para más detalles, vea el anuncio de .NET Standard 2.1.
Las columnas representan las versiones de .NET Standard. Cada celda de encabezado es un vínculo a un
documento que muestra qué API se han agregado en esa versión de .NET Standard.
Las filas representan las diferentes implementaciones de .NET.
El número de versión de cada celda indica la versión mínima de la implementación que necesitará para
tener como destino dicha versión de .NET Standard.
Para ver una tabla interactiva, consulte Versiones de .NET Standard.
Para encontrar la versión más reciente de .NET Standard que puede usar como destino, haga lo siguiente:
1. Busque la fila en la que se indica la implementación de .NET en la que quiere realizar la ejecución.
2. Busque la columna de esa fila que indica la versión de derecha a izquierda.
3. El encabezado de columna indica la versión de .NET Standard que admite el destino. También se puede
seleccionar como destino cualquier versión anterior de .NET Standard. Las versiones superiores de .NET
Standard también serán compatibles con la implementación.
4. Repita este proceso para cada plataforma a la que quiera dirigirse. Si tiene más de una plataforma de
destino, debe elegir la versión más baja. Por ejemplo, si quiere ejecutar en .NET Framework 4.5 y .NET Core
1.0, la versión de .NET Standard más alta que puede usar es .NET Standard 1.1.
Versión de .NET Standard de destino
Al elegir una versión de .NET Standard, debe tener en cuenta lo siguiente:
Cuanto mayor sea la versión, más API tendrá disponibles.
Cuanto menor sea la versión, más plataformas la implementarán.
En general, se recomienda elegir como destino la versión menor de .NET Standard posible. Así, después de
buscar la versión de .NET Standard superior que puede elegir como destino, siga estos pasos:
1. Diríjase a la siguiente versión menor de .NET Standard y cree el proyecto.
2. Si el proyecto se crea correctamente, repita el paso 1. De lo contrario, vuelva a dirigirse a la siguiente
versión mayor, que es la que debe usar.
Sin embargo, el establecimiento como destino de las versiones inferiores de .NET Standard introduce
diferentes dependencias de compatibilidad. Si el proyecto establece como destino .NET Standard 1.x, le
recomendamos que también establezca .NET Standard 2.0 como destino. Esto simplifica el gráfico de
dependencias para los usuarios de la biblioteca que se ejecutan en los marcos compatibles de .NET Standard
2.0 y reduce el número de paquetes que necesitan descargar.
Reglas de control de versiones de .NET Standard
Hay dos reglas de control de versiones principales:
Adición: las versiones de .NET Standard son círculos lógicamente concéntricos: las versiones superiores
incorporan todas las API de las versiones anteriores. No hay ningún cambio importante entre versiones.
Inmutable: Una vez publicadas, las versiones de .NET Standard se congelan. Las nuevas API primero están
disponibles en implementaciones de .NET concretas, como .NET Core. Si el consejo de revisión de .NET
Standard cree que las nuevas API deben estar disponibles para todas las implementaciones de .NET, se
agregan en una nueva versión de .NET Standard.
Especificación
La especificación de .NET Standard es un conjunto estandarizado de API. La especificación se mantiene
mediante implementadores de .NET, específicamente Microsoft (que incluye .NET Framework, .NET Core y
Mono) y Unity. Se usa un proceso de comentarios públicos como parte del establecimiento de las versiones
nuevas de .NET Standard a través de GitHub.
Artefactos oficiales
La especificación oficial es un conjunto de archivos .cs que definen las API que forman parte del estándar. El
directorio ref en el repositorio dotnet/standard define las API de .NET Standard.
El metapaquete NETStandard.Library (código fuente) describe el conjunto de bibliotecas que definen (en
parte) una o varias versiones de .NET Standard.
Un componente determinado, como System.Runtime , describe lo siguiente:
Parte de .NET Standard (solo su ámbito).
Varias versiones de .NET Standard para ese ámbito.
Se proporcionan artefactos derivados para permitir una lectura más cómoda y habilitar ciertos escenarios de
desarrollo (por ejemplo, el uso de un compilador).
Lista de API en Markdown
Ensamblados de referencia, distribuidos como paquetes NuGet y a los que hace referencia el metapaquete
NETStandard.Library.
Representación de paquetes
El principal vehículo de distribución de los ensamblados de referencia de .NET Standard son los paquetes
NuGet. Las implementaciones se entregarán de diversas formas, adecuadas para cada implementación de
.NET.
Los paquetes NuGet tienen como destino uno o varios marcos. Los paquetes de .NET Standard tienen como
destino el marco de trabajo ".NET Standard". Puede establecer como destino el marco de .NET Standard
mediante el TFM compacto netstandard (por ejemplo, netstandard1.4 ). Las bibliotecas diseñadas para
ejecutarse en varios entornos de ejecución deben tener como destino este marco de trabajo. Para obtener el
conjunto más amplio de API, indique netstandard2.0 como destino, puesto que el número de API disponibles
se ha doblado entre .NET Standard 1.6 y 2.0.
El metapaquete NETStandard.Library hace referencia al conjunto completo de paquetes NuGet que definen
.NET Standard. La manera más común de establecer como destino netstandard consiste en hacer referencia a
este metapaquete. Describe y proporciona acceso a las aproximadamente 40 bibliotecas de .NET y las API
asociadas que definen .NET Standard. Puede hacer referencia a paquetes adicionales que tienen como destino
netstandard para obtener acceso a otras API.
Control de versiones
La especificación no es única, sino que se trata de un conjunto de API con versiones lineales y con un
crecimiento incremental. La primera versión del estándar establece un conjunto básico de API. Las versiones
posteriores agregan API y heredan las API definidas por las versiones anteriores. No se ha establecido
ninguna disposición para quitar API del estándar.
.NET Standard no es específico de ninguna implementación de .NET ni coincide con el esquema de control de
versiones de ningún entorno de ejecución.
Las API agregadas a cualquier implementación de .NET (por ejemplo, .NET Framework, .NET Core y Mono)
pueden considerarse candidatas para agregarse a la especificación, sobre todo si se consideran
fundamentales por su naturaleza. Las nuevas versiones de .NET Standard se crean en función de las versiones
de la implementación de .NET, lo que permite establecer como destino nuevas API desde una Biblioteca de
clases portable (PLC) de .NET Standard. Los mecanismos de control de versiones se describen con más detalle
en Control de versiones de .NET Core.
El control de versiones de .NET Standard es importante para su uso. Dada una versión de .NET Standard,
puede usar bibliotecas que tengan como destino esa misma versión o una inferior. En el enfoque siguiente se
describe el flujo de trabajo para el uso de PCL de .NET Standard específicas para tener como destino .NET
Standard.
Seleccione una versión de .NET Standard para usarla para la PCL.
Use bibliotecas que dependan de la misma versión de .NET Standard o de una inferior.
Si encuentra una biblioteca que depende de una versión superior de .NET Standard, deberá adoptar la
misma versión o bien optar por no usar esa biblioteca.
Vea también
Versiones de .NET Standard
Compilación de una biblioteca de .NET Standard
Destinatarios multiplataforma
Glosario de .NET
16/09/2020 • 26 minutes to read • Edit Online
El objetivo principal de este glosario es aclarar los significados de algunos de los términos y acrónimos que
aparecen frecuentemente en la documentación de .NET sin definiciones.
AOT
Compilador Ahead Of Time.
Similar a JIT, este compilador también convierte IL en código de máquina. A diferencia de la compilación JIT, la
compilación AOT ocurre antes de que la aplicación se ejecute y, normalmente, se realiza en un equipo diferente.
Dado que las cadenas de la herramienta de AOT no se compilan en tiempo de ejecución, no tienen que minimizar el
tiempo dedicado a compilar. Esto significa que pueden dedicar más tiempo a la optimización. Puesto que el
contexto de AOT es toda la aplicación, el compilador AOT también realiza vinculación entre módulos y el análisis de
todo el programa, lo que significa que se siguen todas las referencias y se genera un archivo ejecutable único.
Vea CoreRT y .NET Native.
ASP.NET
La implementación original de ASP.NET que se distribuye con .NET Framework.
A veces, ASP.NET es un término genérico que hace referencia a ambas implementaciones de ASP.NET, incluido
ASP.NET Core. El significado que lleva el término en una instancia específica se determina según el contexto. Haga
referencia a ASP.NET 4.x cuando desee dejar claro que no usa ASP.NET para indicar ambas implementaciones.
Vea la documentación de ASP.NET.
ASP.NET Core
Implementación multiplataforma, de alto rendimiento y de código abierto de ASP.NET.
Vea la documentación de ASP.NET Core.
ensamblado
Un archivo .dll/ .exe que puede contener una colección de API a la que puede llamarse mediante aplicaciones u
otros ensamblados.
Un ensamblado puede incluir tipos como interfaces, clases, estructuras, enumeraciones y delegados. A veces, se
hace referencia a los ensamblados de la carpeta bin de un proyecto como archivos binarios. Vea también biblioteca.
BCL
Biblioteca de clases base. También se conoce como biblioteca de marco.
Un conjunto de bibliotecas que conforman los espacios de nombres de System.* (y hasta cierto punto Microsoft.*).
BCL es un marco de nivel inferior de uso general donde se compilan marcos de trabajo de la aplicación de nivel
superior, como ASP.NET Core.
El código fuente de la BCL para .NET 5 (y .NET Core) y versiones posteriores se encuentra en el repositorio del
entorno de ejecución de .NET. La mayoría de las API de BCL para esta implementación más reciente de .NET
también están disponibles en .NET Framework, por lo que puede considerar este código fuente como una
bifurcación del código fuente de la BCL de .NET Framework.
CLR
Common Language Runtime.
El significado exacto depende del contexto. Common Language Runtime normalmente hace referencia al entorno
de ejecución de .NET Framework o de .NET 5 (y .NET Core) y versiones posteriores.
CLR controla la asignación y administración de memoria. CLR es también una máquina virtual que no solo ejecuta
aplicaciones, sino que también genera y compila código sobre la marcha mediante un compilador JIT.
La implementación de CLR para .NET Framework es solo para Windows.
La implementación de CLR para .NET 5 y versiones posteriores (también conocida como CoreCLR) se crea a partir
del mismo código base que el CLR de .NET Framework. Originalmente, CoreCLR era el entorno de ejecución de
Silverlight y estaba diseñado para ejecutarse en varias plataformas, concretamente Windows y OS X. Sigue siendo
un entorno de ejecución multiplataforma y ahora incluye compatibilidad con muchas distribuciones de Linux.
Vea también entorno de ejecución.
CoreCLR
Common Language Runtime para .NET 5 (y .NET Core) y versiones posteriores.
Vea CLR.
CoreRT
A diferencia de CLR, CoreRT no es una máquina virtual, lo que significa que no incluye las funciones para generar y
ejecutar código sobre la marcha porque no incluye un compilador JIT. En cambio, incluye GC, reflexión y capacidad
de identificación del tipo en tiempo de ejecución (RTTI). Con todo, su sistema de tipos está diseñado para que no
sean necesarios los metadatos para la reflexión. No requerir los metadatos permite tener una cadena de
herramientas de AOT que puede vincular metadatos superfluos y (más importante) identificar código que no usa la
aplicación. CoreRT está en desarrollo.
Consulte Introducción a .NET Native u CoreRT.
multiplataforma
La capacidad para desarrollar y ejecutar una aplicación que se puede usar en varios sistemas operativos diferentes,
como Linux, Windows e iOS, sin tener que volver a escribir específicamente para cada uno de ellos. Esto permite
reutilizar el código y posibilita la coherencia entre aplicaciones en distintas plataformas.
ecosistema
Todo el software en tiempo de ejecución, las herramientas de desarrollo y los recursos de la comunidad que se
usan para compilar y ejecutar aplicaciones de una tecnología determinada.
El término "ecosistema de .NET" se diferencia de términos parecidos como "pila de .NET" en que incluye bibliotecas
y aplicaciones de terceros. Aquí se muestra un ejemplo en una frase:
"La finalidad de .NET Standard es establecer una mayor uniformidad en el ecosistema de .NET".
marco de trabajo
En general, una colección completa de API que facilita el desarrollo y la implementación de aplicaciones que se
basan en una tecnología concreta. En este sentido general, ASP.NET Core y Windows Forms son ejemplos de
marcos de trabajo de la aplicación. Vea también biblioteca.
Los siguientes términos tienen un significado diferente:
.NET Framework
Plataforma de destino
TFM (moniker de la plataforma de destino)
Aplicación dependiente de la plataforma
En la documentación de .NET heredada, "marco de trabajo" a veces hace referencia a una implementación de .NET.
Por ejemplo, un artículo puede llamar marco de trabajo a .NET 5.
GC
Recolector de elementos no utilizados.
El recolector de elementos no utilizados es una implementación de administración de memoria automática. GC
libera la memoria ocupada por objetos que ya no se usan.
Vea Recolección de elementos no utilizados.
IL
Lenguaje intermedio.
Los lenguajes .NET de nivel alto, como C#, compilan en un conjunto de instrucciones independiente del hardware,
lo que se denomina lenguaje intermedio (IL). A veces, se hace referencia al IL como MSIL (IL de Microsoft) o CIL
(Common IL).
JIT
Compilador Just-In-Time.
Similar a AOT, este compilador convierte el IL en código de máquina que entienda el procesador. A diferencia de
AOT, la compilación JIT se produce a petición y se lleva a cabo en el mismo equipo en que debe ejecutarse el
código. Puesto que la compilación JIT tiene lugar durante la ejecución de la aplicación, el tiempo de compilación es
parte del tiempo de ejecución. Por tanto, los compiladores JIT tienen que compensar el tiempo invertido en
optimizar el código con el ahorro que puede generar el código resultante. Pero un JIT conoce el hardware real y
puede liberar a los desarrolladores de tener que enviar diferentes implementaciones.
implementación de .NET
Una implementación de .NET incluye lo siguiente:
Uno o varios entornos de ejecución. Ejemplos: CLR y CoreRT.
Una biblioteca de clases que implementa una versión de .NET Standard y puede incluir API adicionales.
Ejemplos: BCL para .NET Framework y para .NET 5 (y .NET Core) y versiones posteriores.
Opcionalmente, uno o varios marcos de trabajo de la aplicación. Ejemplos: ASP.NET, Windows Forms y WPF se
incluyen en .NET Framework y .NET 5.
Opcionalmente, herramientas de desarrollo. Algunas herramientas de desarrollo se comparten entre varias
implementaciones.
Ejemplos de implementaciones de .NET:
.NET Framework
.NET 5 y versiones posteriores (incluido .NET Core 2.1-3.1
Plataforma universal de Windows (UWP)
Mono
biblioteca
Una colección de API que pueden llamarse mediante aplicaciones u otras bibliotecas. Una biblioteca de .NET se
compone de uno o varios ensamblados.
Las palabras biblioteca y marco de trabajo se usan a menudo como sinónimos.
Mono
Mono es una implementación de .NET multiplataforma y de código abierto que se usa principalmente cuando se
requiere un entorno de ejecución pequeño. Es el entorno de ejecución que activa las aplicaciones de Xamarin en
Android, Mac, iOS, tvOS y watchOS, y se centra principalmente en aplicaciones que requieren una superficie
pequeña.
Admite todas las versiones de .NET Standard publicadas actualmente.
Históricamente, Mono implementaba la API de .NET Framework más grande y emulaba algunas de las funciones
más populares en Unix. A veces, se usa para ejecutar aplicaciones de .NET que se basan en estas capacidades en
Unix.
Mono se suele usar con un compilador Just-In-Time, pero también incluye un compilador estático completo
(compilación Ahead Of Time) que se usa en plataformas como iOS.
Consulte la documentación de Mono.
.NET
El término que engloba .NET Standard y todas las cargas de trabajo e implementaciones de .NET. Siempre
totalmente en mayúsculas, nunca ".Net".
Cuando se publique .NET 5 (actualmente en versión preliminar), será la implementación de .NET recomendada para
todo el nuevo desarrollo de .NET, por lo que en algunos contextos ".NET" implicará ".NET 5 y versiones posteriores".
Vea Aspectos básicos de .NET.
CLI de .NET
Una cadena de herramientas multiplataforma para desarrollar aplicaciones y bibliotecas para .NET 5 (y .NET Core) y
versiones posteriores. También conocida como la CLI de .NET Core.
Vea CLI de .NET.
.NET Core
Vea .NET 5 y versiones posteriores.
.NET Framework
Una implementación de .NET que se ejecuta solo en Windows. Incluye Common Language Runtime (CLR), la
biblioteca de clases base (BCL) y las bibliotecas de marco de trabajo de la aplicación, como ASP.NET,
Windows Forms y WPF.
Vea Guía de .NET Framework.
.NET Native
Cadena de herramientas del compilador que genera código nativo Ahead Of Time (AOT), en lugar de Just-In-Time
(JIT).
La compilación se produce en el equipo del desarrollador, de forma similar a cómo funcionan el compilador y el
enlazador de C++. Quita el código que no se usa y emplea más tiempo en optimizarlo. Extrae código de bibliotecas
y lo combina en el archivo ejecutable. El resultado es un módulo único que representa toda la aplicación.
UWP fue el primer marco de trabajo de la aplicación compatible con .NET Native. Ahora, se admite la compilación
de aplicaciones de consola nativas para Windows, macOS y Linux.
Vea Intro to .NET Native and CoreRT (Introducción a .NET Native y CoreRT).
.NET SDK
Conjunto de bibliotecas y herramientas que permiten a los desarrolladores crear aplicaciones y bibliotecas de .NET
para .NET 5 (y .NET Core) y versiones posteriores. También se conoce como el SDK de .NET Core.
Incluye la CLI de .NET para compilar aplicaciones, el entorno de ejecución, y las bibliotecas de .NET para compilar y
ejecutar aplicaciones, y el ejecutable dotnet (dotnet.exe) que ejecuta comandos de la CLI y ejecuta aplicaciones.
Vea Información general sobre el SDK de .NET.
.NET Standard
Una especificación formal de las API de .NET que están disponibles en cada implementación de .NET.
La especificación de .NET Standard a veces se denomina "biblioteca" en la documentación. Dado que una biblioteca
incluye implementaciones de API, no solo especificaciones (interfaces), es confuso denominar "biblioteca" a .NET
Standard.
Vea .NET Standard.
NGEN
Generación (de imágenes) nativas.
Esta tecnología se puede considerar como un compilador JIT persistente. Normalmente, compila código en el
equipo en que se ejecuta el código, pero la compilación se suele producir durante la instalación.
paquete
Un paquete de NuGet — o simplemente un paquete — es un archivo .zip con uno o varios ensamblados del mismo
nombre junto con metadatos adicionales, como el nombre del autor.
El archivo .zip tiene una extensión .nupkg y puede contener recursos (como archivos .dll y .xml) para usar con varios
marcos de destino y versiones. Cuando se instala en una aplicación o biblioteca, se seleccionan los recursos
adecuados en función de la plataforma de destino especificada por la aplicación o biblioteca. Los recursos que
definen la interfaz se encuentran en la carpeta ref y los recursos que definen la implementación se encuentran en la
carpeta lib.
platform
Un sistema operativo y el hardware en que se ejecuta, como Windows, macOS, Linux, iOS y Android.
Aquí tiene ejemplos de uso en frases:
".NET Core es una implementación multiplataforma de .NET".
"Los perfiles de PCL representan plataformas de Microsoft, mientras que .NET Standard es independiente de la
plataforma".
La documentación heredada de .NET a veces usa el término "plataforma de .NET" para referirse a una
implementación de .NET o a la pila de .NET que incluyen todas las implementaciones. Estos dos usos tienden a
confundirse con el significado principal (sistema operativo o hardware), por tanto, tratamos de evitar estos usos.
"Plataforma" tiene un significado diferente en "plataforma del desarrollador", ya que hace referencia al software
que proporciona herramientas y bibliotecas para compilar y ejecutar aplicaciones. .NET es una plataforma para el
desarrollo de contenido de código abierto multiplataforma que permite crear una gran variedad de tipos de
aplicaciones.
A veces, la documentación heredada de .NET usa "entorno de ejecución" para indicar una implementación de .NET,
como en los siguientes ejemplos:
"Los diversos entornos de ejecución .NET implementan versiones concretas de .NET Standard".
"Las bibliotecas diseñadas para ejecutarse en varios entornos de ejecución deben tener como destino este
marco de trabajo". (Hace referencia a .NET Standard).
"Los diversos entornos de ejecución .NET implementan versiones concretas de .NET Standard. … Cada versión
del entorno de ejecución de .NET anuncia la última versión de .NET Standard que admite...".
pila
Un conjunto de tecnologías de programación que se usan para compilar y ejecutar aplicaciones.
La "pila de .NET" hace referencia a .NET Standard y a todas las implementaciones de .NET. La frase "una pila de .NET"
puede hacer referencia a una implementación de .NET.
TFM
Moniker de la plataforma de destino.
Un formato de token normalizado para especificar la plataforma de destino de una aplicación o biblioteca de .NET.
Se suele hacer referencia a las plataformas de destino mediante un nombre corto, como net462 . Los TFM de
formato largo (como .NETFramework,Version=4.6.2) existen, pero no se suelen usar para especificar una
plataforma de destino.
Vea Plataformas de destino.
UWP
Plataforma universal de Windows.
Una implementación de .NET que se usa para compilar aplicaciones Windows modernas y táctiles y software para
Internet de las cosas (IoT). Se ha diseñado para unificar los diferentes tipos de dispositivos de destino, incluidos
equipos, tabletas, teléfonos e incluso la consola Xbox. UWP proporciona muchos servicios, como una tienda de
aplicaciones centralizada, un entorno de ejecución (AppContainer) y un conjunto de API de Windows para usar en
lugar de Win32 (WinRT). Pueden escribirse aplicaciones en C++, C#, Visual Basic y JavaScript. Al usar C# y
Visual Basic, .NET 5 (y .NET Core), así como sus versiones posteriores, proporcionan las API de .NET.
Vea también
Aspectos básicos de .NET
Guía de .NET Framework
ASP.NET Overview (Información general de ASP.NET)
ASP.NET Core Overview (Información general de ASP.NET Core)
Tutorial: Creación de una aplicación de consola de
.NET Core con Visual Studio
16/09/2020 • 6 minutes to read • Edit Online
En este tutorial se muestra cómo crear y ejecutar una aplicación de consola de .NET Core en Visual Studio 2019.
Requisitos previos
Visual Studio 2019, versión 16.6 o posterior con la carga de trabajo Desarrollo multiplataforma de
.NET Core instalada. El SDK de .NET Core 3.1 se instala automáticamente al seleccionar esta carga de
trabajo.
Para más información, consulte Instalación del SDK de .NET Core con Visual Studio.
Creación de la aplicación
Cree un proyecto de aplicación de consola de .NET Core denominado "HelloWorld".
1. Inicie Visual Studio 2019.
2. En la página de inicio, elija Crear un proyecto nuevo .
3. En la página Crear un proyecto , escriba consola en el cuadro de búsqueda. Después, elija C# o Visual
Basic en la lista de lenguajes y luego elija Todas las plataformas en la lista de plataformas. Elija la
plantilla Aplicación de consola (.NET Core) y haga clic en Siguiente .
TIP
Si no ve las plantillas de .NET Core, es probable que falte la carga de trabajo necesaria. En el mensaje ¿No
encuentra lo que busca? , elija el vínculo Instalar más herramientas y características . Se abre el Instalador
de Visual Studio. Asegúrese de que tiene instalada la carga de trabajo Desarrollo multiplataforma de .NET
Core .
using System;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Imports System
Module Program
Sub Main(args As String())
Console.WriteLine("Hello World!")
End Sub
End Module
Main es el punto de entrada de la aplicación, el método que se llama automáticamente mediante el tiempo de
ejecución cuando inicia la aplicación. Los argumentos de línea de comandos proporcionados cuando se inicia la
aplicación están disponibles en la matriz args.
Si no se muestra el idioma que quiere usar, cambie el selector de idioma en la parte superior de la página.
Ejecutar la aplicación
1. Presione Ctrl+F5 para ejecutar el programa sin depurar.
Se abre la ventana de la consola con el texto "Hello World" impreso en la pantalla y parte de la
información de depuración de Visual Studio.
2. Presione cualquier tecla para cerrar la ventana de consola.
Mejora de la aplicación
Mejore la aplicación para pedir su nombre al usuario y mostrarlo con la fecha y la hora.
1. En Program.cs o Program.vb, reemplace el contenido del método Main , que es la línea que llama a
Console.WriteLine , por el código siguiente:
Este código muestra un mensaje en la ventana de la consola y espera a que el usuario escriba una cadena
y, luego, presione Entrar. Almacena esta cadena en una variable denominada name . También recupera el
valor de la propiedad DateTime.Now, que contiene la hora local actual, y lo asigna a una variable
denominada date ( currentDate en Visual Basic). Asimismo, muestra estos valores en la ventana de la
consola. Por último, muestra un mensaje en la ventana de la consola y llama al método
Console.ReadKey(Boolean) para esperar a la entrada del usuario.
El símbolo \n ( vbCrLf en Visual Basic) representa un carácter de nueva línea.
El signo de dólar ( $ ) delante de una cadena permite colocar expresiones como nombres de variable
entre llaves en la cadena. El valor de la expresión se inserta en la cadena en lugar de la expresión. Esta
sintaxis se conoce como cadenas interpoladas.
2. Presione Ctrl+F5 para ejecutar el programa sin depurar.
3. Responda a la solicitud escribiendo un nombre y presionando la tecla Entrar.
En este tutorial se presentan las herramientas de depuración que hay disponibles en Visual Studio.
Requisitos previos
Este tutorial funciona con la aplicación de consola que se crea en Creación de una aplicación de consola de
.NET Core con Visual Studio.
4. La ejecución del programa se detiene cuando llega al punto de interrupción y antes de que se ejecute el
método Console.WriteLine . La ventana Variables locales muestra los valores de las variables definidas en
el método que se ejecuta actualmente.
4. Presione F5 para que continúe la ejecución del programa. Otra manera de hacerlo es elegir Depuración >
Continuar en el menú.
Los valores mostrados en la ventana de la consola corresponden a los cambios realizados en la ventana
Inmediato .
5. Presione cualquier tecla para salir de la aplicación y detenga la depuración.
2. En Expresión condicional , escriba el código siguiente en el campo que muestra el código de ejemplo que
comprueba si x es 5. Si no se muestra el idioma que quiere usar, cambie el selector de idioma en la parte
superior de la página.
String.IsNullOrEmpty(name)
String.IsNullOrEmpty(name)
7. Seleccione la ventana Variables locales , que muestra los valores de las variables que son locales para el
método que se ejecuta actualmente. En este caso, Main es el método que se está ejecutando actualmente.
Observe que el valor de la variable name es "" o String.Empty.
8. Confirme que el valor es una cadena vacía escribiendo la siguiente instrucción en la ventana Inmediato y
presionando Entrar. El resultado es true .
? name == String.Empty
? String.IsNullOrEmpty(name)
En este punto, la ventana Variables locales muestra que la matriz args está vacía, y name y date tienen
valores predeterminados. Además, Visual Studio ha abierto una ventana de consola en blanco.
2. Presione F11. Visual Studio ahora resalta la siguiente línea de ejecución. La ventana Variables locales no
cambia y la ventana de consola permanece en blanco.
C#
Visual Basic
3. Presione F11. Visual Studio resalta la instrucción que incluye la asignación de variables name . La ventana
Variables locales muestra que name es null , y la ventana de consola muestra la cadena "What is your
name?".
4. Para responder a la solicitud, escriba una cadena en la ventana de consola y presione Entrar. La consola no
responde y la cadena que especificó no se muestra en la ventana de la consola, pero el método
Console.ReadLine capturará en cambio la entrada.
5. Presione F11. Visual Studio resalta la instrucción que incluye la asignación de la variable date (
currentDate en Visual Basic). La ventana Variables locales muestra el valor devuelto por la llamada al
método Console.ReadLine. La ventana de la consola también muestra la cadena que escribió en la solicitud.
6. Presione F11. La ventana Variables locales muestra el valor de la variable date tras la asignación desde
la propiedad DateTime.Now. La ventana de consola permanece sin cambios.
7. Presione F11. Visual Studio llama al método Console.WriteLine(String, Object, Object). La ventana de la
consola muestra la cadena con formato.
8. Elija Depurar > Depurar paso a paso para salir . Otra manera de detener la ejecución paso a paso es
presionar Mayús+F11.
La ventana de la consola muestra un mensaje y espera a que presione una tecla.
9. Presione cualquier tecla para cerrar la ventana de consola y detener la depuración.
Cuando presiona F5 o elije Compilar solución en el menú Compilar , Visual Studio compila la versión de
lanzamiento de la aplicación. Puede probarla como hizo con la versión de depuración.
Pasos siguientes
En este tutorial, ha usado las herramientas de depuración de Visual Studio. En el siguiente tutorial, publicará una
versión de la aplicación que se puede implementar.
Publicación de una aplicación de consola de .NET Core con Visual Studio
Tutorial: Publicación de una aplicación de consola de
.NET Core con Visual Studio
16/09/2020 • 5 minutes to read • Edit Online
En este tutorial se muestra cómo publicar una aplicación de consola para que otros usuarios puedan ejecutarla. La
publicación crea el conjunto de archivos que se necesitan para ejecutar la aplicación. Para implementar los
archivos, cópielos en el equipo de destino.
Requisitos previos
Este tutorial funciona con la aplicación de consola que se crea en Creación de una aplicación de consola de
.NET Core con Visual Studio.
Publicar la aplicación
1. Inicie Visual Studio.
2. Abra el proyecto HelloWorld que creó en Creación de una aplicación de consola de .NET Core con
Visual Studio.
3. Asegúrese de que Visual Studio usa la configuración de compilación de versión. Si es necesario, cambie la
configuración de compilación en la barra de herramientas de Depurar a Versión .
4. Haga clic con el botón derecho en el proyecto HelloWorld (no en la solución HelloWorld) y seleccione
Publicar en el menú.
cd C:\Projects\HelloWorld\bin\Release\netcoreapp3.1\publish\
Recursos adicionales
Implementación de aplicaciones .NET Core
Pasos siguientes
En este tutorial, ha publicado una aplicación de consola. En el siguiente tutorial, creará una biblioteca de clases.
Creación de una biblioteca .NET Standard con Visual Studio
Tutorial: Creación de una biblioteca .NET Standard
con Visual Studio
16/09/2020 • 11 minutes to read • Edit Online
En este tutorial, creará una sencilla clases de utilidades que contiene un único método de control de cadenas.
Una biblioteca de clases define los tipos y los métodos que se llaman desde una aplicación. Una biblioteca de
clases que tiene como destino .NET Standard 2.0, lo que permite que cualquier implementación .NET que admita
esa versión de .NET Standard pueda llamar a su biblioteca.
Al terminar con la biblioteca de clases, puede distribuirla como un paquete NuGet o un componente
empaquetado con la aplicación que lo utiliza.
Requisitos previos
Visual Studio 2019, versión 16.6 o posterior con la carga de trabajo Desarrollo multiplataforma de
.NET Core instalada. El SDK de .NET Core 3.1 se instala automáticamente al seleccionar esta carga de trabajo.
En cada proyecto, Visual Basic crea automáticamente un espacio de nombres que corresponde al nombre
del proyecto. En este tutorial, definirá un espacio de nombres de nivel superior mediante la palabra clave
namespace en el archivo de código.
namespace UtilityLibraries
{
public static class StringLibrary
{
public static bool StartsWithUpper(this string str)
{
if (string.IsNullOrWhiteSpace(str))
return false;
char ch = str[0];
return char.IsUpper(ch);
}
}
}
Imports System.Runtime.CompilerServices
Namespace UtilityLibraries
Public Module StringLibrary
<Extension>
Public Function StartsWithUpper(str As String) As Boolean
If String.IsNullOrWhiteSpace(str) Then
Return False
End If
using System;
using UtilityLibraries;
class Program
{
static void Main(string[] args)
{
int row = 0;
do
{
if (row == 0 || row >= 25)
ResetConsole();
Module Program
Dim row As Integer = 0
Sub Main()
Do
If row = 0 OrElse row >= 25 Then ResetConsole()
El código usa la variable row para mantener un recuento del número de filas de datos escritas en la
ventana de consola. Siempre que sea mayor o igual a 25, el código borra la ventana de consola y muestra
un mensaje al usuario.
El programa le pide al usuario que escriba una cadena. Indica si la cadena comienza con un carácter en
mayúsculas. Si el usuario presiona la tecla Entrar sin especificar una cadena, la aplicación finaliza y la
ventana de la consola se cierra.
Ejecutar la aplicación
1. En el Explorador de soluciones , haga clic con el botón derecho en el proyecto Presentación y
seleccione Establecer como proyecto de inicio en el menú contextual.
2. Presione CTRL+F5 para compilar y ejecutar el programa sin depuración.
3. Para probar el programa, escriba cadenas y presione Entrar y, después, presione Entrar para salir.
Recursos adicionales
Desarrollo de bibliotecas con la CLI de .NET Core
Versiones de .NET Standard y las plataformas que admiten.
Pasos siguientes
En este tutorial, ha creado una prueba unitaria de una biblioteca de clases. En el siguiente tutorial, aprenderá a
hacer una prueba unitaria de la biblioteca de clases.
Prueba unitaria a una biblioteca .NET Standard con Visual Studio
También puede omitir las pruebas unitarias automatizadas y aprender a compartir la biblioteca mediante la
creación de un paquete NuGet:
Crear y publicar un paquete con Visual Studio
También puede obtener más información sobre cómo publicar una aplicación de consola. Si publica la aplicación
de consola a partir de la solución que ha creado en este tutorial, la biblioteca de clases lo incluirá como un
archivo .dll.
Publicación de una aplicación de consola de .NET Core con Visual Studio
Tutorial: Prueba de una biblioteca de clases .NET
Standard con .NET Core mediante Visual Studio
16/09/2020 • 16 minutes to read • Edit Online
En este tutorial se muestra cómo automatizar las pruebas unitarias mediante la adición de un proyecto de prueba a
una solución.
Requisitos previos
Este tutorial funciona con la solución que se crea en Creación de una biblioteca de .NET Standard con
Visual Studio.
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace StringLibraryTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
}
}
}
Imports Microsoft.VisualStudio.TestTools.UnitTesting
Namespace StringLibraryTest
<TestClass>
Public Class UnitTest1
<TestMethod>
Sub TestSub()
End Sub
End Class
End Namespace
M ÉTO DO S DE A SERC IÓ N F UN C IÓ N
También puede usar el método Assert.ThrowsException en un método de prueba para indicar el tipo de excepción
que se espera que produzca. La prueba dará error si no se inicia la excepción especificada.
Al probar el método StringLibrary.StartsWithUpper , quiere proporcionar un número de cadenas que comiencen
con un carácter en mayúsculas. Espera que el método devuelva true en estos casos, por lo que puede llamar al
método Assert.IsTrue. Del mismo modo, quiere proporcionar un número de cadenas que comiencen con algo que
no sea un carácter en mayúsculas. Espera que el método devuelva false en estos casos, por lo que puede llamar
al método Assert.IsFalse.
Dado que el método de biblioteca administra cadenas, quiere asegurarse también de que administra
correctamente una cadena vacía ( String.Empty ), una cadena válida que no tenga caracteres y cuyo Length sea 0, y
una cadena null que no se haya inicializado. Se puede llamar a StartsWithUpper directamente como un método
estático y pasar un argumento String único. También puede llamar a StartsWithUpper como método de extensión
en una variable de string asignada a null .
Definirá tres métodos, cada uno de los cuales llama a un método Assert para cada elemento de una matriz de
cadenas. Llamará a una sobrecarga de método que le permite especificar que se muestre un mensaje de error en
caso de error en la prueba. El mensaje identifica la cadena que causó el error.
Para crear los métodos de prueba:
1. En la ventana de código UnitTest1.cs o UnitTest1.vb, reemplace el código por el siguiente:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using UtilityLibraries;
namespace StringLibraryTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestStartsWithUpper()
{
// Tests that we expect to return true.
string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва" };
foreach (var word in words)
{
bool result = word.StartsWithUpper();
Assert.IsTrue(result,
String.Format("Expected for '{0}': true; Actual: {1}",
word, result));
}
}
[TestMethod]
public void TestDoesNotStartWithUpper()
{
// Tests that we expect to return false.
string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
"1234", ".", ";", " " };
foreach (var word in words)
{
bool result = word.StartsWithUpper();
Assert.IsFalse(result,
String.Format("Expected for '{0}': false; Actual: {1}",
word, result));
}
}
[TestMethod]
public void DirectCallWithNullOrEmpty()
{
// Tests that we expect to return false.
string[] words = { string.Empty, null };
foreach (var word in words)
{
bool result = StringLibrary.StartsWithUpper(word);
Assert.IsFalse(result,
String.Format("Expected for '{0}': false; Actual: {1}",
word == null ? "<null>" : word, result));
}
}
}
}
Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports UtilityLibraries
Namespace StringLibraryTest
<TestClass>
Public Class UnitTest1
<TestMethod>
Public Sub TestStartsWithUpper()
' Tests that we expect to return true.
Dim words() As String = {"Alphabet", "Zebra", "ABC", "Αθήνα", "Москва"}
For Each word In words
Dim result As Boolean = word.StartsWithUpper()
Assert.IsTrue(result,
$"Expected for '{word}': true; Actual: {result}")
Next
End Sub
<TestMethod>
Public Sub TestDoesNotStartWithUpper()
' Tests that we expect to return false.
Dim words() As String = {"alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
"1234", ".", ";", " "}
For Each word In words
Dim result As Boolean = word.StartsWithUpper()
Assert.IsFalse(result,
$"Expected for '{word}': false; Actual: {result}")
Next
End Sub
<TestMethod>
Public Sub DirectCallWithNullOrEmpty()
' Tests that we expect to return false.
Dim words() As String = {String.Empty, Nothing}
For Each word In words
Dim result As Boolean = StringLibrary.StartsWithUpper(word)
Assert.IsFalse(result,
$"Expected for '{If(word Is Nothing, "<null>", word)}': false; Actual: {result}")
Next
End Sub
End Class
End Namespace
Si obtiene un error al guardar el código fuente en un archivo con codificación UTF-8, Visual Studio puede
guardarlo como un archivo ASCII. Cuando eso suceda, el tiempo de ejecución no descodifica correctamente
los caracteres UTF-8 del rango ASCII, y los resultados de la prueba no serán correctos.
5. En la barra de menús, seleccione Prueba > Ejecutar todas las pruebas . Si no se abre la ventana
Explorador de pruebas , ábrala mediante Prueba > Explorador de pruebas . Las tres pruebas se
muestran en la sección Pruebas superadas y en la sección Resumen se informa del resultado de la serie
de pruebas.
Administración de errores de prueba
Si está realizando el desarrollo controlado por pruebas (TDD), escribirá las pruebas, y estas generarán un error
cuando las ejecute por primera vez. Después, agregará un código a la aplicación para que la prueba se realice
correctamente. En este tutorial, ha creado la prueba después de escribir el código de la aplicación que valida, por lo
que no ha podido comprobar si la prueba genera un error. Para asegurarse de que una prueba genera un error
cuando espera que lo haga, agregue un valor no válido a la entrada de prueba.
1. Modifique la matriz words en el método TestDoesNotStartWithUpper para incluir la cadena "Error". No
necesita guardar el archivo porque Visual Studio guarda automáticamente archivos abiertos cuando se crea
una solución para ejecutar pruebas.
2. Ejecute la prueba seleccionando Prueba > Ejecutar todas las pruebas de la barra de menús. En la
ventana Explorador de pruebas se indica que dos pruebas se han realizado correctamente y que una ha
finalizado con errores.
3. Seleccione la prueba con errores, TestDoesNotStartWith .
En la ventana Explorador de pruebas se muestra el mensaje generado por la instrucción Assert: "Error de
Assert.IsFalse. Se esperaba para "Error": false; real: True". Debido al error, no se probaron todas las cadenas
de la matriz después de "Error".
4. Quite la cadena "Error" que agregó en el paso 1. Vuelva a ejecutar la prueba y se superarán las pruebas.
3. Ejecute las pruebas unitarias mediante Ejecutar prueba > Todas las pruebas de la barra de menús. Las
pruebas se superan.
Recursos adicionales
Conceptos básicos de las pruebas unitarias en Visual Studio
Pruebas unitarias en .NET Core y .NET Standard
Pasos siguientes
En este tutorial, ha realizado una prueba unitaria de una biblioteca de clases. Puede poner la biblioteca a
disposición de otros usuarios si la publica en NuGet como un paquete. Para obtener información sobre cómo
hacerlo, realice este tutorial de NuGet:
Creación y publicación de un paquete NuGet con Visual Studio
Si publica una biblioteca como un paquete NuGet, otros usuarios pueden instalarla y usarla. Para obtener
información sobre cómo hacerlo, realice este tutorial de NuGet:
Instalación y uso de un paquete en Visual Studio
No es necesario distribuir una biblioteca como un paquete. Se puede agrupar con una aplicación de consola que la
use. Para aprender a publicar una aplicación de consola, vea el tutorial anterior de esta serie:
Publicación de una aplicación de consola de .NET Core con Visual Studio
Tutorial: Depuración de una aplicación de consola
de .NET Core con Visual Studio Code
16/09/2020 • 6 minutes to read • Edit Online
En este tutorial se explica cómo crear y ejecutar una aplicación de consola de .NET Core mediante
Visual Studio Code y la CLI de .NET Core. Las tareas de proyecto, como crear, compilar y ejecutar un proyecto,
se realizan mediante la CLI de .NET Core. Puede seguir este tutorial con un editor de código diferente y
ejecutar comandos en un terminal si lo prefiere.
Requisitos previos
1. Visual Studio Code con la extensión de C# instalada. Para saber cómo instalar extensiones en
Visual Studio Code, vea el Marketplace de extensiones de VS Code.
2. SDK de .NET Core 3.1 o posterior
Creación de la aplicación
Cree un proyecto de aplicación de consola de .NET Core denominado "HelloWorld".
1. Inicie Visual Studio Code.
2. Seleccione Archivo > Abrir carpeta (Archivo > Abrir... en macOS) en el menú principal.
3. En el cuadro de diálogo Abrir carpeta , cree una carpeta de HelloWorld y haga clic en Seleccionar
carpeta (Abrir en macOS).
De forma predeterminada, el nombre de la carpeta se convierte en el nombre del proyecto y del
espacio de nombres. Más adelante en el tutorial, agregará código que supone que el espacio de
nombres del proyecto es HelloWorld .
4. Para abrir el Terminal en Visual Studio Code, seleccione Ver > Terminal en el menú principal.
Se abre el Terminal con el símbolo del sistema en la carpeta HelloWorld.
5. En el Terminal , escriba este comando:
La plantilla crea una aplicación "Hola mundo" sencilla. Llama al método Console.WriteLine(String) para
mostrar "Hello World!" en la ventana de la consola.
El código de plantilla define una clase, Program , con un solo método, Main , que toma una matriz de String
como argumento:
using System;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Main es el punto de entrada de la aplicación, el método que se llama automáticamente mediante el tiempo
de ejecución cuando inicia la aplicación. Los argumentos de línea de comandos proporcionados cuando se
inicia la aplicación están disponibles en la matriz args.
Ejecutar la aplicación
Ejecute este comando en el Terminal :
dotnet run
Mejora de la aplicación
Mejore la aplicación para pedir su nombre al usuario y mostrarlo con la fecha y la hora.
1. Haga clic en el archivo Program.cs para abrirlo.
La primera vez que se abre un archivo de C# en Visual Studio Code, se carga OmniSharp en el editor.
2. Seleccione Sí cuando Visual Studio Code le pida que agregue los recursos que faltan para compilar y
depurar la aplicación.
3. Reemplace el contenido del método Main en Program.cs, que es la línea que llama a
Console.WriteLine , por este código:
Este código muestra un mensaje en la ventana de la consola y espera a que el usuario escriba una
cadena y, luego, presione Entrar. Almacena esta cadena en una variable denominada name . También
recupera el valor de la propiedad DateTime.Now, que contiene la hora local actual, y lo asigna a una
variable denominada date . Asimismo, muestra estos valores en la ventana de la consola. Por último,
muestra un mensaje en la ventana de la consola y llama al método Console.ReadKey(Boolean) para
esperar a la entrada del usuario.
El símbolo \n representa un carácter de nueva línea.
El signo de dólar ( $ ) delante de una cadena permite colocar expresiones como nombres de variable
entre llaves en la cadena. El valor de la expresión se inserta en la cadena en lugar de la expresión. Esta
sintaxis se conoce como cadenas interpoladas.
4. Guarde los cambios.
IMPORTANT
En Visual Studio Code, tiene que guardar los cambios explícitamente. A diferencia de Visual Studio, los cambios
de los archivos no se guardan automáticamente al compilar y ejecutar una aplicación.
dotnet run
Recursos adicionales
Setting up Visual Studio Code (Configuración de Visual Studio Code)
Pasos siguientes
En este tutorial, ha creado una aplicación de consola de .NET Core. En el siguiente tutorial, depurará la
aplicación.
Depuración de una aplicación de consola de .NET Core con Visual Studio Code
Tutorial: Depuración de una aplicación de consola de
.NET Core con Visual Studio Code
16/09/2020 • 14 minutes to read • Edit Online
En este tutorial se presentan las herramientas de depuración disponibles en Visual Studio Code para trabajar con
aplicaciones de .NET Core.
Requisitos previos
Este tutorial funciona con la aplicación de consola que se crea en Creación de una aplicación de consola de
.NET Core con Visual Studio Code.
"console": "internalConsole",
A:
"console": "integratedTerminal",
Iniciar depuración
1. Abra la vista Depurar mediante el icono de depuración que hay en el menú de la izquierda.
2. Seleccione la flecha verde en la parte superior del panel, junto a .NET Core Launch (console) (Inicio de
.NET Core [consola]). Otras maneras de iniciar el programa en modo de depuración son presionar F5 o
elegir Ejecutar > Iniciar depuración en el menú.
3. Seleccione la pestaña Terminal para ver el mensaje "What is your name?" que muestra el programa antes
de esperar una respuesta.
4. Escriba una cadena en la ventana Terminal para responder a la pregunta del nombre y presione Entrar.
La ejecución del programa se detiene cuando llega al punto de interrupción y antes de que se ejecute el
método Console.WriteLine . La sección Variables locales de la ventana Variables muestra los valores de
las variables definidas en el método que se ejecuta actualmente.
2. Seleccione Expression en el menú desplegable, escriba esta expresión condicional y presione Entrar.
String.IsNullOrEmpty(name)
Cada vez que se alcanza el punto de interrupción, el depurador llama al método
String.IsNullOrEmpty(name) y se interrumpe en esta línea solo si la llamada al método devuelve true .
En lugar de una expresión condicional, puede especificar un número de llamadas, que interrumpe la
ejecución del programa antes de que se ejecute una instrucción un número de veces especificado. Otra
opción consiste en especificar una condición de filtro, que interrumpe la ejecución del programa en función
de atributos tales como un identificador de subproceso, un nombre de proceso o un nombre de subproceso.
3. Inicie el programa con la depuración presionando F5.
4. En la ventana Terminal, cuando se le pida que escriba su nombre, presione la tecla Entrar .
Como se ha cumplido la condición que especificó ( name es null o String.Empty), la ejecución del
programa se detiene cuando se alcanza el punto de interrupción y antes de que se ejecute el método
Console.WriteLine .
name == String.Empty
6. Seleccione el botón Continuar en la barra de herramientas para continuar la ejecución del programa.
7. Seleccione la pestaña Terminal y presione cualquier tecla para salir del programa y detener la depuración.
8. Para borrar el punto de interrupción, haga clic en el punto en el margen izquierdo de la ventana de código.
Otras formas de borrar un punto de interrupción consisten en presionar F9 o elegir Ejecutar > Alternar
punto de interrupción en el menú mientras se selecciona la línea de código.
9. Si recibe una advertencia que indica que se perderá la condición del punto de interrupción, seleccione
Quitar punto de interrupción .
6. Para responder al mensaje, escriba una cadena en la ventana Terminal y presione Entrar.
Es posible que la pestaña Terminal no muestre la cadena mientras la está escribiendo, pero el método
Console.ReadLine capturará la entrada.
7. Seleccione Ejecutar > Paso a paso por instrucciones o presione F11.
Visual Studio Code resalta la asignación de la variable date . La ventana Variables muestra el valor
devuelto por la llamada al método Console.ReadLine. En la pestaña Terminal se muestra la cadena que
escribió en el símbolo del sistema.
8. Seleccione Ejecutar > Paso a paso por instrucciones o presione F11.
La ventana Variables muestra el valor de la variable date tras la asignación desde la propiedad
DateTime.Now.
9. Seleccione Ejecutar > Paso a paso por instrucciones o presione F11.
Visual Studio Code llama al método Console.WriteLine(String, Object, Object). La ventana de la consola
muestra la cadena con formato.
10. Seleccione Ejecutar > Paso a paso para salir o presione Mayús+F11.
Recursos adicionales
Debugging in Visual Studio Code (Depuración en Visual Studio Code)
Pasos siguientes
En este tutorial, ha usado las herramientas de depuración de Visual Studio Code. En el siguiente tutorial, publicará
una versión de la aplicación que se puede implementar.
Publicación de una aplicación de consola de .NET Core con Visual Studio Code
Tutorial: Publicación de una aplicación de consola de
.NET Core con Visual Studio Code
16/09/2020 • 6 minutes to read • Edit Online
En este tutorial se muestra cómo publicar una aplicación de consola para que otros usuarios puedan ejecutarla. La
publicación crea el conjunto de archivos que se necesitan para ejecutar una aplicación. Para implementar los
archivos, cópielos en el equipo de destino.
La CLI de .NET Core se usa para publicar la aplicación, por lo que puede seguir este tutorial con un editor de código
que no sea Visual Studio Code si lo prefiere.
Requisitos previos
Este tutorial funciona con la aplicación de consola que se crea en Creación de una aplicación de consola de
.NET Core con Visual Studio Code.
Publicar la aplicación
1. Inicie Visual Studio Code.
2. Abra la carpeta de proyecto HelloWorld que ha creado en Creación de una aplicación de consola de
.NET Core con Visual Studio Code.
3. Elija Ver > Terminal en el menú principal.
Se abrirá el terminal en la carpeta HelloWorld.
4. Ejecute el siguiente comando:
Recursos adicionales
Implementación de aplicaciones .NET Core
Pasos siguientes
En este tutorial, ha publicado una aplicación de consola. En el siguiente tutorial, creará una biblioteca de clases.
Creación de una biblioteca .NET Standard mediante Visual Studio Code
Tutorial: Creación de una biblioteca .NET Standard
mediante Visual Studio Code
16/09/2020 • 9 minutes to read • Edit Online
En este tutorial, creará una sencilla biblioteca de utilidades que contiene un único método de control de cadenas.
Lo implementará como un método de extensión de modo que se pueda llamar como si fuera un miembro de la
clase String.
Una biblioteca de clases define los tipos y los métodos que se llaman desde una aplicación. Una biblioteca de
clases que tiene como destino .NET Standard 2.0, lo que permite que cualquier implementación .NET que admita
esa versión de .NET Standard pueda llamar a su biblioteca. Cuando termine la biblioteca de clases, puede
distribuirla como un componente de terceros o como un componente empaquetado con una o varias aplicaciones.
Requisitos previos
1. Visual Studio Code con la extensión de C# instalada. Para saber cómo instalar extensiones en
Visual Studio Code, vea el Marketplace de extensiones de VS Code.
2. SDK de .NET Core 3.1 o posterior
3. Asegúrese de que la biblioteca tiene como destino la versión correcta de .NET Standard. En Explorer , abra
el archivo StringLibrary/StringLibrary.csproj.
En el elemento TargetFramework se muestra que el proyecto tiene como destino .NET Standard 2.0.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>
using System;
namespace UtilityLibraries
{
public static class StringLibrary
{
public static bool StartsWithUpper(this string str)
{
if (string.IsNullOrWhiteSpace(str))
return false;
char ch = str[0];
return char.IsUpper(ch);
}
}
}
dotnet build
class Program
{
static void Main(string[] args)
{
int row = 0;
do
{
if (row == 0 || row >= 25)
ResetConsole();
El código usa la variable row para mantener un recuento del número de filas de datos escritas en la
ventana de consola. Siempre que sea mayor o igual a 25, el código borra la ventana de consola y muestra
un mensaje al usuario.
El programa le pide al usuario que escriba una cadena. Indica si la cadena comienza con un carácter en
mayúsculas. Si el usuario presiona la tecla Entrar sin especificar una cadena, la aplicación finaliza y la
ventana de la consola se cierra.
4. Guarde los cambios.
Ejecutar la aplicación
1. Ejecute el siguiente comando en el terminal:
2. Para probar el programa, escriba cadenas y presione Entrar y, después, presione Entrar para salir.
La salida del terminal se parece al ejemplo siguiente:
Press <Enter> only to exit; otherwise, enter a string and press <Enter>:
Recursos adicionales
Desarrollo de bibliotecas con la CLI de .NET Core
Versiones de .NET Standard y las plataformas que admiten.
Pasos siguientes
En este tutorial, ha creado una solución, ha agregado un proyecto de biblioteca y ha agregado un proyecto de
aplicación de consola que usa la biblioteca. En el siguiente tutorial, agregará un proyecto de prueba unitaria a la
solución.
Prueba de una biblioteca .NET Standard con .NET Core mediante Visual Studio Code
Tutorial: Prueba de una biblioteca de clases .NET
Standard con .NET Core mediante Visual Studio
Code
16/09/2020 • 12 minutes to read • Edit Online
En este tutorial se muestra cómo automatizar las pruebas unitarias mediante la adición de un proyecto de prueba a
una solución.
Requisitos previos
Este tutorial funciona con la solución que se crea en Creación de una biblioteca de .NET Standard con
Visual Studio Code.
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace StringLibraryTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
}
}
}
M ÉTO DO S DE A SERC IÓ N F UN C IÓ N
También puede usar el método Assert.ThrowsException en un método de prueba para indicar el tipo de excepción
que se espera que produzca. La prueba dará error si no se inicia la excepción especificada.
Al probar el método StringLibrary.StartsWithUpper , quiere proporcionar un número de cadenas que comiencen
con un carácter en mayúsculas. Espera que el método devuelva true en estos casos, por lo que puede llamar al
método Assert.IsTrue. Del mismo modo, quiere proporcionar un número de cadenas que comiencen con algo que
no sea un carácter en mayúsculas. Espera que el método devuelva false en estos casos, por lo que puede llamar
al método Assert.IsFalse.
Dado que el método de biblioteca controla cadenas, también se recomienda asegurarse de que controla
correctamente una cadena vacía ( String.Empty ) y una cadena null . Una cadena vacía es aquella que no tiene
ningún carácter y cuyo valor de Length es 0. Una cadena null es aquella que no se ha inicializado. Se puede
llamar a StartsWithUpper directamente como un método estático y pasar un argumento String único. También
puede llamar a StartsWithUpper como método de extensión en una variable de string asignada a null .
Definirá tres métodos, cada uno de los cuales llama a un método Assert para cada elemento de una matriz de
cadenas. Llamará a una sobrecarga de método que le permite especificar que se muestre un mensaje de error en
caso de error en la prueba. El mensaje identifica la cadena que causó el error.
Para crear los métodos de prueba:
1. Abra StringLibraryTest/UnitTest1.cs y reemplace todo el código por el siguiente.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using UtilityLibraries;
namespace StringLibraryTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestStartsWithUpper()
{
// Tests that we expect to return true.
string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва" };
foreach (var word in words)
{
bool result = word.StartsWithUpper();
Assert.IsTrue(result,
String.Format("Expected for '{0}': true; Actual: {1}",
word, result));
}
}
[TestMethod]
public void TestDoesNotStartWithUpper()
{
// Tests that we expect to return false.
string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
"1234", ".", ";", " " };
foreach (var word in words)
{
bool result = word.StartsWithUpper();
Assert.IsFalse(result,
String.Format("Expected for '{0}': false; Actual: {1}",
word, result));
}
}
[TestMethod]
public void DirectCallWithNullOrEmpty()
{
// Tests that we expect to return false.
string[] words = { string.Empty, null };
foreach (var word in words)
{
bool result = StringLibrary.StartsWithUpper(word);
Assert.IsFalse(result,
String.Format("Expected for '{0}': false; Actual: {1}",
word == null ? "<null>" : word, result));
}
}
}
}
La prueba de caracteres en mayúsculas en el método TestStartsWithUpper incluye la letra mayúscula griega
alfa (U + 0391) y la letra mayúscula cirílica EM (U + 041C). La prueba de caracteres en minúsculas en el
método TestDoesNotStartWithUpper incluye la letra griega minúscula alfa (U + 03B1) y la letra cirílica
minúscula Ghe (U + 0433).
2. Guarde los cambios.
3. Ejecute las pruebas:
en la salida del terminal se indica que se han superado todas las pruebas.
en la salida del terminal se indica que una de las pruebas no se supera y se proporciona un mensaje de
error al respecto: "Error de Assert.IsFalse. Se esperaba para "Error": false; real: True". Debido al error, no se
probaron todas las cadenas de la matriz después de "Error".
Starting test execution, please wait...
3. Quite la cadena "Error" que agregó en el paso 1. Vuelva a ejecutar la prueba y se superarán las pruebas.
Recursos adicionales
Pruebas unitarias en .NET Core y .NET Standard
Pasos siguientes
En este tutorial, ha realizado una prueba unitaria de una biblioteca de clases. Puede poner la biblioteca a
disposición de otros usuarios si la publica en NuGet como un paquete. Para obtener información sobre cómo
hacerlo, realice este tutorial de NuGet:
Creación y publicación de un paquete con la CLI de dotnet
Si publica una biblioteca como un paquete NuGet, otros usuarios pueden instalarla y usarla. Para obtener
información sobre cómo hacerlo, realice este tutorial de NuGet:
Instalar y usar un paquete con la CLI de dotnet
No es necesario distribuir una biblioteca como un paquete. Se puede agrupar con una aplicación de consola que la
use. Para aprender a publicar una aplicación de consola, vea el tutorial anterior de esta serie:
Publicación de una aplicación de consola de .NET Core con Visual Studio Code
Tutorial: Creación de una aplicación de consola de
.NET Core con Visual Studio para Mac
16/09/2020 • 6 minutes to read • Edit Online
En este tutorial se muestra cómo crear y ejecutar una aplicación de consola de .NET Core en Visual Studio para
Mac.
NOTE
Sus comentarios son muy importantes. Hay dos maneras de proporcionar comentarios al equipo de desarrollo de Visual
Studio para Mac:
En Visual Studio para Mac, seleccione Ayuda > Notificar un problema en el menú o Notificar un problema desde
la pantalla de bienvenida, que abre una ventana para presentar un informe de errores. Puede realizar un seguimiento
de sus comentarios en el portal de la Comunidad de desarrolladores.
Para hacer una sugerencia, seleccione Ayuda > Apor tar una sugerencia en el menú o Apor tar una sugerencia
desde la pantalla de bienvenida, que le lleva a la página web de la Comunidad de desarrolladores de Visual Studio para
Mac.
Requisitos previos
Visual Studio para Mac, versión 8.6 o posterior. Seleccione la opción para instalar .NET Core. La instalación
de Xamarin es opcional para el desarrollo de .NET Core. Para obtener más información, vea los siguientes
recursos:
Tutorial: Instalación de Visual Studio para Mac.
Versiones de macOS compatibles.
Versiones de .NET Core compatibles con Visual Studio para Mac.
Creación de la aplicación
Cree un proyecto de aplicación de consola de .NET Core denominado "HelloWorld".
1. Inicie Visual Studio para Mac:
2. Seleccione Nuevo en la ventana de inicio.
3. En el cuadro de diálogo Nuevo proyecto , seleccione Aplicación en el nodo Web and Console (Web y
consola). Seleccione la plantilla Aplicación de consola y haga clic en Siguiente .
4. En el cuadro de diálogo Plataforma de destino del cuadro de diálogo para configurar la nueva
aplicación de consola , seleccione .NET Core 3.1 y seleccione Siguiente .
using System;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Main es el punto de entrada de la aplicación, el método que se llama automáticamente mediante el tiempo de
ejecución cuando inicia la aplicación. Los argumentos de línea de comandos proporcionados cuando se inicia la
aplicación están disponibles en la matriz args .
Ejecutar la aplicación
1. Presione ⌘↵ (opción+command+entrar) para ejecutar la aplicación sin depuración.
2. Cierre la ventana de Terminal .
Mejora de la aplicación
Mejore la aplicación para pedir su nombre al usuario y mostrarlo con la fecha y la hora.
1. En Program.cs, reemplace el contenido del método Main , que es la línea que llama a Console.WriteLine ,
por el código siguiente:
Este código muestra un mensaje en la ventana de la consola y espera a que el usuario escriba una cadena
y, luego, presione Entrar. Almacena esta cadena en una variable denominada name . También recupera el
valor de la propiedad DateTime.Now, que contiene la hora local actual, y lo asigna a una variable
denominada date . Asimismo, muestra estos valores en la ventana de la consola. Por último, muestra un
mensaje en la ventana de la consola y llama al método Console.ReadKey(Boolean) para esperar a la
entrada del usuario.
El símbolo \n representa un carácter de nueva línea.
El signo de dólar ( $ ) delante de una cadena permite colocar expresiones como nombres de variable
entre llaves en la cadena. El valor de la expresión se inserta en la cadena en lugar de la expresión. Esta
sintaxis se conoce como cadenas interpoladas.
2. Presione ⌘↵ (opción+command+entrar) para ejecutar la aplicación.
4. Cierre el terminal.
Pasos siguientes
En este tutorial, ha creado una aplicación de consola de .NET Core. En el siguiente tutorial, depurará la aplicación.
Depuración de una aplicación de consola de .NET Core con Visual Studio para Mac
Tutorial: Depuración de una aplicación de consola de
.NET Core con Visual Studio para Mac
16/09/2020 • 13 minutes to read • Edit Online
En este tutorial se presentan las herramientas de depuración que hay disponibles en Visual Studio para Mac.
Requisitos previos
Este tutorial funciona con la aplicación de consola que se crea en Creación de una aplicación de consola de
.NET Core con Visual Studio para Mac.
4. La ejecución del programa se detiene cuando llega al punto de interrupción y antes de que se ejecute el
método Console.WriteLine .
String.IsNullOrEmpty(name)
Cada vez que se alcanza el punto de interrupción, el depurador llama al método
String.IsNullOrEmpty(name) y se interrumpe en esta línea solo si la llamada al método devuelve true .
En lugar de una expresión condicional, puede especificar un número de llamadas, que interrumpe la
ejecución del programa antes de que una instrucción se ejecute un número especificado de veces.
3. Presione ⌘↵ (command+entrar) para iniciar la depuración.
4. En la ventana de terminal, presione entrar cuando se le pida que escriba su nombre.
Como se ha cumplido la condición que especificó ( name es null o String.Empty), la ejecución del
programa se detiene cuando se alcanza el punto de interrupción.
5. Seleccione la ventana Variables locales , que muestra los valores de las variables que son locales para el
método que se ejecuta actualmente. En este caso, Main es el método que se está ejecutando actualmente.
Observe que el valor de la variable name es "" ; esto es, String.Empty.
6. También puede ver que el valor es una cadena vacía escribiendo el nombre de la variable name en la
ventana Inmediato y presionando entrar.
7. Presione ⌘↵ (command+entrar) para continuar con la depuración.
8. En la ventana de terminal, presione cualquier tecla para salir del programa.
9. Cierre la ventana de terminal.
10. Para borrar el punto de interrupción, haga clic en el punto rojo en el margen izquierdo de la ventana de
código. Otra manera de hacerlo es elegir Ejecutar > Alternar punto de interrupción con la línea de
código seleccionada.
Pasos siguientes
En este tutorial, ha usado las herramientas de depuración de Visual Studio. En el siguiente tutorial, publicará una
versión de la aplicación que se puede implementar.
Publicación de una aplicación de consola de .NET Core con Visual Studio para Mac
Tutorial: Publicación de una aplicación de consola de
.NET Core con Visual Studio para Mac
16/09/2020 • 4 minutes to read • Edit Online
En este tutorial se muestra cómo publicar una aplicación de consola para que otros usuarios puedan ejecutarla. La
publicación crea el conjunto de archivos que se necesitan para ejecutar la aplicación. Para implementar los
archivos, cópielos en el equipo de destino.
Requisitos previos
Este tutorial funciona con la aplicación de consola que se crea en Creación de una aplicación de consola de
.NET Core con Visual Studio para Mac.
Publicar la aplicación
1. Inicie Visual Studio para Mac:
2. Abra el proyecto HelloWorld que creó en Creación de una aplicación de consola de .NET Core con
Visual Studio para Mac.
3. Asegúrese de que Visual Studio esté compilando la versión de lanzamiento de la aplicación. Si es necesario,
cambie la configuración de compilación en la barra de herramientas de Depurar a Versión .
6. Seleccione el icono de engranaje y elija Copy "publish" as Pathname (Copiar "publicar" como nombre de
ruta) en el menú contextual.
cd ~/Projects/HelloWorld/HelloWorld/bin/Release/netcoreapp3.1/publish/
Recursos adicionales
Implementación de aplicaciones .NET Core
Pasos siguientes
En este tutorial, ha publicado una aplicación de consola. En el siguiente tutorial, creará una biblioteca de clases.
Creación de una biblioteca de .NET Standard mediante Visual Studio para Mac
Tutorial: Creación de una biblioteca de .NET
Standard mediante Visual Studio para Mac
16/09/2020 • 9 minutes to read • Edit Online
En este tutorial, creará una biblioteca de clases que contiene un único método de control de cadenas. Lo
implementará como un método de extensión de modo que se pueda llamar como si fuera un miembro de la clase
String.
Una biblioteca de clases define los tipos y los métodos que se llaman desde una aplicación. Una biblioteca de
clases que tenga como destino .NET Standard 2.1 puede usarse por una aplicación que tenga como destino
cualquier implementación de .NET que admita la versión 2.1 de .NET Standard. Cuando termine la biblioteca de
clases, puede distribuirla como un componente de terceros o como un componente empaquetado con una o
varias aplicaciones.
NOTE
Sus comentarios son muy importantes. Hay dos maneras de proporcionar comentarios al equipo de desarrollo de Visual
Studio para Mac:
En Visual Studio para Mac, seleccione Ayuda > Notificar un problema en el menú o Notificar un problema desde
la pantalla de bienvenida, que abre una ventana para presentar un informe de errores. Puede realizar un seguimiento de
sus comentarios en el portal de la Comunidad de desarrolladores.
Para hacer una sugerencia, seleccione Ayuda > Apor tar una sugerencia en el menú o Apor tar una sugerencia
desde la pantalla de bienvenida, que le lleva a la página web de la Comunidad de desarrolladores de Visual Studio para
Mac.
Requisitos previos
Instale Visual Studio para Mac, versión 8.6 o posterior. Seleccione la opción para instalar .NET Core. La
instalación de Xamarin es opcional para el desarrollo de .NET Core. Para obtener más información, vea los
siguientes recursos:
Tutorial: Instalación de Visual Studio para Mac.
Versiones de macOS compatibles.
Versiones de .NET Core compatibles con Visual Studio para Mac.
7. En el panel Solución , expanda el nodo StringLibrary para mostrar el archivo de clase proporcionado por
la plantilla Class1.cs. Haga clic presionando control en el archivo, seleccione Cambiar nombre en el
menú contextual y denomínelo StringLibrary.cs. Abra el archivo y reemplace el contenido por el código
siguiente:
using System;
namespace UtilityLibraries
{
public static class StringLibrary
{
public static bool StartsWithUpper(this string str)
{
if (string.IsNullOrWhiteSpace(str))
return false;
char ch = str[0];
return char.IsUpper(ch);
}
}
}
8. Presione ⌘S (command+S) para guardar el archivo.
9. Seleccione Errores en el margen inferior de la ventana de IDE para abrir el panel Errores . Seleccione el
botón Salida de la compilación .
using System;
using UtilityLibraries;
class Program
{
static void Main(string[] args)
{
int row = 0;
do
{
if (row == 0 || row >= 25)
ResetConsole();
El programa le pide al usuario que escriba una cadena. Indica si la cadena comienza con un carácter en
mayúsculas. Si el usuario presiona la tecla Entrar sin especificar una cadena, la aplicación finaliza y la
ventana de la consola se cierra.
El código usa la variable row para mantener un recuento del número de filas de datos escritas en la
ventana de consola. Siempre que sea mayor o igual a 25, el código borra la ventana de consola y muestra
un mensaje al usuario.
Ejecutar la aplicación
1. Haga clic presionando control en el proyecto ShowCase y seleccione Ejecutar el proyecto en el menú
contextual.
2. Para probar el programa, escriba cadenas y presione Entrar y, después, presione Entrar para salir.
Recursos adicionales
Desarrollo de bibliotecas con la CLI de .NET Core
Versiones de .NET Standard y las plataformas que admiten.
Notas de la versión de Visual Studio 2019 para Mac
Pasos siguientes
En este tutorial, ha creado una solución, ha agregado un proyecto de biblioteca y ha agregado un proyecto de
aplicación de consola que usa la biblioteca. En el siguiente tutorial, agregará un proyecto de prueba unitaria a la
solución.
Prueba de una biblioteca .NET Standard con .NET Core mediante Visual Studio para Mac
Prueba de una biblioteca de clases .NET Standard
con .NET Core mediante Visual Studio
16/09/2020 • 15 minutes to read • Edit Online
En este tutorial se muestra cómo automatizar las pruebas unitarias mediante la adición de un proyecto de prueba a
una solución.
Requisitos previos
Este tutorial funciona con la solución que se crea en Creación de una biblioteca .NET Standard con Visual Studio
para Mac.
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace StringLibraryTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
}
}
}
1. En el panel Solución , presione Ctrl y haga clic en Dependencias en StringLibrar yTest . Seleccione
Agregar referencia en el menú contextual.
2. En el cuadro de diálogo Referencias , seleccione el proyecto StringLibrar y . Seleccione Aceptar .
M ÉTO DO S DE A SERC IÓ N F UN C IÓ N
También puede usar el método Assert.ThrowsException en un método de prueba para indicar el tipo de excepción
que se espera que produzca. La prueba dará error si no se inicia la excepción especificada.
Al probar el método StringLibrary.StartsWithUpper , quiere proporcionar un número de cadenas que comiencen
con un carácter en mayúsculas. Espera que el método devuelva true en estos casos, por lo que puede llamar al
método Assert.IsTrue. Del mismo modo, quiere proporcionar un número de cadenas que comiencen con algo que
no sea un carácter en mayúsculas. Espera que el método devuelva false en estos casos, por lo que puede llamar
al método Assert.IsFalse.
Dado que el método de biblioteca administra cadenas, quiere asegurarse también de que administra
correctamente una cadena vacía ( String.Empty ), una cadena válida que no tenga caracteres y cuyo Length sea 0, y
una cadena null que no se haya inicializado. Se puede llamar a StartsWithUpper directamente como un método
estático y pasar un argumento String único. También puede llamar a StartsWithUpper como método de extensión
en una variable de string asignada a null .
Definirá tres métodos, cada uno de los cuales llama a un método Assert para cada elemento de una matriz de
cadenas. Llamará a una sobrecarga de método que le permite especificar que se muestre un mensaje de error en
caso de error en la prueba. El mensaje identifica la cadena que causó el error.
Para crear los métodos de prueba:
1. Abra el archivo UnitTest1.cs y reemplace el código por el siguiente:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using UtilityLibraries;
namespace StringLibraryTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestStartsWithUpper()
{
// Tests that we expect to return true.
string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва" };
foreach (var word in words)
{
bool result = word.StartsWithUpper();
Assert.IsTrue(result,
String.Format("Expected for '{0}': true; Actual: {1}",
word, result));
}
}
[TestMethod]
public void TestDoesNotStartWithUpper()
{
// Tests that we expect to return false.
string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
"1234", ".", ";", " " };
foreach (var word in words)
{
bool result = word.StartsWithUpper();
Assert.IsFalse(result,
String.Format("Expected for '{0}': false; Actual: {1}",
word, result));
}
}
[TestMethod]
public void DirectCallWithNullOrEmpty()
{
// Tests that we expect to return false.
string[] words = { string.Empty, null };
foreach (var word in words)
{
bool result = StringLibrary.StartsWithUpper(word);
Assert.IsFalse(result,
String.Format("Expected for '{0}': false; Actual: {1}",
word == null ? "<null>" : word, result));
}
}
}
}
3. Presione Ctrl y haga clic en la prueba con errores, TestDoesNotStartWithUpper , y seleccione Mostrar el
panel de resultados en el menú contextual.
El panel Resultados muestra el mensaje generado por la aserción: "Error de Assert.IsFalse. Se esperaba
para "Error": false; real: True". Debido al error, no se probaron todas las cadenas de la matriz después de
"Error".
4. Quite la cadena "Error" que agregó en el paso 1. Vuelva a ejecutar la prueba y se superarán las pruebas.
2. En el panel Solución , presione Ctrl y haga clic en el proyecto StringLibrar y y seleccione Compilación
en el menú contextual para volver a compilar la biblioteca.
3. Ejecute de nuevo las pruebas unitarias.
Las pruebas se superan.
Depuración de pruebas
Puede usar el mismo proceso que se muestra en el Tutorial: Depuración de una aplicación de consola de .NET Core
con Visual Studio para Mac para depurar el código mediante el proyecto de prueba unitaria. En lugar de iniciar el
proyecto de aplicación ShowCase, presione Ctrl y haga clic en el proyecto StringLibrar yTests y seleccione
Iniciar depuración del proyecto en el menú contextual. Visual Studio inicia el proyecto de prueba con el
depurador asociado. La ejecución se detendrá en cualquier punto de interrupción que haya agregado al proyecto
de prueba o al código de la biblioteca subyacente.
Recursos adicionales
Pruebas unitarias en .NET Core y .NET Standard
Pasos siguientes
En este tutorial, ha realizado una prueba unitaria de una biblioteca de clases. Puede poner la biblioteca a
disposición de otros usuarios si la publica en NuGet como un paquete. Para obtener información sobre cómo
hacerlo, realice este tutorial de NuGet:
Crear y publicar un paquete (CLI de dotnet)
Si publica una biblioteca como un paquete NuGet, otros usuarios pueden instalarla y usarla. Para obtener
información sobre cómo hacerlo, realice este tutorial de NuGet:
Instalación y uso de un paquete en Visual Studio para Mac
No es necesario distribuir una biblioteca como un paquete. Se puede agrupar con una aplicación de consola que la
use. Para aprender a publicar una aplicación de consola, vea el tutorial anterior de esta serie:
Publicación de una aplicación de consola de .NET Core con Visual Studio para Mac
Explore estos tutoriales para obtener información
sobre las herramientas de .NET Core y el SDK de
.NET Core
16/09/2020 • 2 minutes to read • Edit Online
Los siguientes tutoriales están disponibles para aprender sobre .NET Core.
Temas avanzados
Procedimiento para crear bibliotecas
Prueba unitaria de una aplicación con xUnit
Prueba unitaria en C#, VB y F# con NUnit, xUnit o MSTest
Prueba unitaria dinámica con Visual Studio
Creación de plantillas para la CLI
Creación y uso de herramientas para la CLI
Creación de una aplicación con complementos
En este artículo se describen las novedades de .NET Core 3.1. Esta versión contiene ligeras mejoras de .NET Core
3.0, y se centra en pequeñas correcciones, pero importantes. La característica más importante sobre .NET Core 3.1
es que es una versión de soporte técnico a largo plazo (LTS).
Si usa Visual Studio 2019, tendrá que actualizar a Visual Studio 2019, versión 16.4 o posterior para trabajar con
proyectos de .NET Core 3.1. Para obtener información sobre las novedades de la versión 16.4 de Visual Studio, vea
Novedades de la versión 16.4 de Visual Studio 2019.
Visual Studio para Mac también admite e incluye .NET Core 3.1 en Visual Studio para Mac 8.4.
Para más información sobre la versión, consulte el anuncio de .NET Core 3.1.
Descargue .NET Core 3.1 y empiece a trabajar en Windows, macOS o Linux.
REL EA SE N OTA
Para obtener más información sobre la configuración de UseAppHost , vea Propiedades de MSBuild para
Microsoft.NET.Sdk.
Windows Forms
Solo Windows
WARNING
Hay cambios importantes en Windows Forms.
Se incluyeron controles heredados en Windows Forms que llevan un tiempo sin estar disponibles en el cuadro de
herramientas del diseñador de Visual Studio. Estos controles se volvieron a reemplazar por otros nuevos en .NET
Framework 2.0 y se han quitado del SDK de escritorio para .NET Core 3.1.
C O N T RO L EL IM IN A DO REEM P L A Z O REC O M EN DA DO A P I A SO C IA DA S EL IM IN A DA S
ContextMenu ContextMenuStrip
MainMenu MenuStrip
C O N T RO L EL IM IN A DO REEM P L A Z O REC O M EN DA DO A P I A SO C IA DA S EL IM IN A DA S
MenuItem ToolStripMenuItem
Se recomienda actualizar las aplicaciones a .NET Core 3.1 y pasar a los controles de reemplazo. Reemplazar los
controles es un proceso sencillo; se trata básicamente de "buscar y reemplazar" el tipo.
C++/CLI
Solo Windows
Se ha agregado compatibilidad con la creación de proyectos de C++/CLI (lo que también se conoce como "C++
administrado"). Los archivos binarios generados a partir de estos proyectos son compatibles con .NET Core 3.0 y
versiones posteriores.
Para agregar compatibilidad con C++/CLI a Visual Studio 2019 versión 16.4, instale la carga de trabajo Desarrollo
para el escritorio con C++. Esta carga de trabajo agrega dos plantillas a Visual Studio:
Biblioteca de clases de CLR (.NET Core)
Proyecto vacío de CLR (.NET Core)
Pasos siguientes
Revise los cambios importantes entre .NET Core 3.0 y 3.1.
Revise los cambios importantes de .NET Core 3.1 para aplicaciones de Windows Forms.
Novedades de .NET Core 3.0
16/09/2020 • 46 minutes to read • Edit Online
En este artículo se describen las novedades de .NET Core 3.0. Una de las mejoras más importantes es la
compatibilidad con las aplicaciones de Escritorio de Windows (solo Windows). Mediante el componente Escritorio
de Windows del SDK de .NET Core 3.0, puede portar sus aplicaciones de Windows Forms y
Windows Presentation Foundation (WPF). Para ser más precisos, el componente Escritorio de Windows solo se
admite e incluye en Windows. Para obtener más información, vea la sección Escritorio de Windows más adelante en
este artículo.
.NET Core 3.0 agrega compatibilidad con C# 8.0. Se recomienda encarecidamente usar Visual Studio 2019, versión
16.3 o una versión posterior, Visual Studio para Mac 8.3 o una versión posterior, o Visual Studio Code con la última
extensión de C# .
Descargue .NET Core 3.0 y empiece a trabajar ya en Windows, macOS o Linux.
Para obtener más información acerca de la versión, consulte el anuncio de .NET Core 3.0.
Microsoft considera .NET Core RC1 como listo para producción y es totalmente compatible. Si usa una versión
preliminar, debe pasar a la versión RTM para obtener soporte técnico continuo.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>
</Project>
Si usa Visual Studio, necesita Visual Studio 2019, ya que Visual Studio 2017 no admite .NET Standard 2.1 ni
.NET Core 3.0 .
Compilación e implementación
Archivos ejecutables predeterminados
.NET Core compila ahora archivos ejecutables dependientes del marco de forma predeterminada. Este
comportamiento es nuevo en las aplicaciones que usan una versión de .NET Core instalada globalmente.
Anteriormente, solo las implementaciones independientes generarían un archivo ejecutable.
Durante dotnet build o dotnet publish , se crea un archivo ejecutable (conocido como appHost ) que coincide
con el entorno y la plataforma del SDK que se usa. Estos ejecutables funcionan de la misma forma que los
ejecutables nativos:
Haga doble clic en el archivo ejecutable.
También puede iniciar la aplicación desde un símbolo del sistema directamente, como myapp.exe en Windows y
./myapp en Linux y macOS.
<PropertyGroup>
<UseAppHost>true</UseAppHost>
</PropertyGroup>
Para obtener más información sobre la configuración de UseAppHost , vea Propiedades de MSBuild para
Microsoft.NET.Sdk.
Archivos ejecutables de único archivo
El comando dotnet publish admite empaquetar la aplicación en un ejecutable de archivo único específico de la
plataforma. El archivo ejecutable es autoextraíble y contiene todas las dependencias (incluidas las nativas)
necesarias para ejecutar la aplicación. Cuando la aplicación se ejecuta por primera vez, se extrae en un directorio
que se basa en el nombre de la aplicación y el identificador de compilación. El inicio es más rápido cuando se
vuelve a ejecutar la aplicación. La aplicación no necesita extraerse por segunda vez a menos que se haya utilizado
una nueva versión.
Para publicar un ejecutable de archivo único, establezca PublishSingleFile en el proyecto o en la línea de
comandos con el comando dotnet publish :
<PropertyGroup>
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
<PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>
o bien
Para obtener más información sobre la publicación de archivos únicos, vea el documento de diseño del programa
de instalación de conjunto de archivos únicos.
Vinculación de ensamblados
El SDL de .NET Core 3.0 cuenta con una herramienta que puede reducir el tamaño de las aplicaciones mediante el
análisis de IL y el recorte de los ensamblados no usados.
Las aplicaciones independientes incluyen todo lo necesario para ejecutar el código, sin necesidad de instalar .NET
en el equipo host. Sin embargo, muchas veces, la aplicación solo requiere un pequeño subconjunto de marco para
que funcione, y otras bibliotecas que no se utilizan podrían quitarse.
.NET Core incluye ahora un valor que usará la herramienta Enlazador de IL para examinar el nivel de integridad de
la aplicación. Esta herramienta detecta el código que es necesario y, después, recorta las bibliotecas no utilizadas.
Esta herramienta puede reducir significativamente el tamaño de implementación de algunas aplicaciones.
Para habilitar esta herramienta, agregue el valor <PublishTrimmed> en el proyecto y publique una aplicación
independiente:
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
Por ejemplo, la nueva y básica plantilla de proyecto de consola "Hola mundo" que se incluye, cuando se publica,
tiene un tamaño aproximado de 70 MB. Mediante el uso de <PublishTrimmed> , ese tamaño se reduce a unos 30 MB.
Es importante tener en cuenta que las aplicaciones o marcos (incluidos ASP.NET Core y WPF) que usan la reflexión
o las características dinámicas relacionadas, se interrumpirán a menudo cuando se recorten. Esta interrupción se
produce porque el enlazador no conoce este comportamiento dinámico y no puede determinar qué tipos de marco
son necesarios para la reflexión. La herramienta Enlazador de IL puede configurarse para tener en cuenta este
escenario.
Por encima de todo lo demás, no olvide probar la aplicación después del recorte.
Para más información sobre la herramienta Enlazador de IL, vea la documentación o visite el repositorio
mono/linker.
Compilación en niveles
La compilación en niveles (TC) está activada de forma predeterminada con .NET Core 3.0. Esta característica permite
que el runtime use el compilador Just-In-Time (JIT) de forma más flexible para lograr un mejor rendimiento.
La principal ventaja de la compilación en niveles es que ofrece dos maneras de aplicar JIT a los métodos: con un
nivel de menor calidad, pero más rápido, o un nivel de mayor calidad, pero más lento. La calidad se refiere al grado
de optimización del método. La compilación en niveles contribuye a mejorar el rendimiento de una aplicación a
medida que pasa por distintas fases de ejecución, desde el inicio hasta el estado estable. Cuando la compilación en
niveles está deshabilitada, todos los métodos se compilan de una manera única que prima el rendimiento de
estado estable sobre el rendimiento de inicio.
Cuando la compilación en niveles está habilitada, se aplica el comportamiento siguiente a la compilación de
métodos al iniciar una aplicación:
Si el método tiene código compilado mediante Ahead-Of-Time, o ReadyToRun, se usa el código generado
previamente.
De lo contrario, el método se compila mediante JIT. Normalmente, estos métodos son genéricos con respecto a
los tipos de valor.
JIT rápido produce código de menor calidad (o menos optimizado) más rápidamente. En .NET Core 3.0,
JIT rápido está habilitado de forma predeterminada para los métodos que no contienen ningún bucle y
tiene preferencia durante el inicio.
JIT de optimización completa produce código de mayor calidad (o más optimizado) más lentamente. En el
caso de los métodos en los que no se use la compilación mediante JIT rápida (por ejemplo, si el método
tiene el atributo MethodImplOptions.AggressiveOptimization), se utilizará la compilación mediante JIT
totalmente optimizada.
En el caso de los métodos a los que se llama con frecuencia, el compilador Just-in-Time al final crea código
totalmente optimizado en segundo plano. Luego, el código optimizado reemplaza el código compilado
previamente de ese método.
El código generado con compilación mediante JIT rápida puede ejecutarse más lentamente, asignar más memoria o
usar más espacio de pila. Si hay problemas, puede deshabilitar JIT rápido con esta propiedad de MSBuild en el
archivo de proyecto:
<PropertyGroup>
<TieredCompilationQuickJit>false</TieredCompilationQuickJit>
</PropertyGroup>
Para deshabilitar completamente la compilación en niveles, use esta propiedad de MSBuild en el archivo de
proyecto:
<PropertyGroup>
<TieredCompilation>false</TieredCompilation>
</PropertyGroup>
TIP
Si cambia esta configuración en el archivo de proyecto, es posible que deba realizar una compilación limpia para que se refleje
la nueva configuración (elimine los directorios obj y bin y vuelva a compilar).
Para obtener más información sobre la configuración de la compilación en tiempo de ejecución, vea Opciones de
configuración del entorno de ejecución para compilación.
Imágenes ReadyToRun
Puede mejorar el tiempo de inicio de la aplicación .NET Core mediante la compilación de los ensamblados de
aplicación como el formato ReadyToRun (R2R). R2R es una forma de compilación Ahead Of Time (AOT).
Los binarios de R2R mejoran el rendimiento de inicio reduciendo la cantidad de trabajo que el compilador Just-In-
Time (JIT) debe llevar a cabo cuando se carga la aplicación. Los binarios contienen código nativo similar en
comparación con lo que generaría el compilador JIT. Sin embargo, los binarios de R2R son más grandes porque
contienen tanto el código de lenguaje intermedio (IL), que sigue siendo necesario para algunos escenarios, como la
versión nativa del mismo código. R2R solo está disponible cuando publica una aplicación independiente que tenga
como destino entornos de tiempo de ejecución específicos (RID), como Linux x64 o Windows x64.
Para compilar el proyecto como ReadyToRun, haga lo siguiente:
1. Agregue el valor <PublishReadyToRun> al proyecto:
<PropertyGroup>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>
2. Publique una aplicación independiente. Por ejemplo, este comando crea una aplicación independiente para
la versión de 64 bits de Windows:
Runtime y SDK
Puesta al día del runtime de versiones principales
.NET Core 3.0 presenta una característica opcional que permite poner la aplicación al día con la versión principal
más reciente de .NET Core. Además, se agregó una nueva configuración para controlar cómo se aplica la puesta al
día a la aplicación. Esto se puede configurar de las maneras siguientes:
Propiedad de archivo del proyecto: RollForward
Propiedad de archivo de configuración en tiempo de ejecución: rollForward
Variable de entorno: DOTNET_ROLL_FORWARD
Argumento de línea de comandos: --roll-forward
Debe especificarse uno de los valores siguientes. Si se omite la configuración, Minor es el valor predeterminado.
LatestPatch
Se pone al día con la última versión de revisión. Se deshabilita la puesta al día de versiones secundarias.
Minor
Se pone al día con la versión secundaria mínima superior, si no se encuentra la versión secundaria solicitada. Si
se encuentra la versión secundaria solicitada, se utiliza la directiva LatestPatch .
Major
Se pone al día con la versión secundaria mínima superior, y la versión secundaria mínima, si no se encuentra la
versión secundaria solicitada. Si se encuentra la versión principal solicitada, se utiliza la directiva Minor .
LatestMinor
Se pone al día con la última versión secundaria, aunque la versión secundaria solicitada esté presente. Se
destina a escenarios de hospedaje de componentes.
LatestMajor
Se pone al día con la última versión principal y la última versión secundaria, aunque la versión principal
solicitada esté presente. Se destina a escenarios de hospedaje de componentes.
Disable
No se pone al día. Solo se enlaza a la versión especificada. No se recomienda esta directiva para uso general, ya
que deshabilita la capacidad de puesta al día con las revisiones más recientes. Este valor solo se recomienda a
efectos de pruebas.
Además del valor Disable , todos los valores usarán la última versión de revisión disponible.
De forma predeterminada, si la versión solicitada (como se especifica en .runtimeconfig.json para la aplicación) es
una versión de lanzamiento, solo se tienen en cuenta las versiones de lanzamiento para la puesta al día. Se omiten
las versiones preliminares. Si no hay ninguna versión de lanzamiento que coincida, se tienen en cuenta las
versiones preliminares. Este comportamiento se puede cambiar estableciendo
DOTNET_ROLL_FORWARD_TO_PRERELEASE=1 , en cuyo caso siempre se tienen en cuenta todas las versiones.
WARNING
Si ha probado herramientas locales en la versión preliminar 1 de .NET Core 3.0, tales como la ejecución de
dotnet tool restore o de dotnet tool install , elimine la carpeta de caché de herramientas locales. En caso contrario,
las herramientas locales no funcionan en las versiones más recientes. Esta carpeta se encuentra en:
En macOS, Linux: rm -r $HOME/.dotnet/toolResolverCache
Las herramientas locales se basan en un nombre de archivo de manifiesto dotnet-tools.json del directorio actual.
Este archivo de manifiesto define las herramientas que estarán disponibles en esa carpeta y a continuación. Puede
distribuir el archivo de manifiesto con su código para asegurarse de que todo aquel que trabaje con su código
pueda restaurar y utilizar las mismas herramientas.
Para las herramientas locales y globales, se requiere una versión compatible del entorno de ejecución. Actualmente,
muchas herramientas de NuGet.org tienen como destino el entorno de ejecución de .NET Core 2.1. Para instalar
estas herramientas local o globalmente, aún tendría que instalar NET Core 2.1 Runtime.
Opciones nuevas de global.json
El archivo global.json tiene opciones nuevas que proporcionan más flexibilidad cuando se intenta definir qué
versión de la SDK de .NET Core se usa. Las opciones nuevas son:
allowPrerelease : Indica si la resolución del SDK debe tener en cuenta las versiones preliminares a la hora de
seleccionar la versión del SDK que se va a usar.
rollForward : indica la directiva de puesta al día que se va a usar al seleccionar una versión del SDK, ya sea
como reserva si falta una versión específica del SDK o como una directiva para usar una versión superior.
Para más información sobre los cambios, incluidos los valores predeterminados, los valores admitidos y reglas de
coincidencia nuevas, consulte la información general de global.json.
Tamaños del montón de recolección de elementos no utilizados más pequeños
Se ha reducido el tamaño predeterminado del montón del recolector de elementos no utilizados, lo que se traduce
en que .NET Core usa menos memoria. Este cambio se adapta mejor al presupuesto de asignación de generación 0
con tamaños de caché de procesador moderno.
Compatibilidad con Large Pages de recolección de elementos no utilizados
Large Pages (también conocida como Huge Pages en Linux) es una característica en la que el sistema operativo es
capaz de establecer regiones de memoria más grandes que el tamaño de página nativo (a menudo, 4 K) para
mejorar el rendimiento de la aplicación que solicita estas páginas grandes.
Ahora, el recolector de elementos no utilizados puede configurarse con el valor GCLargePages como
característica opcional para elegir la asignación de páginas grandes en Windows.
Visual Studio 2019 agrega plantillas de nuevo proyecto para WPF y Windows Forms de .NET Core 3.0.
Para obtener más información sobre cómo migrar una aplicación existente de .NET Framework, vea los artículos
sobre cómo portar proyectos de WPF y cómo portar proyectos de Windows Forms.
PPP alto de WinForms
En .NET Core, las aplicaciones de Windows Forms pueden establecer el modo de valores altos de PPP con
Application.SetHighDpiMode(HighDpiMode). El método SetHighDpiMode establece el modo de valores altos de PPP
correspondiente a menos que la opción se haya establecido por otros medios como App.Manifest o P/Invoke antes
de Application.Run .
Los posibles valores de highDpiMode , expresados por la enumeración System.Windows.Forms.HighDpiMode son:
DpiUnaware
SystemAware
PerMonitor
PerMonitorV2
DpiUnawareGdiScaled
Para más información sobre los modos de valores altos de PPP, consulte el artículo sobre desarrollo de aplicaciones
de escritorio con valores altos de PPP en Windows.
Creación de componentes COM
En Windows, ahora puede crear componentes COM administrados invocables. Esta capacidad es fundamental para
usar .NET Core con modelos de complemento COM, así como para ofrecer paridad con .NET Framework.
A diferencia de .NET Framework, donde se utilizó mscoree.dll como servidor COM, .NET Core agregará un archivo
dll de inicio nativo al directorio bin al compilar el componente COM.
Para ver un ejemplo de cómo crear un componente COM y usarlo, consulte la demostración de COM.
Interoperabilidad nativa de Windows
Windows ofrece una API nativa enriquecida en forma de API de C sin formato, COM y WinRT. Mientras que
.NET Core admite P/Invoke , .NET Core 3.0 agrega la capacidad de API COM CoCreate y API WinRT Activate .
Para obtener un ejemplo de código, vea la demostración de Excel.
Implementación de MSIX
MSIX es un nuevo formato de paquete de aplicación de Windows. Se puede usar para implementar aplicaciones de
escritorio de .NET Core 3.0 en Windows 10.
El proyecto de paquete de aplicación de Windows, disponible en Visual Studio 2019, le permite crear paquetes de
MSIX con aplicaciones de .NET Core independientes.
El archivo del proyecto de .NET Core debe especificar los tiempos de ejecución admitidos en la propiedad
<RuntimeIdentifiers> :
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
Mejoras de Linux
SerialPort para Linux
.Net Core 3.0 proporciona compatibilidad básica para System.IO.Ports.SerialPort en Linux.
Anteriormente, .NET Core solo admitía el uso de SerialPort en Windows.
Para obtener más información sobre la compatibilidad limitada para el puerto de serie en Linux, vea Problema
#33146 de GitHub.
Docker y límites de memoria de cgroup
La ejecución de .NET Core 3.0 en Linux con Docker funciona mejor con límites de memoria de cgroup. La ejecución
de un contenedor de Docker con límites de memoria, como con docker run -m , cambia el comportamiento de
.NET Core.
Tamaño predeterminado del montón del recolector de elementos no utilizados (GC): máximo de 20 MB o 75 %
del límite de memoria en el contenedor.
Puede establecerse el tamaño explícito como número absoluto o porcentaje del límite de cgroup.
El tamaño mínimo del segmento reservado por el montón de GC es de 16 MB. Con este tamaño se reduce el
número de montones que se crean en las máquinas.
Compatibilidad de GPIO con Raspberry Pi
Se han publicado dos paquetes en NuGet que puede usar para la programación de GPIO:
System.Device.Gpio
Iot.Device.Bindings
Los paquetes GPIO incluyen interfaces API para dispositivos GPIO, SPI, I2C y PWM. El paquete de enlaces de IoT
incluye enlaces de dispositivos. Para obtener más información, vea el repositorio de GitHub de los dispositivos.
Compatibilidad con ARM64 para Linux
.NET Core 3.0 agrega compatibilidad con ARM64 para Linux. El principal caso de uso de ARM64 son escenarios de
IoT. Para obtener más información, vea el artículo sobre el estado de ARM64 de .NET Core.
Hay imágenes de docker para .NET Core en ARM64 disponibles para Alpine, Debian y Ubuntu.
NOTE
Windows aún no ofrece soporte técnico para ARM64 .
Seguridad
TLS 1.3 y OpenSSL 1.1.1 en Linux
.NET Core aprovecha ahora la ventaja de la compatibilidad con TLS 1.3 en OpenSSL 1.1.1, cuando está disponible
en un entorno determinado. Con TLS 1.3:
Se han mejorado los tiempos de conexión con menores recorridos de ida y vuelta necesarios entre el cliente y
servidor.
Se ha mejorado la seguridad gracias a la eliminación de varios algoritmos criptográficos obsoletos y no seguros.
Cuando está disponible, .NET Core 3.0 utiliza OpenSSL 1.1.1 , OpenSSL 1.1.0 o OpenSSL 1.0.2 en un sistema
Linux. Si OpenSSL 1.1.1 está disponible, los tipos System.Net.Security.SslStream y System.Net.Http.HttpClient,
utilizarán TLS 1.3 (suponiendo que el cliente y el servidor admitan TLS 1.3 ).
IMPORTANT
Windows y macOS aún no admiten TLS 1.3 . .NET Core 3.0 será compatible con TLS 1.3 en estos sistemas operativos
cuando haya disponible soporte técnico.
El siguiente ejemplo de C# 8.0 muestra .NET Core 3.0 en Ubuntu 18.10 al conectarse a https://www.cloudflare.com:
using System;
using System.Net.Security;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace whats_new
{
public static class TLS
{
public static async Task ConnectCloudFlare()
{
var targetHost = "www.cloudflare.com";
await sslStream.AuthenticateAsClientAsync(targetHost);
await Console.Out.WriteLineAsync($"Connected to {targetHost} with {sslStream.SslProtocol}");
}
}
}
Cifrados de criptografía
.NET 3.0 agrega compatibilidad con los cifrados AES-GCM y AES-CCM , que se implementan con
System.Security.Cryptography.AesGcm y System.Security.Cryptography.AesCcm respectivamente. Estos dos
algoritmos son algoritmos AEAD (Authenticated Encryption with Associated Data).
El código siguiente muestra cómo utilizar cifrado AesGcm para cifrar y descifrar datos aleatorios.
using System;
using System.Linq;
using System.Security.Cryptography;
namespace whats_new
{
public static class Cipher
{
public static void Run()
{
// key should be: pre-known, derived, or transported via another channel, such as RSA encryption
byte[] key = new byte[16];
RandomNumberGenerator.Fill(key);
using System;
using System.Security.Cryptography;
namespace whats_new
{
public static class RSATest
{
public static void Run(string keyFile)
{
using var rsa = RSA.Create();
Existe también el tipo System.Range, que consta de dos valores Index , uno para el inicio y otro para el final, y se
puede escribir con una expresión de intervalo x..y (C#). Luego puede crear un índice con un Range , lo que
genera un segmento:
Además de poder ejecutar await foreach , también puede crear iteradores asincrónicos, por ejemplo, uno que
devuelva un enumerador IAsyncEnumerable/IAsyncEnumerator en el que pueda ejecutar await y yield . Para los
objetos que deban eliminarse, puede usar IAsyncDisposable , que implementan varios tipos BCL, como Stream y
Timer .
ILogB(Double)
Corresponde a la operación IEEE logB que devuelve un valor entero, devuelve el logaritmo en base 2
integral del parámetro de entrada. Este método es efectivamente el mismo que floor(log2(x)) , pero con
errores de redondeo mínimo.
ScaleB(Double, Int32)
Corresponde a la operación IEEE scaleB que toma un valor integral, devuelve eficazmente x * pow(2, n) ,
pero se realiza con errores de redondeo mínimo.
Log2(Double)
Corresponde a la operación IEEE log2 . Devuelve el logaritmo de base 2. Minimiza el error de redondeo.
FusedMultiplyAdd(Double, Double, Double)
Corresponde a la operación IEEE fma . Realiza una multiplicación y suma fusionadas. Es decir, realiza
(x * y) + z como operación única, de forma que se minimiza el error de redondeo. Un ejemplo es
FusedMultiplyAdd(1e308, 2.0, -1e308) , que devuelve 1e308 . La operación (1e308 * 2.0) - 1e308 regular
devuelve double.PositiveInfinity .
CopySign(Double, Double)
Corresponde a la operación IEEE copySign . Devuelve el valor de x , pero con el signo de y .
Elementos intrínsecos dependientes de la plataforma .NET
Se han agregado API que permiten el acceso a determinadas instrucciones CPU orientadas al rendimiento, como
los conjuntos de instrucciones de manipulación de bits o SIMD . Estas instrucciones pueden ayudar a
conseguir importantes mejoras de rendimiento en ciertos escenarios, como el procesamiento de datos con
eficiencia en paralelo.
En su caso, las bibliotecas de .NET han comenzado a utilizar estas instrucciones para mejorar el rendimiento.
Para obtener más información, consulte el artículo sobre elementos intrínsecos dependientes de la plataforma .NET.
API de versión mejoradas de .NET Core
A partir de .NET Core 3.0, las API de versión incluidas con .NET Core ahora devuelven la información que se espera.
Por ejemplo:
System.Console.WriteLine($"Environment.Version: {System.Environment.Version}");
// Old result
// Environment.Version: 4.0.30319.42000
//
// New result
// Environment.Version: 3.0.0
System.Console.WriteLine($"RuntimeInformation.FrameworkDescription:
{System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription}");
// Old result
// RuntimeInformation.FrameworkDescription: .NET Core 4.6.27415.71
//
// New result (notice the value includes any preview release information)
// RuntimeInformation.FrameworkDescription: .NET Core 3.0.0-preview4-27615-11
WARNING
Cambio importante. Se trata técnicamente de un cambio importante, porque ha cambiado el esquema de control de
versiones.
// HTTP/1.1 request
using (var response = await client.GetAsync("/"))
Console.WriteLine(response.Content);
// HTTP/2 request
using (var request = new HttpRequestMessage(HttpMethod.Get, "/") { Version = new Version(2, 0) })
using (var response = await client.SendAsync(request))
Console.WriteLine(response.Content);
En segundo lugar, puede cambiar HttpClient para usar HTTP/2 de forma predeterminada:
// HTTP/2 is default
using (var response = await client.GetAsync("/"))
Console.WriteLine(response.Content);
Muchas veces cuando está desarrollando una aplicación, desea utilizar una conexión no cifrada. Si sabe que el
punto de conexión de destino utilizará HTTP/2, puede activar las conexiones no cifradas para HTTP/2. Puede
activarlas estableciendo la variable de entorno DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP2UNENCRYPTEDSUPPORT
en 1 o habilitándolas en el contexto de la aplicación:
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
Pasos siguientes
Revise los cambios importantes entre .NET Core 2.2 y 3.0.
Revise los cambios importantes de .NET Core 3.0 para aplicaciones de Windows Forms.
Novedades de .NET Core 2.2
16/09/2020 • 8 minutes to read • Edit Online
.NET Core 2.2 incluye mejoras en la implementación de aplicaciones, en el control de eventos de los servicios en
tiempo de ejecución, en la autenticación de bases de datos SQL de Azure, en el rendimiento del compilador JIT y la
inyección de código antes de la ejecución del método Main .
Principal
Control de eventos en los ser vicios en tiempo de ejecución
A menudo es posible que desee supervisar el uso que hace la aplicación de los servicios de tiempo de ejecución,
como GC, JIT y ThreadPool, para comprender cómo afectan a la aplicación. En los sistemas Windows, esto se hace
normalmente mediante la supervisión de los eventos ETW del proceso actual. Aunque este método sigue
funcionando bien, no siempre es posible usar ETW si la ejecución se realiza en un entorno con pocos privilegios o
en Linux o macOS.
A partir de .NET Core 2.2, ahora se pueden consumir eventos CoreCLR utilizando la clase
System.Diagnostics.Tracing.EventListener. Estos eventos describen el comportamiento de esos servicios en tiempo
de ejecución como la interoperabilidad, ThreadPool, JIT y GC. Estos son los mismos eventos que se exponen como
parte del proveedor ETW de CoreCLR. De esta forma, las aplicaciones pueden consumir estos eventos o usar un
mecanismo de transporte para enviarlos a un servicio de agregación de telemetría. Puede ver cómo suscribirse a
eventos en el código de ejemplo siguiente:
internal sealed class SimpleEventListener : EventListener
{
// Called whenever an EventSource is created.
protected override void OnEventSourceCreated(EventSource eventSource)
{
// Watch for the .NET runtime EventSource and enable all of its events.
if (eventSource.Name.Equals("Microsoft-Windows-DotNETRuntime"))
{
EnableEvents(eventSource, EventLevel.Verbose, (EventKeywords)(-1));
}
}
Además, .NET Core 2.2 agrega las dos propiedades siguientes a la clase EventWrittenEventArgs para proporcionar
información adicional sobre los eventos ETW:
EventWrittenEventArgs.OSThreadId
EventWrittenEventArgs.TimeStamp
Datos
Autenticación de AAD en bases de datos SQL de Azure con la propiedad SqlConnection.AccessToken
A partir de .NET Core 2.2, puede usarse un token de acceso emitido por Azure Active Directory para autenticarse en
una base de datos SQL de Azure. Para admitir tokens de acceso, la propiedad AccessToken se ha agregado a la clase
SqlConnection. Para aprovechar las ventajas de la autenticación de AAD, descargue la versión 4.6 del paquete
System.Data.SqlClient NuGet. Para poder usar la característica, puede obtener el valor del token de acceso mediante
la biblioteca de autenticación de Active Directory para .NET contenida en el paquete NuGet
Microsoft.IdentityModel.Clients.ActiveDirectory .
Tiempo de ejecución
Inyección de código antes de ejecutar el método Main
A partir de .NET Core 2.2, puede usar un enlace de inicio para inyectar código antes de ejecutar el método Main de
la aplicación. Los enlaces de inicio permiten a un host personalizar el comportamiento de las aplicaciones una vez
que se hayan implementado sin necesidad de volver a compilar o cambiar la aplicación.
Los proveedores de hospedaje deberían definir la directiva y la configuración personalizadas, incluida la
configuración que posiblemente influya en el comportamiento de carga del punto de entrada principal, como el
comportamiento System.Runtime.Loader.AssemblyLoadContext . El enlace puede usarse para configurar la
inyección de telemetría o el seguimiento, configurar las devoluciones de llamada para el control, así como definir
otros comportamientos dependientes del entorno. El enlace es independiente del punto de entrada, por lo que no
es necesario modificar el código de usuario.
Consulte Host startup hook (Hospedaje del enlace de inicio) para obtener más información.
Vea también
Novedades de .NET Core 3.1
Novedades de ASP.NET Core 2.2
Novedades de EF Core 2.2
Novedades de .NET Core 2.1
16/09/2020 • 21 minutes to read • Edit Online
.NET Core 2.1 incluye mejoras y características nuevas en las áreas siguientes:
Herramientas
Puesta al día
Implementación
Paquete de compatibilidad de Windows
Mejoras de la compilación JIT
Cambios en la API
Tooling
El SDK de .NET Core 2.1 (v 2.1.300), el conjunto de herramientas incluidas con .NET Core 2.1, incluye los siguientes
cambios y mejoras:
Mejoras en el rendimiento de la compilación
Un aspecto fundamental de .NET Core 2.1 es mejorar el rendimiento del tiempo de compilación, especialmente
para compilaciones incrementales. Estas mejoras de rendimiento se aplican a las compilaciones de línea de
comandos mediante dotnet build y a las compilaciones de Visual Studio. Algunas áreas individuales de mejora
incluyen:
Para la resolución de activos del paquete, solo se resuelven los activos utilizados por una compilación en
lugar de todos los activos.
Almacenamiento en caché de referencias de ensamblado.
Uso de servidores de compilación de SDK de larga ejecución, que son procesos que se extienden a través de
invocaciones dotnet build individuales. Eliminan la necesidad de compilar mediante JIT grandes bloques
de código cada vez que se ejecuta dotnet build . Los procesos del servidor de compilación se pueden
terminar automáticamente con el siguiente comando:
dotnet watch proporciona un monitor del sistema de archivos que espera que un archivo cambie antes de
ejecutar un conjunto designado de comandos. Por ejemplo, el siguiente comando vuelve a generar
automáticamente el proyecto actual y genera un resultado detallado cada vez que cambie un archivo en él:
Tenga en cuenta que la opción -- precede a la opción --verbose . Delimita las opciones pasadas
directamente al comando dotnet watch de los argumentos que se pasan al proceso dotnet secundario.
Sin él, la opción --verbose se aplica al comando dotnet watch , no al comando dotnet build .
Para más información, consulte Desarrollar aplicaciones ASP.NET Core con un monitor de archivos.
dotnet dev-certs genera y administra los certificados que se usan durante el desarrollo de aplicaciones de
ASP.NET Core.
dotnet user-secrets administra los secretos en un almacén de secretos de usuario en aplicaciones de
ASP.NET Core.
dotnet sql-cachecrea una tabla e índices en una base de datos de Microsoft SQL Server que se usará para
el almacenamiento en caché distribuido.
dotnet ef es una herramienta para administrar bases de datos, objetos DbContext y migraciones en las
aplicaciones de Entity Framework Core. Para obtener más información, vea EF Core .NET Command-line
Tools (Herramienta de la línea de comandos de .NET de EF Core).
Herramientas globales
.NET core 2.1 es compatible con Herramientas globales: es decir, las herramientas personalizadas que están
disponibles globalmente desde la línea de comandos. El modelo de extensibilidad en versiones previas de
.NET Core permitió que las herramientas personalizadas estuvieran disponibles en cada proyecto solo utilizando
DotnetCliToolReference .
Para instalar una herramienta global, use el comando dotnet tool install. Por ejemplo:
Una vez instalada, la herramienta se puede ejecutar desde la línea de comandos especificando el nombre de la
herramienta. Para más información, vea Información general sobre las herramientas globales de .NET Core.
Administración de herramientas con el comando dotnet tool
En el SDK de .NET Core 2.1, todas las operaciones de herramientas utilizan el comando dotnet tool . Están
disponibles las siguientes opciones:
dotnet tool install para instalar una herramienta.
dotnet tool update para desinstalar y reinstalar una herramienta, lo cual la actualiza eficazmente.
dotnet tool list para enumerar las herramientas instaladas actualmente.
dotnet tool uninstall para desinstalar las herramientas instaladas actualmente.
Puesta al día
Todas las aplicaciones de .NET Core a partir de .NET Core 2.0 se ponen al día automáticamente a la versión
secundaria más reciente instalada en un sistema.
A partir de .NET Core 2.0, si la versión de .NET Core que se creó una aplicación no está presente en tiempo de
ejecución, la aplicación se ejecuta automáticamente en la versión secundaria instalada más reciente de .NET Core.
En otras palabras, si una aplicación se compila con .NET Core 2.0, y .NET Core 2.0 no está presente en el sistema
host pero sí lo está .NET Core 2.1, la aplicación se ejecuta con .NET Core 2.1.
IMPORTANT
Este comportamiento de puesta al día no se aplica para versiones preliminares. De forma predeterminada, tampoco se aplica
a las versiones principales, pero se puede cambiar con las opciones siguientes.
Este comportamiento se puede modificar si se cambia la configuración para la puesta al día en los marcos de
trabajo compartidos que no sean candidatos. Los valores disponibles son los siguientes:
0 : se deshabilita el comportamiento de puesta al día de las versiones secundarias. Con este valor, una
aplicación compilada para .NET Core 2.0.0 se pondrá al día a .NET Core 2.0.1, pero no a .NET Core 2.2.0 ni .NET
Core 3.0.0.
1 : se habilita el comportamiento de puesta al día de las versiones secundarias. Este es el valor
predeterminado de la opción. Con este valor, una aplicación compilada para .NET Core 2.0.0 se pondrá al día a
.NET Core 2.0.1 o .NET Core 2.2.0, en función de la versión instalada, pero no a .NET Core 3.0.0.
2 : se habilita el comportamiento de puesta al día de las versiones principales y secundarias. Si se establece, se
tienen en cuenta incluso versiones principales diferentes, por lo que una aplicación compilada para .NET Core
2.0.0 se pondrá al día a .NET Core 3.0.0.
Esta configuración se puede modificar de estas tres maneras:
Si se establece la variable de entorno DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX en el valor deseado.
Agregue la línea siguiente con el valor deseado al archivo .runtimeconfig.json:
"rollForwardOnNoCandidateFx" : 0
Cuando se usa la CLI de .NET Core, si se agrega la opción siguiente con el valor deseado a un comando de
.NET Core como run :
La puesta al día de versiones de revisión es independiente de esta configuración y se realiza después de aplicar la
puesta al día de cualquier versión principal o secundaria posible.
Implementación
Mantenimiento de aplicaciones independientes
dotnet publish ahora publica aplicaciones independientes con una versión en tiempo de ejecución con
mantenimiento. Cuando publica una aplicación independiente con el SDK 2.1 de .NET Core (v. 2.1.300), su
aplicación incluye la última versión de tiempo de ejecución con mantenimiento conocida por ese SDK. Cuando
actualice a la versión más reciente del SDK, publicará con la versión más reciente del tiempo de ejecución de
.NET Core. Esto se aplica para tiempos de ejecución de .NET Core 1.0 y versiones posteriores.
La publicación independiente se basa en las versiones del tiempo de ejecución en NuGet.org. No necesita tener el
tiempo de ejecución con mantenimiento en su máquina.
Con el SDK de .NET Core 2.0, las aplicaciones independientes se publican con el tiempo de ejecución de .NET Core
2.0.0 a menos que se especifique una versión diferente a través de la propiedad RuntimeFrameworkVersion . Con
este nuevo comportamiento, ya no será necesario configurar esta propiedad para seleccionar una versión de
tiempo de ejecución más alta para una aplicación independiente. El enfoque más sencillo a partir de ahora es
publicar siempre con el SDK de .NET Core 2.1 (v. 2.1.300).
Para obtener más información, vea Puesta al día del runtime de implementación autocontenida.
COMPlus_TieredCompilation="1"
Para utilizar la compilación por niveles por proyecto, agregue la propiedad <TieredCompilation> a la
sección <PropertyGroup> del archivo de proyecto de MSBuild, como se muestra en el ejemplo siguiente:
<PropertyGroup>
<!-- other property definitions -->
<TieredCompilation>true</TieredCompilation>
</PropertyGroup>
Cambios en la API
Span<T> y Memory<T>
.NET Core 2.1 incluye algunos tipos nuevos que hacen que trabajar con matrices y otros tipos de memoria sea
mucho más eficiente. Estos nuevos tipos incluyen:
System.Span<T> y System.ReadOnlySpan<T>.
System.Memory<T> y System.ReadOnlyMemory<T>.
Sin estos tipos, al pasar tales elementos como una porción de una matriz o una sección de un búfer de memoria,
debe hacer una copia de una parte de los datos antes de pasarlo a un método. Estos tipos proporcionan una vista
virtual de los datos que elimina la necesidad de ejecutar operaciones adicionales de copia y asignación de
memoria.
En el ejemplo siguiente se usa una instancia de Span<T> y Memory<T> para proporcionar una vista virtual de 10
elementos de una matriz.
using System;
class Program
{
static void Main()
{
int[] numbers = new int[100];
for (int i = 0; i < 100; i++)
{
numbers[i] = i * 2;
}
Module Program
Sub Main()
Dim numbers As Integer() = New Integer(99) {}
For i As Integer = 0 To 99
numbers(i) = i * 2
Next
Compresión de Brotli
.NET Core 2.1 agrega compatibilidad con la compresión y descompresión de Brotli. Brotli es un algoritmo de
compresión sin pérdida de datos de uso general que se define en RFC 7932 y es compatible con la mayoría de los
exploradores web y servidores web principales. Puede usar la clase System.IO.Compression.BrotliStream basada
en secuencias o las clases System.IO.Compression.BrotliEncoder y System.IO.Compression.BrotliDecoder basadas
en intervalos de alto rendimiento. En el ejemplo siguiente se muestra la compresión con la clase BrotliStream:
AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);
AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", False)
También puede usar una variable de entorno para optar por no usar implementaciones de sockets basadas en
SocketsHttpHandler. Para ello, configure DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER en false o en 0.
En Windows, también puede optar por usar System.Net.Http.WinHttpHandler, que se basa en una implementación
nativa, o la clase SocketsHttpHandler pasando una instancia de la clase al constructor HttpClient.
En Linux y macOS, solo se puede configurar HttpClient para cada proceso. En Linux, deberá implementar libcurl si
desea usar la antigua implementación de HttpClient. (Se instala con .NET Core 2.0).
Cambios importantes
Para obtener más información sobre los cambios importantes, vea Cambios importantes para la migración de la
versión 2.0 a la 2.1.
Vea también
Novedades de .NET Core 3.1
Novedades de EF Core 2.1
Novedades de ASP.NET Core 2.1
Novedades de .NET Core 2.0
18/03/2020 • 13 minutes to read • Edit Online
.NET Core 2.0 incluye mejoras y características nuevas en las áreas siguientes:
Herramientas
Compatibilidad con lenguajes
Mejoras en la plataforma
Cambios en la API
Integración de Visual Studio
Mejoras en la documentación
Tooling
dotnet restore se ejecuta de manera implícita
En las versiones anteriores de .NET Core, era necesario ejecutar el comando dotnet restore para descargar
dependencias inmediatamente después de crear un proyecto nuevo con el comando dotnet new, así como también
cada vez que se agregaba una dependencia nueva al proyecto.
No es necesario ejecutar dotnet restore porque lo ejecutan implícitamente todos los comandos que necesitan que
se produzca una restauración, como dotnet new , dotnet build , dotnet run , dotnet test , dotnet publish y
dotnet pack . Para deshabilitar la restauración implícita, use la opción --no-restore .
El comando dotnet restore sigue siendo válido en algunos escenarios donde tiene sentido realizar una
restauración explícita, como las compilaciones de integración continua en Azure DevOps Services o en los sistemas
de compilación que necesitan controlar explícitamente cuándo se produce la restauración.
Para obtener información sobre cómo administrar fuentes de NuGet, vea la documentación de dotnet restore .
También puede deshabilitar la invocación automática de dotnet restore si pasa el modificador --no-restore a los
comandos new , run , build , publish , pack y test .
Redestinación a .NET Core 2.0
Si el SDK de .NET Core 2.0 SDK está instalado, los proyectos que tienen .NET Core 1.x como destino se pueden
redestinar a .NET Core 2.0.
Para redestinar a .NET Core 2.0, edite el archivo del proyecto cambiando el valor del elemento <TargetFramework>
(o del elemento <TargetFrameworks> , si tiene más de un destino en el archivo del proyecto) de 1.x a 2.0:
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
También puede redestinar las bibliotecas de .NET Standard a .NET Standard 2.0 del mismo modo:
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
Para más información sobre cómo migrar el proyecto a .NET Core 2.0, consulte el artículo sobre migración de
ASP.NET Core 1.x a ASP.NET Core 2.0.
Mejoras en la plataforma
.NET Core 2.0 incluye varias características que facilitan la instalación de .NET Core y su uso en sistemas operativos
compatibles.
.NET Core para Linux es una sola implementación
.NET Core 2.0 ofrece una sola implementación de Linux que funciona en varias distribuciones de Linux. .NET Core
1.x requería que descargara una implementación de Linux específica para la distribución.
También puede desarrollar aplicaciones que tienen Linux como destino como un solo sistema operativo. .NET Core
1.x requería que se tuviera cada distribución de Linux como destino de forma independiente.
Compatibilidad con las bibliotecas de cifrado de Apple
.NET Core 1.x en macOS requería la biblioteca de cifrado del kit de herramientas OpenSSL. .NET Core 2.0 usa las
bibliotecas de cifrado de Apple y no requiere OpenSSL, por lo que ya no es necesario instalarlo.
Mejoras en la documentación
Arquitectura de aplicación de .NET
Arquitectura de aplicación de .NET le permite acceder a un conjunto de libros electrónicos que ofrecen orientación,
procedimientos recomendados y aplicaciones de ejemplo cuando use .NET para compilar:
Microservicios y contenedores Docker
Aplicaciones web con ASP.NET
Aplicaciones móviles con Xamarin
Aplicaciones que se implementan en la nube con Azure
Vea también
Novedades de ASP.NET Core 2.0
Novedades de .NET Standard
09/04/2020 • 7 minutes to read • Edit Online
.NET Standard es una especificación formal que define un conjunto de API con control de versiones que debe estar
disponible en las implementaciones de .NET que cumplen con esa versión del estándar. .NET Standard está dirigido
a los desarrolladores de bibliotecas. Una biblioteca que tenga como destino una versión estándar de .NET Standard
se puede usar en cualquier implementación de .NET Framework, .NET Core o Xamarin que admita esa versión del
estándar.
.NET Standard se incluye con el SDK de .NET Core, así como con Visual Studio cuando se selecciona la carga de
trabajo de .NET Core.
Vea también
.NET Standard
Introducing .NET Standard (Introducción a .NET Standard)
Información general sobre el SDK de .NET Core
04/05/2020 • 3 minutes to read • Edit Online
El SDK de .NET Core es un conjunto de bibliotecas y herramientas que permiten a los desarrolladores crear
aplicaciones y bibliotecas de .NET Core. Contiene los siguientes componentes que se usan para compilar y ejecutar
aplicaciones:
La CLI de .NET Core.
Bibliotecas y runtime de .NET Core.
El controlador dotnet .
Vea también
Información general sobre la CLI de .NET Core
Introducción a la creación de versiones de .NET Core
Cómo quitar los componentes .NET Core Runtime y SDK
Selección de la versión de .NET Core que se va a usar
Información general sobre la CLI de .NET Core
16/09/2020 • 5 minutes to read • Edit Online
Comandos de la CLI
De forma predeterminada, se instalan los siguientes comandos:
Comandos básicos
new
restore
build
publish
run
test
vstest
pack
migrate
clean
sln
help
store
Comandos avanzados
nuget delete
nuget locals
nuget push
msbuild
dotnet install script
Las herramientas son aplicaciones de consola que se instalan mediante paquetes NuGet y se invocan desde
el símbolo del sistema. Puede encargarse de escribir las herramientas o instalar las escritas por terceros.
Las herramientas también se denominan herramientas globales, herramientas de ruta de acceso de
herramientas y herramientas locales. Para obtener más información, vea Información general sobre las
herramientas de .NET Core.
Estructura de comandos
La estructura de comandos de la CLI consta del controlador ("dotnet"), el comando y posiblemente de los
argumentos de comandos y otras opciones. Este patrón se puede ver en la mayoría de las operaciones de
la CLI, como la creación de una nueva aplicación de consola y su ejecución desde la línea de comandos,
como muestran los siguientes comandos cuando se ejecutan desde un directorio denominado my_app:
Controlador
El controlador se denomina dotnet y tiene dos responsabilidades, ejecutar una aplicación dependiente del
marco o ejecutar un comando.
Para ejecutar una aplicación dependiente del marco, especifique la aplicación después del controlador, por
ejemplo, dotnet /path/to/my_app.dll . Cuando ejecute el comando desde la carpeta donde reside la DLL de
la aplicación, simplemente ejecute dotnet my_app.dll . Si quiere usar una versión específica del entorno de
ejecución .NET Core, use la opción --fx-version <VERSION> (consulte la referencia del comando dotnet).
Cuando se proporciona un comando para el controlador, dotnet.exe inicia el proceso de ejecución del
comando de la CLI. Por ejemplo:
dotnet build
En primer lugar, el controlador determina la versión de SDK que se debe usar. Si no hay ningún archivo
global.json, se usa la última versión del SDK disponible. Podría tratarse de una versión preliminar o de una
versión estable, en función de lo último que esté disponible en el equipo. Una vez determinada la versión
del SDK, se ejecutará el comando.
Comando
El comando realiza una acción. Por ejemplo, dotnet build compila código. dotnet publish publica el
código. Los comandos se implementan como una aplicación de consola usando una convención
dotnet {command} .
Argumentos
Los argumentos que se pasan en la línea de comandos son los argumentos para el comando invocado. Por
ejemplo, cuando ejecuta dotnet publish my_app.csproj , el argumento my_app.csproj indica el proyecto
que se publicará y se pasa al comando publish .
Opciones
Las opciones que se pasan en la línea de comandos son las opciones para el comando que se invoca. Por
ejemplo, cuando ejecuta dotnet publish --output /build_output , la opción --output y su valor se pasan al
comando publish .
Vea también
Repositorio de GitHub dotnet/sdk
.NET Core installation guide (Guía de instalación de .NET Core)
comando dotnet
16/09/2020 • 23 minutes to read • Edit Online
NOMBRE
dotnet : controlador genérico de la CLI de .NET Core.
Sinopsis
Para obtener información sobre los comandos disponibles y el entorno:
dotnet -h|--help
--roll-forward está disponible a partir de .NET Core 3.x. En .NET Core 2.x, use
--roll-forward-on-no-candidate-fx .
Descripción
El comando dotnet tiene dos funciones:
Proporciona comandos para trabajar con proyectos de .NET Core.
Por ejemplo, dotnet build compila un proyecto. Cada comando define sus propias opciones y
argumentos. Todos los comandos admiten la opción --help para ver una breve documentación sobre
cómo usar el comando.
Ejecuta aplicaciones de .NET Core.
Hay que especificar la ruta de acceso al archivo .dll de una aplicación para poder ejecutarla. Ejecutar la
aplicación significa buscar y ejecutar el punto de entrada, que en el caso de las aplicaciones de consola es
el método Main . Por ejemplo, dotnet myapp.dll ejecuta la aplicación myapp . Vea Implementación de
aplicaciones .NET Core para obtener más información sobre las opciones de implementación.
Opciones
Existen distintas opciones disponibles para usar dotnet por sí mismo, para ejecutar un comando y para ejecutar
una aplicación.
Opciones de dotnet por sí mismo
Las siguientes opciones son para usar dotnet por sí mismo. Por ejemplo: dotnet --info . Muestran información
sobre el entorno.
--info
Imprime información detallada sobre una instalación de .NET Core y el entorno informático, por ejemplo,
el sistema operativo actual y el SHA de confirmación de la versión de .NET Core.
--version
Muestra una lista de los runtime de .NET Core instalados. Una versión x86 del SDK muestra solo los
runtime x86 y una versión x64 solo los runtime x64.
--list-sdks
Establece el nivel de detalle del comando. Los valores permitidos son q[uiet] , m[inimal] , n[ormal] ,
d[etailed] y diag[nostic] . Estos no se admiten en todos los comandos. Vea la página específica de cada
comando para asegurarse de que la opción está disponible.
-h|--help
Cada comando define opciones específicas de ese comando. Vea la página de comandos específica para
obtener una lista de las opciones disponibles.
Definición de tiempo de ejecución
Las siguientes opciones están disponibles cuando dotnet ejecuta una aplicación. Por ejemplo:
dotnet myapp.dll --roll-forward Major .
--additionalprobingpath <PATH>
Ruta de acceso a un archivo .deps.json adicional. Un archivo deps.json contiene una lista de dependencias,
dependencias de compilación e información de versión que se usa para resolver conflictos de ensamblado.
Para más información, vea Runtime Configuration Files (Archivos de configuración en tiempo de
ejecución) en GitHub.
--depsfile <PATH_TO_DEPSFILE>
Ruta de acceso al archivo deps.json. Un archivo deps.json es un archivo de configuración que contiene
información sobre las dependencias necesarias para ejecutar la aplicación. El SDK de .NET Core genera este
archivo.
--runtimeconfig
Versión del runtime de .NET Core que se va a usar para ejecutar la aplicación.
Esta opción reemplaza la versión de la primera referencia de marco en el archivo .runtimeconfig.json de
la aplicación. Esto significa que solo funciona según lo esperado si solo hay una referencia de marco. Si la
aplicación tiene más de una referencia de marco, el uso de esta opción puede hacer que se produzcan
errores.
comandos de dotnet
General
C O M A N DO F UN C IÓ N
Referencias de proyecto
C O M A N DO F UN C IÓ N
Paquetes NuGet
C O M A N DO F UN C IÓ N
Comandos NuGet
C O M A N DO F UN C IÓ N
dotnet nuget locals Borra o muestra los recursos de NuGet locales, como la caché
de solicitudes http, la caché temporal o la carpeta de
paquetes global de toda la máquina.
dotnet nuget list source Enumera todos los orígenes de NuGet configurados.
C O M A N DO F UN C IÓ N
dotnet tool list Muestra todas las herramientas globales, de ruta de acceso
de herramientas o locales instaladas actualmente en el
equipo.
dotnet tool update Actualiza una herramienta que está instalada en el equipo.
Herramientas adicionales
A partir del SDK de .NET Core 2.1.300, una serie de herramientas que estaban disponibles solo en función del
proyecto mediante DotnetCliToolReference ahora están disponibles como parte del SDK de .NET Core. Estas
herramientas se muestran en la tabla siguiente:
H ERRA M IEN TA F UN C IÓ N
Para obtener más información sobre cada herramienta, escriba dotnet <tool-name> --help .
Ejemplos
Cree una aplicación de consola de .NET Core:
dotnet build
dotnet myapp.dll
Variables de entorno
DOTNET_ROOT , DOTNET_ROOT(x86)
Especifica la ubicación de los runtime de .NET Core, si no están instalados en la ubicación predeterminada.
La ubicación predeterminada en Windows es C:\Program Files\dotnet . La ubicación predeterminada en
Linux y macOS es /usr/share/dotnet . Esta variable de entorno solo se usa cuando se ejecutan aplicaciones
a través de archivos ejecutables generados (hosts de aplicaciones). DOTNET_ROOT(x86) se usa en su lugar
cuando se ejecuta un archivo ejecutable de 32 bits en un sistema operativo de 64 bits.
DOTNET_PACKAGES
Especifica la ubicación del índice de mantenimiento que usará el host compartido al cargar el entorno de
tiempo de ejecución.
DOTNET_NOLOGO
Especifica si los mensajes de bienvenida y de telemetría de .NET Core se muestran en la primera ejecución.
Establézcala en true para silenciar estos mensajes (valores true , 1 o yes aceptados) o en false para
permitirlos (valores false , 0 o no aceptados). Si no se establece, el valor predeterminado es false y
los mensajes se mostrarán en la primera ejecución. Esta marca no tiene ningún efecto en la telemetría
(consulte DOTNET_CLI_TELEMETRY_OPTOUT para excluirse del envío de telemetría).
DOTNET_CLI_TELEMETRY_OPTOUT
Especifica si se recopilan datos sobre el uso de herramientas de .NET Core y se envían a Microsoft.
Establézcalo en true para no participar de la característica de la telemetría (se aceptan los valores true ,
1 o yes ). De lo contrario, establézcalo en false para participar de la característica de la telemetría (se
aceptan los valores false , 0 o no ). Si no se establece, el valor predeterminado es false , y la
característica de telemetría está activa.
DOTNET_MULTILEVEL_LOOKUP
Especifica si desde la ubicación global se resuelve .NET Core Runtime, el marco de trabajo compartido o el
SDK. Si no se define, establece el valor predeterminado de 1 ( true lógico). Establézcalo en 0 ( false
lógico) para no resolver desde la ubicación global y tener instalaciones de .NET Core aisladas. Para más
información sobre las búsquedas de varios niveles, vea Multi-level SharedFX Lookup (Búsqueda SharedFX
de varios niveles).
DOTNET_ROLL_FORWARD Disponible a par tir de .NET Core 3.x.
Determina el comportamiento de puesta al día. Para obtener más información, vea la opción
--roll-forward más arriba en este mismo artículo.
Establece el idioma de la interfaz de usuario de la CLI mediante un valor de configuración regional como
en-us . Los valores admitidos son los mismos que en Visual Studio. Para obtener más información, vea la
sección sobre cómo cambiar el idioma del instalador en la documentación de instalación de Visual Studio.
Se aplican las reglas del administrador de recursos de .NET, por lo que no hay que elegir una coincidencia
exacta—(también se pueden elegir descendientes en el árbol CultureInfo ). Por ejemplo, si se establece en
fr-CA , la CLI buscará y usará las traducciones de fr . Si se establece en un idioma que no se admite, la
CLI revertirá al inglés.
DOTNET_DISABLE_GUI_ERRORS
En el caso de los archivos ejecutables generados habilitados para GUI, se deshabilita el cuadro de diálogo
emergente que suele aparecer con ciertos tipos de errores. Solo escribe en stderr y se cierra en esos
casos.
DOTNET_ADDITIONAL_DEPS
Ubicación del "almacén compartido" a la que la resolución de ensamblado revierte en algunos casos.
DOTNET_STARTUP_HOOKS
Vea también
Runtime Configuration Files (Archivos de configuración en tiempo de ejecución)
Opciones de configuración en tiempo de ejecución de .NET Core
dotnet build
16/09/2020 • 10 minutes to read • Edit Online
NOMBRE
dotnet build : compila un proyecto y todas sus dependencias.
Sinopsis
dotnet build [<PROJECT>|<SOLUTION>] [-c|--configuration <CONFIGURATION>]
[-f|--framework <FRAMEWORK>] [--force] [--interactive] [--no-dependencies]
[--no-incremental] [--no-restore] [--nologo] [-o|--output <OUTPUT_DIRECTORY>]
[-r|--runtime <RUNTIME_IDENTIFIER>] [--source <SOURCE>]
[-v|--verbosity <LEVEL>] [--version-suffix <VERSION_SUFFIX>]
Descripción
El comando dotnet build crea el proyecto y sus dependencias en un conjunto de archivos binarios. Los
archivos binarios incluyen el código del proyecto en archivos de lenguaje intermedio (IL) con una extensión .dll.
Según el tipo de proyecto y la configuración, se pueden incluir otros archivos, como los siguientes:
Un archivo ejecutable que se pueda usar para ejecutar la aplicación, si el tipo de proyecto es un archivo
ejecutable que tiene como destino .NET Core 3.0 o versiones posteriores.
Archivos de símbolos usados para la depuración con una extensión .pdb.
Un archivo .deps.json, que muestra las dependencias de la aplicación o la biblioteca.
Un archivo .runtimeconfig.json, que especifica el runtime compartido y su versión de una aplicación.
Otras bibliotecas de las que depende el proyecto (a través de referencias de proyecto o referencias de
paquetes NuGet).
En el caso de los proyectos ejecutables que tienen como destino versiones anteriores a .NET Core 3.0, las
dependencias de biblioteca de NuGet normalmente no se copian en la carpeta de salida. Se resuelven desde la
carpeta de paquetes globales NuGet en tiempo de ejecución. Teniendo eso en cuenta, el producto de
dotnet build no está listo para transferirse a otra máquina para ejecutarse. Para crear una versión de la
aplicación que se pueda implementar, se debe publicar (por ejemplo, con el comando dotnet publish). Para
obtener más información, consulte el tema Implementación de aplicaciones .NET Core.
En el caso de los proyectos ejecutables que tienen como destino .NET Core 3.0 y versiones posteriores, las
dependencias de biblioteca se copian en la carpeta de salida. Esto significa que, si no hay ninguna otra lógica
específica de la publicación (como los proyectos web), se debería poder implementar la salida de la compilación.
Restauración implícita
La compilación requiere el archivo project.assets.json, que muestra las dependencias de la aplicación. El archivo
se crea cuando se ejecuta dotnet restore . Sin el archivo de recursos en su lugar, las herramientas no pueden
resolver los ensamblados de referencia, lo que se traduce en errores.
No es necesario ejecutar dotnet restore porque lo ejecutan implícitamente todos los comandos que necesitan
que se produzca una restauración, como dotnet new , dotnet build , dotnet run , dotnet test , dotnet publish
y dotnet pack . Para deshabilitar la restauración implícita, use la opción --no-restore .
El comando dotnet restore sigue siendo válido en algunos escenarios donde tiene sentido realizar una
restauración explícita, como las compilaciones de integración continua en Azure DevOps Services o en los
sistemas de compilación que necesitan controlar explícitamente cuándo se produce la restauración.
Para obtener información sobre cómo administrar fuentes de NuGet, vea la documentación de dotnet restore .
Este comando admite las opciones de dotnet restore cuando se pasan con el formato largo (por ejemplo,
--source ). No se admiten las opciones de formato corto, como -s .
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
Para generar una biblioteca, omita la propiedad <OutputType> o cambie su valor a Library . La DLL de IL para
una biblioteca no contiene puntos de entrada y no se puede ejecutar.
MSBuild
dotnet build usa MSBuild para compilar el proyecto, por lo que admite las compilaciones en paralelo e
incrementales. Para obtener más información, consulte Compilaciones incrementales.
Además de sus opciones, el comando dotnet build acepta opciones de MSBuild, como -p para establecer
propiedades o -l para definir un registrador. Para obtener más información sobre estas opciones, vea la
Referencia de la línea de comandos de MSBuild. O también puede usar el comando dotnet msbuild.
Ejecutar dotnet build es equivalente a ejecutar dotnet msbuild -restore ; sin embargo, el nivel de detalle
predeterminado de la salida es distinto.
Argumentos
PROJECT | SOLUTION
El archivo de proyecto o solución para compilar. Si no se especifica un archivo de proyecto o solución, MSBuild
busca en el directorio de trabajo actual un archivo que tiene una extensión de archivo que termina por proj o sln
y usa ese archivo.
Opciones
-c|--configuration <CONFIGURATION>
-f|--framework <FRAMEWORK>
Compila para un marco de trabajo específico. El marco se debe definir en el archivo de proyecto.
--force
Fuerza la resolución de todas las dependencias, incluso si la última restauración se realizó correctamente.
Especificar esta marca es lo mismo que eliminar el archivo project.assets.json.
-h|--help
Permite que el comando se detenga y espere una entrada o una acción del usuario. Por ejemplo, para
completar la autenticación. Disponible desde el SDK de .NET Core 3.0.
--no-dependencies
Omite las referencias de proyecto a proyecto (P2P) y solo compila el proyecto raíz especificado.
--no-incremental
Marca la compilación como no segura para la compilación incremental. Esta marca desactiva la
compilación incremental y fuerza una recompilación limpia del gráfico de dependencias del proyecto.
--no-restore
No se muestra la pancarta de inicio ni el mensaje de copyright. Disponible desde el SDK de .NET Core 3.0.
-o|--output <OUTPUT_DIRECTORY>
Directorio donde se colocan los archivos binarios compilados. Si no se especifica la ruta de acceso
predeterminada es ./bin/<configuration>/<framework>/ . En el caso de los proyectos con varias
plataformas de destino (a través de la propiedad TargetFrameworks ), también debe definir --framework al
especificar esta opción.
-r|--runtime <RUNTIME_IDENTIFIER>
Especifica el tiempo de ejecución de destino. Para obtener una lista de identificadores de tiempo de
ejecución (RID), consulte el catálogo de RID.
--source <SOURCE>
URI del origen del paquete NuGet que se usará durante la operación de restauración.
-v|--verbosity <LEVEL>
Establece el nivel de detalle de MSBuild. Los valores permitidos son q[uiet] , m[inimal] , n[ormal] ,
d[etailed] y diag[nostic] . De manera predeterminada, es minimal .
--version-suffix <VERSION_SUFFIX>
Ejemplos
Creación de un proyecto y sus dependencias:
dotnet build
Compilación de un proyecto y sus dependencias para un tiempo de ejecución concreto (en este ejemplo,
Ubuntu 18.04):
Compile el proyecto y use origen del paquete NuGet especificado durante la operación de restauración
(SDK de .NET Core 2.0 y versiones posteriores):
Name
dotnet build-server : interactúa con servidores iniciados por una compilación.
Sinopsis
dotnet build-server shutdown [--msbuild] [--razor] [--vbcscompiler]
Comandos
shutdown
Cierra los servidores de la compilación que se inician desde dotnet. De forma predeterminada, se cierran
todos los servidores.
Opciones
-h|--help
Name
dotnet clean : limpia la salida de un proyecto.
Sinopsis
dotnet clean [<PROJECT>|<SOLUTION>] [-c|--configuration <CONFIGURATION>]
[-f|--framework <FRAMEWORK>] [--interactive]
[--nologo] [-o|--output <OUTPUT_DIRECTORY>]
[-r|--runtime <RUNTIME_IDENTIFIER>] [-v|--verbosity <LEVEL>]
Description
El comando dotnet clean limpia la salida de la compilación anterior. Se implementa como un destino MSBuild,
por lo que el proyecto se evalúa cuando se ejecuta el comando. Solo se limpian las salidas que se crearon durante
la compilación. Se limpian las carpetas intermedias (obj) y de la salida final (bin).
Argumentos
PROJECT | SOLUTION
Proyecto o solución de MSBuild que se va a limpiar. Si no se especifica un archivo de proyecto o solución, MSBuild
busca en el directorio de trabajo actual un archivo que tenga una extensión de archivo que termine en proj o sln y
lo usa.
Opciones
-c|--configuration <CONFIGURATION>
Define la configuración de compilación. El valor predeterminado para la mayoría de los proyectos es Debug ,
pero puede invalidar los valores de configuración de compilación en el proyecto. Esta opción solo es
necesaria al realizar la limpieza si la especificó durante el tiempo de compilación.
-f|--framework <FRAMEWORK>
El marco que se especificó en tiempo de compilación. El marco se debe definir en el archivo de proyecto. Si
especificó el marco en tiempo de compilación, debe especificar el marco al realizar la limpieza.
-h|--help
Permite que el comando se detenga y espere una entrada o una acción del usuario. Por ejemplo, para
completar la autenticación. Disponible desde el SDK de .NET Core 3.0.
--nologo
No se muestra la pancarta de inicio ni el mensaje de copyright. Disponible desde el SDK de .NET Core 3.0.
-o|--output <OUTPUT_DIRECTORY>
Directorio que contiene los artefactos compilados que se van a limpiar. Especifique el modificador
-f|--framework <FRAMEWORK> con el modificador del directorio de salida si especificó el marco cuando se
compiló el proyecto.
-r|--runtime <RUNTIME_IDENTIFIER>
Limpia la carpeta de salida del tiempo de ejecución especificado. Esto se usa si se ha creado una
implementación autocontenida.
-v|--verbosity <LEVEL>
Establece el nivel de detalle de MSBuild. Los valores permitidos son q[uiet] , m[inimal] , n[ormal] ,
d[etailed] y diag[nostic] . El valor predeterminado es normal .
Ejemplos
Limpie una compilación predeterminada del proyecto:
dotnet clean
Name
dotnet help : muestra documentación más detallada en línea para el comando especificado.
Sinopsis
dotnet help <COMMAND_NAME> [-h|--help]
Description
El comando dotnet help abre la página de referencia para obtener información más detallada sobre el comando
especificado en docs.microsoft.com.
Argumentos
COMMAND_NAME
Nombre del comando de la CLI de .NET Core. Para obtener una lista de los comandos de la CLI válidos, vea
Comandos de la CLI.
Opciones
-h|--help
Ejemplos
Se abre la página de documentación del comando dotnet new:
NOMBRE
dotnet migrate : migra un proyecto .NET Core de la versión preliminar 2 a un proyecto del estilo de SDK de .NET
Core.
Sinopsis
dotnet migrate [<SOLUTION_FILE|PROJECT_DIR>] [--format-report-file-json <REPORT_FILE>]
[-r|--report-file <REPORT_FILE>] [-s|--skip-project-references [Debug|Release]]
[--skip-backup] [-t|--template-file <TEMPLATE_FILE>] [-v|--sdk-package-version]
[-x|--xproj-file]
Descripción
Este comando está en desuso. El comando dotnet migrate ya no está disponible a partir del SDK de .NET Core 3.0.
Solo puede migrar un proyecto de .NET Core de la versión preliminar 2 a un proyecto de .NET Core 1.x, para el que
no hay soporte técnico.
De forma predeterminada, el comando migra el proyecto raíz y todas las referencias de proyecto que contiene.
Este comportamiento se deshabilita mediante la opción --skip-project-references en tiempo de ejecución.
La migración se puede realizar en los recursos siguientes:
Un único proyecto mediante la especificación del archivo project.json que quiere migrar.
Todos los directorios especificados en el archivo global.json pasando una ruta al archivo global.json.
Un archivo solution.sln, donde se migran los proyectos a los que se hace referencia en la solución.
Todos los subdirectorios del directorio dado de manera recursiva.
El comando dotnet migrate mantiene el archivo project.json migrado dentro de un directorio backup , que se crea
en caso de que no exista. Este comportamiento se invalida con la opción --skip-backup .
De forma predeterminada, la operación de migración genera el estado del proceso de migración a la salida
estándar (STDOUT). Si usa la opción --report-file <REPORT_FILE> , la salida se guarda en el archivo especificado.
El comando dotnet migrate solo admite proyectos válidos basados en project.json de la versión preliminar 2. Esto
significa que no se puede usar para migrar DNX o los proyectos basados en project.json de la versión preliminar 1
directamente a proyectos de MSBuild/csproj. Primero debe migrar manualmente el proyecto a un proyecto
basado en project.json de la versión preliminar 2 y luego usar el comando dotnet migrate para migrar el
proyecto.
Argumentos
PROJECT_JSON/GLOBAL_JSON/SOLUTION_FILE/PROJECT_DIR
La ruta de acceso a uno de los siguientes elementos:
Un archivo project.json para migrar.
Un archivo global.json: se migran las carpetas especificadas en global.json.
Un archivo solution.sln: se migran los proyectos a los que se hace referencia en la solución.
Un directorio para migrar: se buscan de forma recursiva los archivos project.json que se van a migrar dentro
del directorio especificado.
Si no se especifica nada, se toma como valor predeterminado el directorio actual.
Opciones
--format-report-file-json <REPORT_FILE>
Salida del archivo de informe de migración como JSON en lugar de mensajes de usuario.
-h|--help
Omite la migración de referencias de proyecto. De forma predeterminada, las referencias de proyecto se migran
de forma recursiva.
--skip-backup
Omite el traslado de project.json, global.json y *.xproj a un directorio backup tras la realización correcta de la
migración.
-t|--template-file <TEMPLATE_FILE>
Archivo csproj de plantilla que se utilizará para la migración. De forma predeterminada, se usa la misma plantilla
que la descartada por dotnet new console .
-v|--sdk-package-version <VERSION>
La versión del paquete sdk a la que se hace referencia en la aplicación migrada. El valor predeterminado es la
versión del SDK en dotnet new .
-x|--xproj-file <FILE>
La ruta de acceso al archivo xproj que se usará. Necesario cuando hay más de un archivo xproj en un directorio de
proyecto.
Ejemplos
Migrar un proyecto del directorio actual y todas sus dependencias de proyecto a proyecto:
dotnet migrate
Migrar solo el proyecto actual y ninguna dependencia de proyecto a proyecto (P2P). Además, usar una versión
específica de SDK:
dotnet migrate -s -v 1.0.0-preview4
dotnet msbuild
16/09/2020 • 2 minutes to read • Edit Online
NOMBRE
dotnet msbuild : compila un proyecto y todas sus dependencias. Nota: Si hay varios, es posible que sea necesario
especificar una solución o un archivo de proyecto.
Sinopsis
dotnet msbuild <MSBUILD_ARGUMENTS>
dotnet msbuild -h
Descripción
El comando dotnet msbuild permite el acceso a una instancia de MSBuild completamente funcional.
El comando tiene exactamente las mismas funcionalidades que el cliente de línea de comandos de MSBuild
existente solo para proyectos de estilo SDK. Las opciones son las mismas. Para obtener más información sobre
las opciones disponibles, vea Referencia de la línea de comandos de MSBuild.
El comando dotnet build es equivalente al comando dotnet msbuild -restore . Si no quiere compilar el proyecto
y hay un destino concreto que quiere ejecutar, use dotnet build o dotnet msbuild y especifique el destino.
Ejemplos
Creación de un proyecto y sus dependencias:
dotnet msbuild
Visualización del proyecto completo con todos los destinos incluidos en el SDK:
NOMBRE
dotnet new : crea un nuevo proyecto, archivo de configuración o solución según la plantilla especificada.
Sinopsis
dotnet new <TEMPLATE> [--dry-run] [--force] [-i|--install {PATH|NUGET_ID}]
[-lang|--language {"C#"|"F#"|VB}] [-n|--name <OUTPUT_NAME>]
[--nuget-source <SOURCE>] [-o|--output <OUTPUT_DIRECTORY>]
[-u|--uninstall] [--update-apply] [--update-check] [Template options]
Descripción
El comando dotnet new crea un proyecto de .NET Core u otros artefactos basados en una plantilla.
El comando llama al motor de plantillas para crear los artefactos en el disco basándose en las opciones y
la plantilla especificadas.
Restauración implícita
No es necesario ejecutar dotnet restore porque lo ejecutan implícitamente todos los comandos que
necesitan que se produzca una restauración, como dotnet new , dotnet build , dotnet run , dotnet test ,
dotnet publish y dotnet pack . Para deshabilitar la restauración implícita, use la opción --no-restore .
El comando dotnet restore sigue siendo válido en algunos escenarios donde tiene sentido realizar una
restauración explícita, como las compilaciones de integración continua en Azure DevOps Services o en
los sistemas de compilación que necesitan controlar explícitamente cuándo se produce la restauración.
Para obtener información sobre cómo administrar fuentes de NuGet, vea la documentación de
dotnet restore .
Argumentos
TEMPLATE
La plantilla de la que se va a crear una instancia cuando se invoca el comando. Cada plantilla
puede tener opciones específicas que puede pasar. Para obtener más información, vea Opciones
de plantilla.
Puede ejecutar dotnet new --list o dotnet new -l para ver una lista de todas las plantillas
instaladas. Si el valor TEMPLATE no es una coincidencia exacta con el texto de la columna
Plantillas o Nombre cor to de la tabla devuelta, se realiza una coincidencia de subcadena con
esas dos columnas.
A partir del SDK de .NET Core 3.0, la CLI busca plantillas en NuGet.org al invocar el comando
dotnet new en las siguientes condiciones:
Opciones
--dry-run
Fuerza la generación de contenido incluso aunque se vayan a cambiar los archivos existentes. Es
necesario si la plantilla elegida invalidará los archivos existentes en el directorio de salida.
-h|--help
Imprime la ayuda para el comando. Puede invocarse para el propio comando dotnet new o para
cualquier plantilla. Por ejemplo: dotnet new mvc --help .
-i|--install <PATH|NUGET_ID>
Instala un paquete de plantillas desde los parámetros PATH o NUGET_ID proporcionados. Si quiere
instalar una versión preliminar de un paquete de plantilla, tendrá que especificar la versión en el
formato de <package-name>::<package-version> . De forma predeterminada, dotnet new pasa *
para la versión, que representa la última versión estable del paquete. Vea un ejemplo en la sección
Ejemplos.
Si ya hay instalada una versión de la plantilla al ejecutar este comando, la plantilla se actualizará a
la versión especificada o a la versión estable más reciente si no se ha especificado ninguna
versión.
Para obtener información sobre cómo crear plantillas personalizadas, consulte Plantillas
personalizadas para dotnet new.
-l|--list
Muestra las plantillas que contienen el nombre especificado. Si no se especifica ningún nombre,
enumera todas las plantillas.
-lang|--language {C#|F#|VB}
El lenguaje de la plantilla que se va a crear. El lenguaje aceptado cambia según la plantilla (vea los
valores predeterminados en la sección argumentos). No es válido para algunas plantillas.
NOTE
Algunos shells interpretan # como un carácter especial. En esos casos, incluya el valor del parámetro de
lenguaje entre comillas. Por ejemplo: dotnet new console -lang "F#" .
-n|--name <OUTPUT_NAME>
El nombre de la salida creada. Si no se especifica ningún nombre, se usa el nombre del directorio
actual.
--nuget-source <SOURCE>
Especifica un origen de NuGet para usarlo durante la instalación. Disponible a partir del SDK de
.NET Core 2.1.
-o|--output <OUTPUT_DIRECTORY>
Filtra plantillas en función de los tipos disponibles. Los valores predefinidos son project , item y
other .
-u|--uninstall [PATH|NUGET_ID]
NOTE
Para desinstalar una plantilla mediante PATH , debe usar el nombre completo de la ruta de acceso. Por
ejemplo, C:/Users/<USER>/Documents/Templates/GarciaSoftware.ConsoleTemplate.CSharp funcionará,
pero ./GarciaSoftware.ConsoleTemplate.CSharp desde la carpeta contenedora no lo hará. No incluya
ninguna barra diagonal para finalizar el directorio en la ruta de acceso a la plantilla.
--update-apply
Opciones de plantilla
Cada plantilla de proyecto puede tener opciones adicionales disponibles. Las plantillas principales tienen
las siguientes opciones adicionales:
consola
-f|--framework <FRAMEWORK>
Especifica el marco de destino. Disponible desde el SDK de .NET Core 3.0.
En la tabla siguiente se enumeran los valores predeterminados según el número de versión del
SDK que esté usando:
3.1 netcoreapp3.1
3.0 netcoreapp3.0
--langVersion <VERSION_NUMBER>
Establece la propiedad LangVersion en el archivo del proyecto creado. Por ejemplo, use
--langVersion 7.3 para emplear C# 7.3. No es compatible con F#. Disponible a partir del SDK de
.NET Core 2.2.
Para obtener una lista de las versiones predeterminadas de C#, vea Valores predeterminados.
--no-restore
classlib
-f|--framework <FRAMEWORK>
Especifica el marco de destino. Valores: netcoreapp<version> para crear una biblioteca de clases
de .NET Core o netstandard<version> para crear una biblioteca de clases de .NET Standard. El
valor predeterminado es netstandard2.0 .
--langVersion <VERSION_NUMBER>
Establece la propiedad LangVersion en el archivo del proyecto creado. Por ejemplo, use
--langVersion 7.3 para emplear C# 7.3. No es compatible con F#. Disponible a partir del SDK de
.NET Core 2.2.
Para obtener una lista de las versiones predeterminadas de C#, vea Valores predeterminados.
--no-restore
Establece la propiedad LangVersion en el archivo del proyecto creado. Por ejemplo, use
--langVersion 7.3 para emplear C# 7.3.
Para obtener una lista de las versiones predeterminadas de C#, vea Valores predeterminados.
--no-restore
No se ejecuta ninguna restauración implícita durante la creación del proyecto.
winforms, winformslib
--langVersion <VERSION_NUMBER>
Establece la propiedad LangVersion en el archivo del proyecto creado. Por ejemplo, use
--langVersion 7.3 para emplear C# 7.3.
Para obtener una lista de las versiones predeterminadas de C#, vea Valores predeterminados.
--no-restore
worker, grpc
-f|--framework <FRAMEWORK>
mstest, xunit
-f|--framework <FRAMEWORK>
Especifica el marco de destino. Opción disponible a partir del SDK de .NET Core 3.0.
En la tabla siguiente se enumeran los valores predeterminados según el número de versión del
SDK que esté usando:
3.1 netcoreapp3.1
3.0 netcoreapp3.0
-p|--enable-pack
nunit
-f|--framework <FRAMEWORK>
3.1 netcoreapp3.1
3.0 netcoreapp3.0
2.2 netcoreapp2.2
2.1 netcoreapp2.1
-p|--enable-pack
página
-na|--namespace <NAMESPACE_NAME>
viewimports, proto
-na|--namespace <NAMESPACE_NAME>
blazorserver
-au|--auth <AUTHENTICATION_TYPE>
Instancia de Azure Active Directory B2C con la que se realiza la conexión. Úsela con la
autenticación IndividualB2C . El valor predeterminado es https://login.microsoftonline.com/tfp/ .
-ssp|--susi-policy-id <ID>
--aad-instance <INSTANCE>
Instancia de Azure Active Directory con la que se realiza la conexión. Úsela con las autenticaciones
SingleOrg o MultiOrg . El valor predeterminado es https://login.microsoftonline.com/ .
--client-id <ID>
Identificador de cliente de este proyecto. Úsela con las autenticaciones IndividualB2C , SingleOrg
o MultiOrg . El valor predeterminado es 11111111-1111-1111-11111111111111111 .
--domain <DOMAIN>
Dominio del inquilino del directorio. Úsela con las autenticaciones SingleOrg o IndividualB2C . El
valor predeterminado es qualified.domain.name .
--tenant-id <ID>
Identificador de inquilino del directorio con el que se realiza la conexión. Úsela con la
autenticación SingleOrg . El valor predeterminado es 22222222-2222-2222-2222-222222222222 .
--callback-path <PATH>
Ruta de acceso de solicitud de la ruta de acceso de la base de la aplicación del URI de redirección.
Úsela con las autenticaciones SingleOrg o IndividualB2C . El valor predeterminado es
/signin-oidc .
-r|--org-read-access
Concede a esta aplicación acceso de lectura al directorio. Solo se aplica a las autenticaciones
SingleOrg y MultiOrg .
--exclude-launch-settings
Desactiva HTTPS. Esta opción solo se aplica si no se usan Individual , IndividualB2C , SingleOrg
o MultiOrg en --auth .
-uld|--use-local-db
Especifica que se debería usar LocalDB en vez de SQLite. Solo se aplica a las autenticaciones
Individual y IndividualB2C .
--no-restore
web
--exclude-launch-settings
3.1 netcoreapp3.1
3.0 netcoreapp3.0
2.1 netcoreapp2.1
--no-restore
Desactiva HTTPS.
mvc, webapp
-au|--auth <AUTHENTICATION_TYPE>
Instancia de Azure Active Directory B2C con la que se realiza la conexión. Úsela con la
autenticación IndividualB2C . El valor predeterminado es https://login.microsoftonline.com/tfp/ .
-ssp|--susi-policy-id <ID>
--aad-instance <INSTANCE>
Instancia de Azure Active Directory con la que se realiza la conexión. Úsela con las autenticaciones
SingleOrg o MultiOrg . El valor predeterminado es https://login.microsoftonline.com/ .
--client-id <ID>
Identificador de cliente de este proyecto. Úsela con las autenticaciones IndividualB2C , SingleOrg
o MultiOrg . El valor predeterminado es 11111111-1111-1111-11111111111111111 .
--domain <DOMAIN>
Dominio del inquilino del directorio. Úsela con las autenticaciones SingleOrg o IndividualB2C . El
valor predeterminado es qualified.domain.name .
--tenant-id <ID>
Identificador de inquilino del directorio con el que se realiza la conexión. Úsela con la
autenticación SingleOrg . El valor predeterminado es 22222222-2222-2222-2222-222222222222 .
--callback-path <PATH>
Ruta de acceso de solicitud de la ruta de acceso de la base de la aplicación del URI de redirección.
Úsela con las autenticaciones SingleOrg o IndividualB2C . El valor predeterminado es
/signin-oidc .
-r|--org-read-access
Concede a esta aplicación acceso de lectura al directorio. Solo se aplica a las autenticaciones
SingleOrg y MultiOrg .
--exclude-launch-settings
Desactiva HTTPS. Esta opción solo se aplica si no se usan Individual , IndividualB2C , SingleOrg
o MultiOrg .
-uld|--use-local-db
Especifica que se debería usar LocalDB en vez de SQLite. Solo se aplica a las autenticaciones
Individual y IndividualB2C .
-f|--framework <FRAMEWORK>
Especifica el marco de destino. Opción disponible a partir del SDK de .NET Core 3.0.
En la tabla siguiente se enumeran los valores predeterminados según el número de versión del
SDK que esté usando:
3.1 netcoreapp3.1
3.0 netcoreapp3.0
--no-restore
Incluye BrowserLink en el proyecto. Opción no disponible en el SDK de .NET Core 2.2 y 3.1.
-rrc|--razor-runtime-compilation
angular, react
-au|--auth <AUTHENTICATION_TYPE>
Tipo de autenticación que se va a usar. Disponible desde el SDK de .NET Core 3.0.
Los valores posibles son:
None : sin autenticación (valor predeterminado).
Individual : autenticación individual.
--exclude-launch-settings
Especifica que se debería usar LocalDB en vez de SQLite. Solo se aplica a las autenticaciones
Individual y IndividualB2C . Disponible desde el SDK de .NET Core 3.0.
-f|--framework <FRAMEWORK>
3.1 netcoreapp3.1
3.0 netcoreapp3.0
2.1 netcoreapp2.0
reactredux
--exclude-launch-settings
3.1 netcoreapp3.1
3.0 netcoreapp3.0
2.1 netcoreapp2.0
--no-restore
Desactiva HTTPS.
razorclasslib
--no-restore
Permite agregar vistas y páginas de Razor tradicionales además de los componentes a esta
biblioteca. Disponible desde el SDK de .NET Core 3.0.
webapi
-au|--auth <AUTHENTICATION_TYPE>
Instancia de Azure Active Directory B2C con la que se realiza la conexión. Úsela con la
autenticación IndividualB2C . El valor predeterminado es https://login.microsoftonline.com/tfp/ .
-ssp|--susi-policy-id <ID>
Instancia de Azure Active Directory con la que se realiza la conexión. Úsela con la autenticación
SingleOrg . El valor predeterminado es https://login.microsoftonline.com/ .
--client-id <ID>
Identificador de cliente de este proyecto. Úsela con las autenticaciones IndividualB2C o
SingleOrg . El valor predeterminado es 11111111-1111-1111-11111111111111111 .
--domain <DOMAIN>
Dominio del inquilino del directorio. Úsela con las autenticaciones IndividualB2C o SingleOrg . El
valor predeterminado es qualified.domain.name .
--tenant-id <ID>
Identificador de inquilino del directorio con el que se realiza la conexión. Úsela con la
autenticación SingleOrg . El valor predeterminado es 22222222-2222-2222-2222-222222222222 .
-r|--org-read-access
--exclude-launch-settings
Especifica que se debería usar LocalDB en vez de SQLite. Solo se aplica a la autenticación
IndividualB2C .
-f|--framework <FRAMEWORK>
3.1 netcoreapp3.1
3.0 netcoreapp3.0
2.1 netcoreapp2.1
--no-restore
globaljson
--sdk-version <VERSION_NUMBER>
Especifica la versión del SDK de .NET Core que se usará en el archivo global.json.
Ejemplos
Creación de un proyecto de aplicación de consola de C# mediante la especificación del nombre de
plantilla:
Enumeración de todas las plantillas disponibles en las plantillas de aplicación de página única
(SPA):
Enumeración de todas las plantillas que coinciden con la subcadena we. No se encuentra ninguna
coincidencia exacta, por lo que se ejecuta la coincidencia de subcadena con las columnas de
nombre corto y de nombre.
dotnet new we -l
Intento de invocar a la plantilla que coincide con ng. Si no se puede determinar una única
coincidencia, se enumeran las plantillas que son coincidencias parciales.
dotnet new ng
Enumeración de las plantillas instaladas y detalles sobre ellas, incluido cómo desinstalarlas:
dotnet new -u
Vea también
Plantillas personalizadas para dotnet new
Creación de una plantilla personalizada para dotnet new
Repositorio de GitHub dotnet/dotnet-template-samples
Available templates for dotnet new (Plantillas disponibles para dotnet new)
dotnet nuget delete
20/04/2020 • 2 minutes to read • Edit Online
Name
dotnet nuget delete : elimina o quita de la lista un paquete del servidor.
Sinopsis
dotnet nuget delete [<PACKAGE_NAME> <PACKAGE_VERSION>] [--force-english-output]
[--interactive] [-k|--api-key <API_KEY>] [--no-service-endpoint]
[--non-interactive] [-s|--source <SOURCE>]
Description
El comando dotnet nuget delete elimina o quita de la lista un paquete del servidor. Para nuget.org, la acción es
quitar de la lista el paquete.
Argumentos
PACKAGE_NAME
Opciones
--force-english-output
Permite que el comando se bloquee y requiere una acción manual para operaciones tales como la
autenticación. Opción disponible a partir del SDK de .NET Core 2.2.
-k|--api-key <API_KEY>
No agrega "api/v2/paquete" a la dirección URL de origen. Opción disponible a partir del SDK de .NET Core
2.1.
--non-interactive
Especifica la dirección URL del servidor. Las direcciones URL admitidas para nuget.org incluyen
https://www.nuget.org , https://www.nuget.org/api/v3 y https://www.nuget.org/api/v2/package . Para fuentes
privadas, reemplace el nombre de host (por ejemplo, %hostname%/api/v3 ).
Ejemplos
Elimina la versión 1.0 del paquete Microsoft.AspNetCore.Mvc :
Elimina la versión 1.0 del paquete Microsoft.AspNetCore.Mvc , sin pedir al usuario credenciales u otra
información:
Name
dotnet nuget locals : borra o enumera recursos locales de NuGet.
Sinopsis
dotnet nuget locals <CACHE_LOCATION> [(-c|--clear)|(-l|--list)] [--force-english-output]
Description
El comando dotnet nuget locals borra o enumera los recursos locales de NuGet en la caché de solicitudes http, la
caché temporal o la carpeta de paquetes globales de toda la máquina.
Argumentos
CACHE_LOCATION
La ubicación de caché que se va a mostrar o borrar. Acepta uno de los valores siguientes:
all : indica que la operación especificada se debe aplicar a todos los tipos de caché: caché de solicitudes
http, caché de paquetes globales y caché temporal.
http-cache : indica que la operación especificada se aplica solo a la caché de solicitudes http. Las otras
ubicaciones de caché no se ven afectadas.
global-packages : indica que la operación especificada se aplica solo a la caché de paquetes globales. Las
otras ubicaciones de caché no se ven afectadas.
temp : indica que la operación especificada se aplica solo a la caché temporal. Las otras ubicaciones de
caché no se ven afectadas.
Opciones
--force-english-output
La opción de borrado ejecuta una operación de borrado sobre el tipo de caché especificado. El contenido de
los directorios de caché se elimina de forma recursiva. El usuario o grupo de ejecución deben tener permiso
para los archivos en los directorios de la caché. En caso contrario, se muestra un error para indicar los
archivos o las carpetas que no se han borrado.
-l|--list
La opción de lista se usa para mostrar la ubicación del tipo de caché especificado.
Ejemplos
Muestra las rutas de acceso de todos los directorios de caché locales (el directorio de caché http, el
directorio de caché de paquetes globales y el directorio de caché temporal):
Borra todos los archivos de todos los directorios de caché locales (directorio de caché http, directorio de
caché de paquetes globales y directorio de caché temporal):
Borra todos los archivos del directorio local de la caché de paquetes globales:
Solución de problemas
Para más información sobre problemas y errores comunes encontrados al usar el comando dotnet nuget locals ,
consulte Managing the NuGet cache (Administración de la caché de NuGet).
dotnet nuget push
16/09/2020 • 5 minutes to read • Edit Online
NOMBRE
dotnet nuget push : inserta un paquete en el servidor y lo publica.
Sinopsis
dotnet nuget push [<ROOT>] [-d|--disable-buffering] [--force-english-output]
[--interactive] [-k|--api-key <API_KEY>] [-n|--no-symbols true]
[--no-service-endpoint] [-s|--source <SOURCE>] [--skip-duplicate]
[-sk|--symbol-api-key <API_KEY>] [-ss|--symbol-source <SOURCE>]
[-t|--timeout <TIMEOUT>]
Descripción
El comando dotnet nuget push inserta un paquete en el servidor y lo publica. El comando push usa los detalles del
servidor y de las credenciales encontrados en el archivo de configuración NuGet del sistema o en la cadena de
archivos de configuración. Para más información sobre los archivos de configuración, consulte Configuring NuGet
Behavior (Configuración del comportamiento de NuGet). La configuración predeterminada de NuGet se obtiene
mediante la carga de %AppData%\NuGet\NuGet.config (Windows) o $HOME/.local/share (Linux/macOS), y luego
la carga de cualquier archivo nuget.config o .nuget\nuget.config comenzando desde la raíz de la unidad y
finalizando en el directorio actual.
El comando inserta un paquete existente. No crea un paquete. Para crear un paquete, use dotnet pack .
Argumentos
ROOT
Opciones
-d|--disable-buffering
Deshabilita el almacenamiento en búfer al realizar inserciones en un servidor HTTP(S) para reducir el uso de
memoria.
--force-english-output
No agrega "api/v2/paquete" a la dirección URL de origen. Opción disponible desde el SDK de .NET Core 2.1.
-s|--source <SOURCE>
Especifica la dirección URL del servidor. Esta opción es necesaria a menos que el valor de configuración
DefaultPushSource esté establecido en el archivo de configuración de NuGet.
--skip-duplicate
Al insertar varios paquetes en un servidor HTTP(S), trata cualquier respuesta de conflicto 409 como una
advertencia para que la inserción pueda continuar. Disponible a partir del SDK de .NET Core 3.1.
-sk|--symbol-api-key <API_KEY>
Especifica el tiempo de espera para la inserción en un servidor en segundos. El valor predeterminado es 300
segundos (5 minutos). Si se especifica 0 (cero segundos), se aplica el valor predeterminado.
Ejemplos
Inserte foo.nupkg en el origen de inserción predeterminado y especifique una clave de API:
Inserte todos los archivos .nupkg del directorio actual en el origen de inserción predeterminado:
NOTE
Si este comando no funciona, es posible que se deba a un error presente en versiones anteriores del SDK (SDK de
.NET Core 2.1 y versiones anteriores). Para solucionar este problema, actualice la versión de su SDK o ejecute el
siguiente comando en su lugar: dotnet nuget push "**/*.nupkg"
NOTE
Las comillas de inclusión son necesarias para los shells como bash que usan comodines de archivo. Para obtener más
información, vea NuGet/Home#4393.
Inserte todos los archivos .nupkg, aunque un servidor HTTP(S) devuelva una respuesta de conflicto 409:
Inserte todos los archivos .nupkg del directorio actual en un directorio de fuente local:
Este comando no almacena paquetes en una estructura de carpetas jerárquica, lo que se recomienda para
optimizar el rendimiento. Para obtener más información, vea Fuentes locales.
dotnet nuget add source
20/04/2020 • 2 minutes to read • Edit Online
NOMBRE
dotnet nuget add source : agrega un origen de NuGet.
Sinopsis
dotnet nuget add source <PACKAGE_SOURCE_PATH> [--name <SOURCE_NAME>] [--username <USER>]
[--password <PASSWORD>] [--store-password-in-clear-text]
[--valid-authentication-types <TYPES>] [--configfile <FILE>]
Descripción
El comando dotnet nuget add source agrega un nuevo origen de paquete a los archivos de configuración de
NuGet.
Argumentos
PACKAGE_SOURCE_PATH
Opciones
--configfile <FILE>
Deshabilita el cifrado de la contraseña para permitir el almacenamiento de las credenciales de origen del
paquete portátil.
-u|--username <USER>
Lista separada por comas de tipos de autenticación válidos para este origen. Establézcalo en basic si el
servidor anuncia NTLM o Negotiate y las credenciales deben enviarse mediante el mecanismo básico, por
ejemplo, cuando se usa una instancia de PAT con Azure DevOps Server local. Otros valores válidos son
negotiate , kerberos , ntlm y digest , pero es poco probable que estos valores sean útiles.
Ejemplos
Agregue nuget.org como origen:
Agregue un origen que necesite autenticación (luego, pase a la instalación del proveedor de credenciales):
Vea también
Secciones de origen del paquete en archivos NuGet.config
Comando sources (nuget.exe)
dotnet nuget disable source
20/04/2020 • 2 minutes to read • Edit Online
NOMBRE
dotnet nuget disable source : deshabilita un origen de NuGet.
Sinopsis
dotnet nuget disable source <NAME> [--configfile <FILE>]
Descripción
El comando dotnet nuget disable source deshabilita un origen existente en los archivos de configuración de
NuGet.
Argumentos
NAME
Opciones
--configfile <FILE>
Ejemplos
Deshabilite un origen con el nombre de mySource :
Vea también
Secciones de origen del paquete en archivos NuGet.config
Comando sources (nuget.exe)
dotnet nuget enable source
20/04/2020 • 2 minutes to read • Edit Online
NOMBRE
dotnet nuget enable source : habilita un origen de NuGet.
Sinopsis
dotnet nuget enable source <NAME> [--configfile <FILE>]
Descripción
El comando dotnet nuget enable source habilita un origen existente en los archivos de configuración de NuGet.
Argumentos
NAME
Opciones
--configfile <FILE>
Ejemplos
Habilite un origen con el nombre de mySource :
Vea también
Secciones de origen del paquete en archivos NuGet.config
Comando sources (nuget.exe)
dotnet nuget list source
20/04/2020 • 2 minutes to read • Edit Online
NOMBRE
dotnet nuget list source : enumera todos los orígenes de NuGet configurados.
Sinopsis
dotnet nuget list source [--format [Detailed|Short]] [--configfile <FILE>]
Descripción
El comando dotnet nuget list source muestra todos los orígenes existentes de los archivos de configuración de
NuGet.
Opciones
--configfile <FILE>
Ejemplos
Enumeración de los orígenes configurados del directorio actual:
Vea también
Secciones de origen del paquete en archivos NuGet.config
Comando sources (nuget.exe)
dotnet nuget remove source
20/04/2020 • 2 minutes to read • Edit Online
NOMBRE
dotnet nuget remove source : quita un origen de NuGet.
Sinopsis
dotnet nuget remove source <NAME> [--configfile <FILE>]
Descripción
El comando dotnet nuget remove source quita un origen existente de los archivos de configuración de NuGet.
Argumentos
NAME
Opciones
--configfile
Ejemplos
Quite un origen con el nombre de mySource :
Vea también
Secciones de origen del paquete en archivos NuGet.config
Comando sources (nuget.exe)
dotnet nuget update source
20/04/2020 • 2 minutes to read • Edit Online
NOMBRE
dotnet nuget update source : actualiza un origen de NuGet.
Sinopsis
dotnet nuget update source <NAME> [--source <SOURCE>] [--username <USER>]
[--password <PASSWORD>] [--store-password-in-clear-text]
[--valid-authentication-types <TYPES>] [--configfile <FILE>]
Descripción
El comando dotnet nuget update source actualiza un origen existente en los archivos de configuración de NuGet.
Argumentos
NAME
Opciones
--configfile <FILE>
Deshabilita el cifrado de la contraseña para permitir el almacenamiento de las credenciales de origen del
paquete portátil.
-u|--username <USER>
Ejemplos
Actualice un origen con el nombre de mySource :
Vea también
Secciones de origen del paquete en archivos NuGet.config
Comando sources (nuget.exe)
dotnet pack
16/09/2020 • 9 minutes to read • Edit Online
NOMBRE
dotnet pack : empaqueta el código en un paquete de NuGet.
Sinopsis
dotnet pack [<PROJECT>|<SOLUTION>] [-c|--configuration <CONFIGURATION>]
[--force] [--include-source] [--include-symbols] [--interactive]
[--no-build] [--no-dependencies] [--no-restore] [--nologo]
[-o|--output <OUTPUT_DIRECTORY>] [--runtime <RUNTIME_IDENTIFIER>]
[-s|--serviceable] [-v|--verbosity <LEVEL>]
[--version-suffix <VERSION_SUFFIX>]
Descripción
El comando dotnet pack compila el proyecto y crea paquetes de NuGet. El resultado de este comando es un
paquete de NuGet (es decir, un archivo .nupkg).
Si quiere generar un paquete que contenga los símbolos de depuración, tiene dos opciones a su disposición:
--include-symbols : crea el paquete de símbolos.
--include-source : crea el paquete de símbolos con una carpeta src dentro que contiene los archivos de
origen.
Las dependencias de NuGet del proyecto empaquetado se agregan al archivo .nuspec, por lo que se pueden
resolver adecuadamente cuando se instala el paquete. Las referencias de proyecto a proyecto no se empaquetan
dentro del proyecto. Actualmente, debe disponer de un paquete por proyecto si tiene dependencias de proyecto
a proyecto.
De forma predeterminada, dotnet pack compila primero el proyecto. Si desea evitar este comportamiento, pase
la opción --no-build . Esta opción a menudo resulta útil en escenarios de compilación de integración continua
(CI) donde se conoce el código que se compiló anteriormente.
NOTE
En algunos casos, no se puede realizar la compilación implícita. Esto puede ocurrir cuando se establece
GeneratePackageOnBuild , para evitar una dependencia cíclica entre los destinos de compilación y de paquete. La
compilación también puede producir un error si hay un archivo bloqueado u otro problema.
Puede proporcionar propiedades de MSBuild en el comando dotnet pack para el proceso de empaquetado.
Para obtener más información, vea Propiedades de metadatos de NuGet y la Referencia de la línea de comandos
de MSBuild. La sección Ejemplos muestra cómo utilizar el modificador -p de MSBuild en un par de escenarios
diferentes.
Los proyectos web no están empaquetados de forma predeterminada. Para invalidar el comportamiento
predeterminado, agregue la siguiente propiedad a su archivo .csproj:
<PropertyGroup>
<IsPackable>true</IsPackable>
</PropertyGroup>
Restauración implícita
No es necesario ejecutar dotnet restore porque lo ejecutan implícitamente todos los comandos que necesitan
que se produzca una restauración, como dotnet new , dotnet build , dotnet run , dotnet test , dotnet publish
y dotnet pack . Para deshabilitar la restauración implícita, use la opción --no-restore .
El comando dotnet restore sigue siendo válido en algunos escenarios donde tiene sentido realizar una
restauración explícita, como las compilaciones de integración continua en Azure DevOps Services o en los
sistemas de compilación que necesitan controlar explícitamente cuándo se produce la restauración.
Para obtener información sobre cómo administrar fuentes de NuGet, vea la documentación de dotnet restore .
Este comando admite las opciones de dotnet restore cuando se pasan con el formato largo (por ejemplo,
--source ). No se admiten las opciones de formato corto, como -s .
Argumentos
PROJECT | SOLUTION
El archivo de proyecto o solución para empaquetar. Es una ruta de acceso a un archivo csproj, un archivo vbproj,
un archivo fsproj, un archivo de solución o un directorio. Si no se especifica, el comando busca un archivo del
proyecto o de la solución en el directorio actual.
Opciones
-c|--configuration <CONFIGURATION>
--force
Fuerza la resolución de todas las dependencias, incluso si la última restauración se realizó correctamente.
Especificar esta marca es lo mismo que eliminar el archivo project.assets.json.
-h|--help
Incluye los paquetes NuGet de símbolos de depuración, además de los paquetes NuGet normales en el
directorio de salida. Los archivos de origen se incluyen en la carpeta src dentro del paquete de
símbolos.
--include-symbols
Incluye los paquetes NuGet de símbolos de depuración, además de los paquetes NuGet normales en el
directorio de salida.
--interactive
Permite que el comando se detenga y espere la entrada o acción del usuario (por ejemplo, completar la
autenticación). Disponible desde el SDK de .NET Core 3.0.
--no-build
No se muestra la pancarta de inicio ni el mensaje de copyright. Disponible desde el SDK de .NET Core 3.0.
-o|--output <OUTPUT_DIRECTORY>
Especifica el tiempo de ejecución de destino para el que restaurar los paquetes. Para obtener una lista de
identificadores de tiempo de ejecución (RID), consulte el catálogo de RID.
-s|--serviceable
Establece la marca de servicio en el paquete. Para más información, consulte .NET Blog: .NET 4.5.1
Supports Microsoft Security Updates for .NET NuGet Libraries (Blog de .NET: .NET 4.5.1 admite
actualizaciones de seguridad de Microsoft para bibliotecas NuGet de .NET).
--version-suffix <VERSION_SUFFIX>
Establece el nivel de detalle del comando. Los valores permitidos son q[uiet] , m[inimal] , n[ormal] ,
d[etailed] y diag[nostic] .
Ejemplos
Empaquetado del proyecto en el directorio actual:
dotnet pack
Empaquetar el proyecto en el directorio actual y colocar los paquetes resultantes en la carpeta nupkgs :
Empaquete el proyecto y use un entorno de ejecución específico (Windows 10) para la operación de
restauración:
Para obtener información sobre cómo usar NuspecFile , NuspecBasePath y NuspecProperties , vea los
siguientes recursos:
Empaquetado mediante un archivo .nuspec
Puntos de extensión avanzados para crear un paquete personalizado
Propiedades globales
dotnet publish
16/09/2020 • 18 minutes to read • Edit Online
NOMBRE
dotnet publish : publica la aplicación y sus dependencias en una carpeta para la implementación en un
sistema de hospedaje.
Sinopsis
dotnet publish [<PROJECT>|<SOLUTION>] [-c|--configuration <CONFIGURATION>]
[-f|--framework <FRAMEWORK>] [--force] [--interactive]
[--manifest <PATH_TO_MANIFEST_FILE>] [--no-build] [--no-dependencies]
[--no-restore] [--nologo] [-o|--output <OUTPUT_DIRECTORY>]
[-p:PublishReadyToRun=true] [-p:PublishSingleFile=true] [-p:PublishTrimmed=true]
[-r|--runtime <RUNTIME_IDENTIFIER>] [--self-contained [true|false]]
[--no-self-contained] [-v|--verbosity <LEVEL>]
[--version-suffix <VERSION_SUFFIX>]
Descripción
dotnet publish : compila la aplicación, lee sus dependencias especificadas en el archivo de proyecto y
publica el conjunto resultante de archivos en un directorio. La salida incluye los recursos siguientes:
Código de lenguaje intermedio (IL) en un ensamblado con una extensión dll.
Un archivo .deps.json que incluye todas las dependencias del proyecto.
Un archivo .runtime.config.json en el que se especifica el tiempo de ejecución compartido que espera la
aplicación, así como otras opciones de configuración para el tiempo de ejecución (por ejemplo, el tipo de
recolección de elementos no utilizados).
Las dependencias de la aplicación, que se copian de la caché de NuGet a la carpeta de salida.
La salida del comando dotnet publish está lista para la implementación en un sistema de hospedaje (por
ejemplo, un servidor, un equipo PC o Mac, un portátil) para la ejecución. Es la única manera admitida
oficialmente para preparar la aplicación para la implementación. Dependiendo del tipo de implementación
que especifique el proyecto, el sistema de hospedaje puede o no tener instalado el entorno de tiempo de
ejecución compartido de .NET Core. Para obtener más información, vea Publicación de aplicaciones .NET
Core con la CLI de .NET Core.
Restauración implícita
No es necesario ejecutar dotnet restore porque lo ejecutan implícitamente todos los comandos que
necesitan que se produzca una restauración, como dotnet new , dotnet build , dotnet run , dotnet test ,
dotnet publish y dotnet pack . Para deshabilitar la restauración implícita, use la opción --no-restore .
El comando dotnet restore sigue siendo válido en algunos escenarios donde tiene sentido realizar una
restauración explícita, como las compilaciones de integración continua en Azure DevOps Services o en los
sistemas de compilación que necesitan controlar explícitamente cuándo se produce la restauración.
Para obtener información sobre cómo administrar fuentes de NuGet, vea la documentación de
dotnet restore .
MSBuild
Con el comando dotnet publish se llama a MSBuild, lo que invoca el destino Publish . Todos los
parámetros pasados a dotnet publish se pasan a MSBuild. Los parámetros -c y -o se asignan
respectivamente a las propiedades Configuration y PublishDir de MSBuild.
El comando dotnet publish acepta opciones de MSBuild, como -p para establecer propiedades y -l para
definir un registrador. Por ejemplo, se puede establecer una propiedad de MSBuild mediante el uso del
formato: -p:<NAME>=<VALUE> .
También se pueden establecer las propiedades relacionadas con la publicación si se hace referencia a un
archivo .pubxml (disponible a partir del SDK de .NET Core 3.1). Por ejemplo:
Argumentos
PROJECT|SOLUTION
Opciones
-c|--configuration <CONFIGURATION>
-f|--framework <FRAMEWORK>
Publica la aplicación para el marco de trabajo de destino especificado. Debe especificar el marco de
trabajo de destino en el archivo de proyecto.
--force
Permite que el comando se detenga y espere una entrada o una acción del usuario. Por ejemplo, para
completar la autenticación. Disponible desde el SDK de .NET Core 3.0.
--manifest <PATH_TO_MANIFEST_FILE>
Especifica uno o varios manifiestos de destino que se usarán para recortar el conjunto de paquetes
publicados con la aplicación. El archivo de manifiesto es parte de la salida del comando dotnet store
. Para especificar varios manifiestos, agregue la opción --manifest para cada manifiesto.
--no-build
<DefaultItemExcludes>$(DefaultItemExcludes);publishoutput**</DefaultItemExcludes>
SDK de .NET Core 3.x y versiones posteriores
Si se especifica una ruta de acceso relativa al publicar un proyecto, el directorio de salida
generado es relativo al directorio de trabajo actual, no a la ubicación del archivo del proyecto.
Si se especifica una ruta de acceso relativa al publicar una solución, todas las salidas de todos
los proyectos van en la carpeta especificada relativa al directorio de trabajo actual. A fin de que
la salida de la publicación vaya a carpetas independientes para cada proyecto, especifique una
ruta de acceso relativa mediante el uso de la propiedad PublishDir de MSBuild en lugar de la
opción --output . Por ejemplo, dotnet publish -p:PublishDir=.\publish envía la salida de
publicación de cada proyecto a una carpeta publish en la carpeta que contiene el archivo del
proyecto.
SDK de .NET Core 2.x
Si se especifica una ruta de acceso relativa al publicar un proyecto, el directorio de salida
generado es relativo a la ubicación del archivo del proyecto, no al directorio de trabajo actual.
Si se especifica una ruta de acceso relativa al publicar una solución, la salida de cada proyecto
va a una carpeta independiente relativa a la ubicación del archivo del proyecto. Si se especifica
una ruta de acceso absoluta al publicar una solución, la salida de las publicaciones de todos los
proyectos van a la carpeta especificada.
-p:PublishReadyToRun=true
Compila los ensamblados de aplicación con el formato ReadyToRun (R2R). R2R es una forma de
compilación Ahead Of Time (AOT). Para obtener más información, vea Imágenes ReadyToRun.
Disponible desde el SDK de .NET Core 3.0.
Se recomienda especificar esta opción en un perfil de publicación en lugar de hacerlo en la línea de
comandos. Para obtener más información, vea MSBuild.
-p:PublishSingleFile=true
Recorta las bibliotecas no utilizadas para reducir el tamaño de implementación de una aplicación
cuando se publica un ejecutable independiente. Para obtener más información, vea Recorte de
implementaciones autocontenidas y ejecutables. Disponible a partir del SDK de .NET Core 3.0 como
una característica en versión preliminar.
Se recomienda especificar esta opción en un perfil de publicación en lugar de hacerlo en la línea de
comandos. Para obtener más información, vea MSBuild.
--self-contained [true|false]
Publica el tiempo de ejecución de .NET Core con la aplicación para que no sea necesario tener
instalado el tiempo de ejecución en la máquina de destino. El valor predeterminado es true si se
especifica un identificador en tiempo de ejecución y el proyecto es de tipo ejecutable (no un proyecto
de biblioteca). Para obtener más información, vea Publicación de aplicaciones .NET Core y Publicación
de aplicaciones .NET Core con la CLI de .NET Core.
Si se usa esta opción sin especificar true o false , el valor predeterminado es true . En ese caso, no
coloque el argumento de la solución o el proyecto inmediatamente después de --self-contained ,
porque se espera que true o false estén en esa posición.
--no-self-contained
Publica la aplicación para un determinado entorno de tiempo de ejecución. Para obtener una lista de
identificadores de tiempo de ejecución (RID), consulte el catálogo de RID. Para obtener más
información, vea Publicación de aplicaciones .NET Core y Publicación de aplicaciones .NET Core con la
CLI de .NET Core.
-v|--verbosity <LEVEL>
Establece el nivel de detalle del comando. Los valores permitidos son q[uiet] , m[inimal] , n[ormal] ,
d[etailed] y diag[nostic] . El valor predeterminado es minimal .
--version-suffix <VERSION_SUFFIX>
Define el sufijo de versión para reemplazar el asterisco ( * ) en el campo de versión del archivo de
proyecto.
Ejemplos
Cree un archivo binario multiplataforma dependiente del marco para el proyecto en el directorio
actual:
dotnet publish
A partir del SDK de .NET Core 3.0, en este ejemplo también se crea un ejecutable dependiente del
marco para la plataforma actual.
Cree un ejecutable independiente para el proyecto en el directorio actual, para un tiempo de
ejecución específico:
El RID debe estar en el archivo del proyecto. Este ejemplo se aplica al SDK de .NET Core 3.0 y
versiones posteriores.
Publique el proyecto en el directorio actual, para un tiempo de ejecución específico y una plataforma
de destino:
Publique la aplicación actual pero sin restaurar las referencias de proyecto a proyecto (P2P), solo el
proyecto raíz, durante la operación de restauración:
Vea también
Información general sobre la publicación de aplicaciones de .NET Core
Publicación de aplicaciones .NET Core con la CLI de .NET Core
Marcos de trabajo de destino
Catálogo de identificadores de tiempo de ejecución (RID)
Trabajo con la certificación de macOS Catalina
Estructura de directorios de una aplicación publicada
Referencia de la línea de comandos de MSBuild
Perfiles de publicación (.pubxml) de Visual Studio para la implementación de aplicaciones ASP.NET Core
dotnet msbuild
ILLInk.Tasks
dotnet restore
16/09/2020 • 9 minutes to read • Edit Online
NOMBRE
dotnet restore : restaura las dependencias y las herramientas de un proyecto.
Sinopsis
dotnet restore [<ROOT>] [--configfile <FILE>] [--disable-parallel]
[-f|--force] [--force-evaluate] [--ignore-failed-sources]
[--interactive] [--lock-file-path <LOCK_FILE_PATH>] [--locked-mode]
[--no-cache] [--no-dependencies] [--packages <PACKAGES_DIRECTORY>]
[-r|--runtime <RUNTIME_IDENTIFIER>] [-s|--source <SOURCE>]
[--use-lock-file] [-v|--verbosity <LEVEL>]
Descripción
El comando dotnet restore usa NuGet para restaurar las dependencias, así como las herramientas
específicas del proyecto que se especifican en el archivo project.json. En la mayoría de los casos, no es
necesario usar explícitamente el comando dotnet restore , ya que una restauración de NuGet se
ejecuta implícitamente si es necesario al ejecutar los siguientes comandos:
dotnet new
dotnet build
dotnet build-server
dotnet run
dotnet test
dotnet publish
dotnet pack
A veces, puede que no sea conveniente ejecutar la restauración de NuGet implícita con estos
comandos. Por ejemplo, algunos sistemas automatizados, como los sistemas de compilación, deben
llamar a dotnet restore explícitamente para controlar cuándo se produce la restauración a fin de
controlar el uso de la red. Para evitar la restauración de NuGet implícita, puede usar la marca
--no-restore con cualquiera de estos comandos para deshabilitar la restauración implícita.
Especificación de fuentes
Para restaurar las dependencias, NuGet necesita las fuentes donde se encuentran los paquetes. Las
fuente se proporcionan normalmente mediante el archivo de configuración nuget.config. Cuando se
instala el SDK de .NET Core, se proporciona un archivo de configuración predeterminado. Para
especificar fuentes adicionales, realice una de las acciones siguientes:
Cree su propio archivo nuget.config en el directorio del proyecto. Para obtener más información,
vea Configuraciones comunes de NuGet y Diferencias de nuget.config más adelante en este
artículo.
Use comandos de dotnet nuget como dotnet nuget add source .
Puede invalidar las fuentes nuget.config con la opción -s .
Para obtener información sobre cómo usar las fuentes autenticadas, vea Consumir paquetes desde
fuentes autenticadas.
Carpeta de paquetes globales
Para las dependencias, puede especificar dónde se colocan los paquetes restaurados durante la
operación de restauración mediante el argumento --packages . Si no se especifica, se usa la caché de
paquetes NuGet predeterminada, que se encuentra en el directorio .nuget/packages del directorio de
inicio del usuario en todos los sistemas operativos. Por ejemplo, /home/usuario1 en Linux o
C:\Usuarios\usuario1 en Windows.
Herramientas específicas del proyecto
Para herramientas específicas del proyecto, dotnet restore restaura primero el paquete en el que se
empaqueta la herramienta y, a continuación, continúa con la restauración de las dependencias de la
herramienta especificadas en su project.json.
Diferencias de nuget.config
El comportamiento del comando dotnet restore depende de las opciones de configuración del
archivo nuget.config, si existe. Por ejemplo, establecer globalPackagesFolder en nuget.config coloca los
paquetes NuGet restaurados en la carpeta especificada. Esta es una alternativa para especificar la
opción --packages en el comando dotnet restore . Para más información, consulte la referencia de
nuget.config.
Hay tres configuraciones específicas que dotnet restore omite:
bindingRedirects
Los redireccionamientos de enlace no funcionan con elementos de <PackageReference> y
.NET Core solo admite elementos de <PackageReference> para los paquetes NuGet.
solution
Esta configuración es específica para Visual Studio y no se aplica a .NET Core. .NET Core no usa
un archivo packages.config y, en su lugar, usa elementos de <PackageReference> para los
paquetes NuGet.
trustedSigners
Esta configuración no es aplicable porque NuGet ya no admite la comprobación
multiplataforma de los paquetes de confianza.
Argumentos
ROOT
Opciones
--configfile <FILE>
Fuerza la restauración para volver a evaluar todas las dependencias aunque ya exista un archivo
de bloqueo.
-h|--help
Solo se advierte sobre los orígenes con error si hay paquetes que satisfagan el requisito de
versión.
--interactive
Permite que el comando se detenga y espere la entrada o acción del usuario (por ejemplo,
completar la autenticación). Desde .NET Core 2.1.400.
--lock-file-path <LOCK_FILE_PATH>
Especifica un tiempo de ejecución para la restauración del paquete. Se usa para restaurar los
paquetes con tiempos de ejecución que no se enumeran explícitamente en la etiqueta
<RuntimeIdentifiers> del archivo .csproj. Para obtener una lista de identificadores de tiempo de
ejecución (RID), consulte el catálogo de RID. Para proporcionar varios RID; especifique esta
opción varias veces.
-s|--source <SOURCE>
Especifica el URI del origen del paquete NuGet que se usará durante la operación de
restauración. Este valor invalida todos los orígenes especificados en los archivos nuget.config.
Al especificar esta opción varias veces, se pueden proporcionar varios orígenes.
--use-lock-file
Habilita la generación del archivo de bloqueo del proyecto y su uso con la restauración.
-v|--verbosity <LEVEL>
Establece el nivel de detalle del comando. Los valores permitidos son q[uiet] , m[inimal] ,
n[ormal] , d[etailed] y diag[nostic] . El valor predeterminado es minimal .
Ejemplos
Restauración de dependencias y herramientas para el proyecto en el directorio actual:
dotnet restore
NOMBRE
dotnet run : ejecuta el código fuente sin comandos explícitos de compilación o inicio.
Sinopsis
dotnet run [-c|--configuration <CONFIGURATION>] [-f|--framework <FRAMEWORK>]
[--force] [--interactive] [--launch-profile <NAME>] [--no-build]
[--no-dependencies] [--no-launch-profile] [--no-restore]
[-p|--project <PATH>] [-r|--runtime <RUNTIME_IDENTIFIER>]
[-v|--verbosity <LEVEL>] [[--] [application arguments]]
Descripción
El comando dotnet run proporciona una opción conveniente para ejecutar la aplicación desde el código fuente
con un comando. Es útil para un desarrollo iterativo rápido desde la línea de comandos. El comando depende del
comando dotnet build para compilar el código. Los requisitos para la compilación, como que el cliente se deba
restaurar primero, también se aplican a dotnet run .
Los archivos de salida se escriben en la ubicación predeterminada, que es bin/<configuration>/<target> . Por
ejemplo, si tiene una aplicación netcoreapp2.1 y ejecuta dotnet run , la salida se colocará en
bin/Debug/netcoreapp2.1 . Los archivos se sobrescriben según sea necesario. Los archivos temporales se colocan
en el directorio obj .
Si el proyecto especifica varios marcos, al ejecutar dotnet run se produce un error a menos que se use la opción
-f|--framework <FRAMEWORK> para especificar el marco.
El comando dotnet run debe usarse en el contexto de proyectos, no de ensamblados compilados. Si, por el
contrario, está intentando ejecutar una DLL de aplicación dependiente del marco de trabajo, debe usar dotnet sin
un comando. Por ejemplo, para ejecutar myapp.dll , use:
dotnet myapp.dll
Para más información sobre el controlador dotnet , consulte el tema Herramientas de la interfaz de la línea de
comandos (CLI) de .NET Core .
Para ejecutar la aplicación, el comando dotnet run resuelve las dependencias de la aplicación que se encuentran
fuera del entorno de tiempo de ejecución compartido desde la caché de NuGet. Dado que se usan dependencias
almacenadas en caché, no se recomienda utilizar dotnet run para ejecutar aplicaciones en producción. En su
lugar, cree una implementación mediante el comando dotnet publish e implemente la salida publicada.
Restauración implícita
No es necesario ejecutar dotnet restore porque lo ejecutan implícitamente todos los comandos que necesitan
que se produzca una restauración, como dotnet new , dotnet build , dotnet run , dotnet test , dotnet publish
y dotnet pack . Para deshabilitar la restauración implícita, use la opción --no-restore .
El comando dotnet restore sigue siendo válido en algunos escenarios donde tiene sentido realizar una
restauración explícita, como las compilaciones de integración continua en Azure DevOps Services o en los
sistemas de compilación que necesitan controlar explícitamente cuándo se produce la restauración.
Para obtener información sobre cómo administrar fuentes de NuGet, vea la documentación de dotnet restore .
Este comando admite las opciones de dotnet restore cuando se pasan con el formato largo (por ejemplo,
--source ). No se admiten las opciones de formato corto, como -s .
Opciones
--
Delimita los argumentos a dotnet run a partir de argumentos de la aplicación que se va a ejecutar. Todos
los argumentos después de este delimitador se pasan a la aplicación que se ejecuta.
-c|--configuration <CONFIGURATION>
-f|--framework <FRAMEWORK>
Compila y ejecuta la aplicación con el marco especificado. El marco debe especificarse en el archivo de
proyecto.
--force
Fuerza la resolución de todas las dependencias, incluso si la última restauración se realizó correctamente.
Especificar esta marca es lo mismo que eliminar el archivo project.assets.json.
-h|--help
Permite que el comando se detenga y espere la entrada o acción del usuario (por ejemplo, completar la
autenticación). Disponible desde el SDK de .NET Core 3.0.
--launch-profile <NAME>
El nombre del perfil de inicio (si lo hay) que se usará al iniciar la aplicación. Los perfiles de inicio se
definen en el archivo launchSettings.json y se suelen denominar Development , Staging y Production .
Para obtener más información, consulte Working with multiple environments (Trabajo con varios
entornos).
--no-build
No compila el proyecto antes de ejecutarlo. También establece la marca --no-restore de forma implícita.
--no-dependencies
Al restaurar un proyecto con referencias de proyecto a proyecto (P2P), se restaura el proyecto raíz y no las
referencias.
--no-launch-profile
No intenta usar launchSettings.json para configurar la aplicación.
--no-restore
Especifica la ruta de acceso del archivo del proyecto que se va a ejecutar (nombre de la carpeta o ruta de
acceso completa). Si no se especifica, se toma como predeterminado el directorio actual.
-r|--runtime <RUNTIME_IDENTIFIER>
Especifica el tiempo de ejecución de destino para el que restaurar los paquetes. Para obtener una lista de
identificadores de tiempo de ejecución (RID), consulte el catálogo de RID. Opción corta -r disponible a
partir del SDK de .NET Core 3.0.
-v|--verbosity <LEVEL>
Establece el nivel de detalle del comando. Los valores permitidos son q[uiet] , m[inimal] , n[ormal] ,
d[etailed] y diag[nostic] . El valor predeterminado es m . Disponible a partir del SDK de .NET Core 2.1.
Ejemplos
Ejecución del proyecto en el directorio actual:
dotnet run
Ejecute el proyecto en el directorio actual (el argumento --help en este ejemplo se pasa a la aplicación,
dado que se usa la opción -- en blanco):
Restaure las dependencias y herramientas del proyecto en el directorio actual mostrando solo la salida
mínima y, después, ejecute el proyecto: (SDK de .NET Core 2.0 y versiones superiores):
NOMBRE
dotnet sln : enumera o modifica los proyectos en un archivo de solución de .NET Core.
Sinopsis
dotnet sln [<SOLUTION_FILE>] [command]
Descripción
El comando dotnet sln proporciona una opción conveniente para enumerar y modificar los proyectos en un
archivo de solución.
Para usar el comando dotnet sln , debe existir el archivo de solución. Si necesita crear uno, use el comando
dotnet new, como en el ejemplo siguiente:
Argumentos
SOLUTION_FILE
El archivo de solución que se va a usar. Si se omite este argumento, el comando busca uno en el directorio
actual. Si encuentra varios archivos de solución o no encuentra ninguno, se produce un error en el
comando.
Opciones
-h|--help
Comandos
list
Argumentos
SOLUTION_FILE
El archivo de solución que se va a usar. Si se omite este argumento, el comando busca uno en el directorio
actual. Si encuentra varios archivos de solución o no encuentra ninguno, se produce un error en el
comando.
Opciones
-h|--help
Argumentos
SOLUTION_FILE
El archivo de solución que se va a usar. Si no se especifica, el comando busca uno en el directorio actual y
produce un error si hay varios archivos de solución.
PROJECT_PATH
La ruta de acceso al proyecto o los proyectos que se van a agregar a la solución. Las expansiones del
patrón comodines de shell de Unix y Linux se procesan correctamente mediante el comando dotnet sln .
Opciones
-h|--help
Coloca el proyecto en la raíz de la solución, en lugar de crear una carpeta de la solución. Disponible desde
el SDK de .NET Core 3.0.
-s|--solution-folder <PATH>
La ruta de acceso de la carpeta de la solución de destino a la que se van a agregar los proyectos.
Disponible desde el SDK de .NET Core 3.0.
remove
Argumentos
SOLUTION_FILE
El archivo de solución que se va a usar. Si se deja sin especificar, el comando busca uno en el directorio
actual y produce un error si hay varios archivos de solución.
PROJECT_PATH
La ruta de acceso al proyecto o los proyectos que se van a agregar a la solución. Las expansiones del
patrón comodines de shell de Unix y Linux se procesan correctamente mediante el comando dotnet sln .
Opciones
-h|--help
Ejemplos
Enumere los proyectos en una solución:
Agregue varios proyectos de C# a una solución mediante un patrón de comodines (solo para Unix y
Linux):
Quite varios proyectos de C# de una solución mediante un patrón de comodines (solo para Unix y Linux):
Name
dotnet store : almacena los ensamblados especificados en el almacenamiento de paquetes en tiempo de
ejecución.
Sinopsis
dotnet store -m|--manifest <PATH_TO_MANIFEST_FILE>
-f|--framework <FRAMEWORK_VERSION> -r|--runtime <RUNTIME_IDENTIFIER>
[--framework-version <FRAMEWORK_VERSION>] [--output <OUTPUT_DIRECTORY>]
[--skip-optimization] [--skip-symbols] [-v|--verbosity <LEVEL>]
[--working-dir <WORKING_DIRECTORY>]
Description
dotnet store almacena los ensamblados especificados en el almacenamiento de paquetes en tiempo de
ejecución. De forma predeterminada, los ensamblados están optimizados para el tiempo de ejecución y el marco
de trabajo de destino. Para obtener más información, consulte el tema Almacenamiento de paquetes en tiempo de
ejecución.
Opciones necesarias
-f|--framework <FRAMEWORK>
Especifica la plataforma de destino. La plataforma de destino tiene que especificarse en el archivo del
proyecto.
-m|--manifest <PATH_TO_MANIFEST_FILE>
Opciones no necesarias
--framework-version <FRAMEWORK_VERSION>
Especifica la versión del SDK de .NET Core. Esta opción le permite seleccionar una versión de un marco
concreto más allá del marco de trabajo especificado en la opción -f|--framework .
-h|--help
Omite la generación de símbolos. Actualmente, solo se pueden generar símbolos en Windows y Linux.
-v|--verbosity <LEVEL>
Establece el nivel de detalle del comando. Los valores permitidos son q[uiet] , m[inimal] , n[ormal] ,
d[etailed] y diag[nostic] .
-w|--working-dir <WORKING_DIRECTORY>
El directorio de trabajo que usa el comando. Si no se especifica, usa el subdirectorio obj del directorio
actual.
Ejemplos
Almacenamiento de los paquetes especificados en el archivo de proyecto packages.csproj para .NET Core
2.0.0:
Vea también
Runtime package store (Almacenamiento de paquetes en tiempo de ejecución)
dotnet test
16/09/2020 • 17 minutes to read • Edit Online
NOMBRE
dotnet test : controlador de prueba de .NET usado para ejecutar pruebas unitarias.
Sinopsis
dotnet test [<PROJECT> | <SOLUTION> | <DIRECTORY> | <DLL>]
[-a|--test-adapter-path <ADAPTER_PATH>] [--blame] [--blame-crash]
[--blame-crash-dump-type <DUMP_TYPE>] [--blame-crash-collect-always]
[--blame-hang] [--blame-hang-dump-type <DUMP_TYPE>]
[--blame-hang-timeout <TIMESPAN>]
[-c|--configuration <CONFIGURATION>]
[--collect <DATA_COLLECTOR_NAME>]
[-d|--diag <LOG_FILE>] [-f|--framework <FRAMEWORK>]
[--filter <EXPRESSION>] [--interactive]
[-l|--logger <LOGGER>] [--no-build]
[--nologo] [--no-restore] [-o|--output <OUTPUT_DIRECTORY>]
[-r|--results-directory <RESULTS_DIR>] [--runtime <RUNTIME_IDENTIFIER>]
[-s|--settings <SETTINGS_FILE>] [-t|--list-tests]
[-v|--verbosity <LEVEL>] [[--] <RunSettings arguments>]
Descripción
El comando dotnet test se usa para ejecutar pruebas unitarias en una solución determinada. El comando
dotnet test compila la solución y ejecuta una aplicación host de prueba para cada proyecto de prueba de la
solución. El host de prueba ejecuta las pruebas en el proyecto especificado mediante un marco de pruebas (por
ejemplo, MSTest, NUnit o xUnit) e informa del éxito o el error de cada prueba. Si todas las pruebas son
correctas, el ejecutor de pruebas devuelve 0 como un código de salida; en caso contrario, si se produce algún
error en una prueba, devuelve 1.
En el caso de los proyectos con varios destinos, las pruebas se ejecutan para cada plataforma de destino. El
host de pruebas y el marco de pruebas unitarias se empaquetan como paquetes NuGet y se restauran como
dependencias ordinarias para el proyecto.
Los proyectos de prueba especifican el ejecutor de pruebas usando un elemento <PackageReference> ordinario,
como se puede ver en este archivo de proyecto de ejemplo:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
</ItemGroup>
</Project>
El comando dotnet restore sigue siendo válido en algunos escenarios donde tiene sentido realizar una
restauración explícita, como las compilaciones de integración continua en Azure DevOps Services o en los
sistemas de compilación que necesitan controlar explícitamente cuándo se produce la restauración.
Para obtener información sobre cómo administrar fuentes de NuGet, vea la documentación de dotnet restore .
Argumentos
PROJECT | SOLUTION | DIRECTORY | DLL
Opciones
-a|--test-adapter-path <ADAPTER_PATH>
Ruta de acceso a un directorio en el que se van hacer búsquedas de adaptadores de prueba adicionales.
Solo se inspeccionan los archivos .dll con el sufijo .TestAdapter.dll . Si no se especifica, la búsqueda se
realiza en el directorio del archivo .dll de la prueba.
--blame
Ejecuta las pruebas en el modo de culpabilidad. Esta opción es útil para aislar las pruebas problemáticas
que hacen que el host de prueba se bloquee. Cuando se detecta un bloqueo, crea un archivo de
secuencia en TestResults/<Guid>/<Guid>_Sequence.xml que captura el orden de las pruebas ejecutadas
antes del bloqueo.
--blame-crash (Disponible desde el SDK de versión preliminar de .NET 5.0)
Ejecuta las pruebas en modo de culpa y recopila un volcado de memoria cuando el host de prueba se
cierra de forma inesperada. Esta opción solo se admite en Windows. Un directorio que contenga
procdump.exe y procdump64.exe debe estar en la variable de entorno PATH o PROCDUMP_PATH.
Descargue las herramientas. Implica --blame .
--blame-crash-dump-type <DUMP_TYPE> (Disponible desde el SDK de versión preliminar de .NET 5.0)
El tipo de volcado de memoria que se va a recopilar. Implica --blame-crash .
--blame-crash-collect-always (Disponible desde el SDK de versión preliminar de .NET 5.0)
Recopila un volcado de memoria en la salida de host de prueba esperada e inesperada.
--blame-hang (Disponible desde el SDK de versión preliminar de .NET 5.0)
Ejecute las pruebas en modo de culpa y recopile un volcado de bloqueo cuando una prueba supere el
tiempo de espera especificado.
--blame-hang-dump-type <DUMP_TYPE> (Disponible desde el SDK de versión preliminar de .NET 5.0)
El tipo de volcado de memoria que se va a recopilar. Debe ser full , mini o none . Cuando se especifica
none , el host de prueba finaliza en el tiempo de espera, pero no se recopila ningún volcado. Implica
--blame-hang .
Habilita el recopilador de datos para la ejecución de pruebas. Para obtener más información, consulte
Monitor and analyze test run (Supervisar y analizar ejecuciones de pruebas).
Para recopilar la cobertura de código en cualquier plataforma compatible con .NET Core, instale Coverlet
y use la opción --collect:"XPlat Code Coverage" .
En Windows, puede recopilar la cobertura de código mediante la opción --collect "Code Coverage" .
Esta opción genera un archivo .coverage, que se puede abrir en Visual Studio 2019 Enterprise. Para más
información, vea Uso de cobertura de código y Personalización del análisis de cobertura de código.
-d|--diag <LOG_FILE>
-f|--framework <FRAMEWORK>
Fuerza el uso del host de prueba de dotnet o .NET Framework para los archivos binarios de prueba.
Esta opción solo determina el tipo de host que se va a usar. La versión de Framework real que se va a
usar viene determinada por runtimeConfig.json del proyecto de prueba. Si no se especifica, se usa el
atributo de ensamblado TargetFramework para determinar el tipo de host. Si ese atributo se quita de .dll,
se usa el host de .NET Framework.
--filter <EXPRESSION>
Filtra las pruebas del proyecto actual con la expresión dada. Para más información, consulte la sección
Detalles de la opción de filtro. Para obtener más información y ejemplos sobre cómo usar el filtrado de
pruebas unitarias selectivas, vea Ejecución de pruebas unitarias selectivas.
-h|--help
Permite que el comando se detenga y espere una entrada o una acción del usuario. Por ejemplo, para
completar la autenticación. Disponible desde el SDK de .NET Core 3.0.
-l|--logger <LOGGER>
Especifica un registrador para los resultados de pruebas. A diferencia de MSBuild, la prueba de dotnet
no acepta abreviaturas: en lugar de -l "console;v=d" use -l "console;verbosity=detailed" .
--no-build
Ejecuta pruebas sin mostrar la pancarta de Microsoft TestPlatform. Disponible desde el SDK de
.NET Core 3.0.
--no-restore
Directorio donde se encuentran los archivos binarios que se ejecutarán. Si no se especifica la ruta de
acceso predeterminada es ./bin/<configuration>/<framework>/ . En el caso de los proyectos con varias
plataformas de destino (a través de la propiedad TargetFrameworks ), también debe definir --framework
al especificar esta opción. dotnet test siempre ejecuta las pruebas desde el directorio de salida. Puede
usar AppDomain.BaseDirectory para consumir recursos de prueba en el directorio de salida.
-r|--results-directory <RESULTS_DIR>
El archivo .runsettings que se usará para ejecutar las pruebas. El elemento TargetPlatform (x86|x64)
no tiene ningún efecto en dotnet test . Para ejecutar pruebas destinadas a x86, instale la versión x86 de
.NET Core. El valor de bits de dotnet.exe que se encuentra en la ruta de acceso es lo que se usará para
ejecutar las pruebas. Para obtener más información, vea los siguientes recursos:
Configuración de pruebas unitarias con un archivo .runsettings .
Configuración de una serie de pruebas
-t|--list-tests
Establece el nivel de detalle del comando. Los valores permitidos son q[uiet] , m[inimal] , n[ormal] ,
d[etailed] y diag[nostic] . De manera predeterminada, es minimal . Para obtener más información,
vea LoggerVerbosity.
Argumentos de RunSettings
Los argumentos RunSettings insertados se pasan como los últimos argumentos en la línea de comandos
después de "-- " (fíjese en el espacio después de --). Los argumentos RunSettings insertados se especifican
como pares de [name]=[value] . Se usa un espacio para separar varios pares de [name]=[value] .
Ejemplo: dotnet test -- MSTest.DeploymentEnabled=false MSTest.MapInconclusiveToFailed=True
Para obtener más información, vea Paso de argumentos de RunSettings a través de la línea de comandos.
Ejemplos
Ejecución de las pruebas en el proyecto en el directorio actual:
dotnet test
Ejecute las pruebas del proyecto en el directorio actual y genere un archivo de resultados de prueba en
formato trx:
Ejecute las pruebas del proyecto en el directorio actual y genere un archivo de cobertura de código
(después de instalar la integración de recopiladores de Coverlet):
Ejecute las pruebas en el proyecto en el directorio actual y genere un archivo de cobertura de código
(solo en Windows):
dotnet test --collect "Code Coverage"
Ejecute las pruebas del proyecto en el directorio actual y regístrese con el nivel de detalle
pormenorizado en la consola:
Ejecute las pruebas del proyecto en el directorio actual e informe de las pruebas que estaban en curso
cuando se bloqueó el host de prueba:
MSTest FullyQualifiedName
NOMBRE
ClassName
Prioridad
TestCategory
xUnit FullyQualifiedName
DisplayName
Rasgos
NUnit FullyQualifiedName
NOMBRE
TestCategory
Prioridad
O P ERA DO R F UN C IÓ N
= Coincidencia exacta
!= Coincidencia no exacta
~ Contiene
!~ No contiene
<value> es una cadena. Todas las búsquedas distinguen mayúsculas de minúsculas.
Una expresión sin <operator>automáticamente se considera un contains en la propiedad
FullyQualifiedName (por ejemplo, dotnet test --filter xyz es lo mismo que
dotnet test --filter FullyQualifiedName~xyz ).
O P ERA DO R F UN C IÓ N
| O
& AND
Si usa operadores condicionales (por ejemplo, (Name~TestMethod1) | (Name~TestMethod2) ), puede incluir las
expresiones entre paréntesis.
Para obtener más información y ejemplos sobre cómo usar el filtrado de pruebas unitarias selectivas, vea
Ejecución de pruebas unitarias selectivas.
Vea también
Marcos y destinos
Catálogo de identificadores de entorno de ejecución (RID) de .NET Core
Paso de argumentos runsettings a través de la línea de comandos
dotnet tool install
16/09/2020 • 4 minutes to read • Edit Online
NOMBRE
dotnet tool install : instala la herramienta de .NET Core especificada en el equipo.
Sinopsis
dotnet tool install <PACKAGE_NAME> -g|--global
[--add-source <SOURCE>] [--configfile <FILE>]
[--framework <FRAMEWORK>] [-v|--verbosity <LEVEL>]
[--version <VERSION_NUMBER>]
Descripción
El comando dotnet tool install permite instalar en el equipo herramientas de .NET Core. Para usar el
comando, especifique una de las siguientes opciones de instalación:
Para instalar una herramienta global en la ubicación predeterminada, use la opción --global .
Para instalar una herramienta global en una ubicación personalizada, use la opción --tool-path .
Para instalar una herramienta local, omita las opciones --global y --tool-path .
Las herramientas locales están disponibles a par tir del SDK de .NET Core 3.0.
Las herramientas globales se instalan en los siguientes directorios de forma predeterminada cuando se
especifica la opción -g o --global :
SO RUTA DE A C C ESO
Linux/macOS $HOME/.dotnet/tools
Windows %USERPROFILE%\.dotnet\tools
Argumentos
PACKAGE_NAME
Nombre o identificador del paquete NuGet que contiene la herramienta de .NET Core que se quiere
instalar.
Opciones
add-source <SOURCE>
Especifica el marco de destino para instalar la herramienta. De forma predeterminada, el SDK de .NET
Core intenta elegir la plataforma de destino más apropiada.
-g|--global
Especifica que la instalación se realiza en todos los usuarios. No se puede combinar con la opción
--tool-path . Al omitir --global y --tool-path , se especifica la instalación de una herramienta local.
-h|--help
Especifica la ubicación de donde se tiene que instalar la herramienta global. PATH puede ser una ruta
absoluta o relativa. Si la ruta no existe, el comando intenta crearla. Al omitir --global y --tool-path , se
especifica la instalación de una herramienta local.
-v|--verbosity <LEVEL>
Establece el nivel de detalle del comando. Los valores permitidos son q[uiet] , m[inimal] , n[ormal] ,
d[etailed] y diag[nostic] .
--version <VERSION_NUMBER>
Ejemplos
dotnet tool install -g dotnetsay
Vea también
Herramientas de .NET Core
Tutorial: Instalación y uso de una herramienta global de .NET Core mediante la CLI de .NET Core
Tutorial: Instalación y uso de una herramienta local de .NET Core mediante la CLI de .NET Core
dotnet tool list
16/09/2020 • 3 minutes to read • Edit Online
NOMBRE
dotnet tool list : enumera todas las herramientas de .NET Core del tipo especificado actualmente instalado en
el equipo.
Sinopsis
dotnet tool list -g|--global
Descripción
El comando dotnet tool list permite enumerar todas las herramientas locales, de ruta de acceso de
herramientas y globales de .NET Core instaladas en el equipo. El comando enumera el nombre del paquete, la
versión instalada y el comando de la herramienta. Para usar el comando, especifique una de las siguientes
opciones:
Para enumerar las herramientas globales instaladas en la ubicación predeterminada, use la opción --global .
Para enumerar las herramientas globales instaladas en la ubicación personalizada, use la opción --tool-path .
Para enumerar las herramientas locales, use la opción --local u omita las opciones --global , --tool-path y
--local .
Las herramientas locales están disponibles a par tir del SDK de .NET Core 3.0.
Opciones
-g|--global
Enumera las herramientas globales de los usuarios. No se puede combinar con la opción --tool-path . Al
omitir --global y --tool-path , se muestran las herramientas locales.
-h|--help
Enumera las herramientas locales del directorio actual. No se puede combinar con las opciones --global
o --tool-path . Al omitir --global y --tool-path , se enumeran las herramientas locales, aunque no se
haya especificado --local .
--tool-path <PATH>
Especifica una ubicación personalizada para las herramientas globales. PATH puede ser una ruta absoluta o
relativa. No se puede combinar con la opción --global . Al omitir --global y --tool-path , se muestran
las herramientas locales.
Ejemplos
dotnet tool list -g
Enumera todas las herramientas globales instaladas para todos los usuarios en su equipo (perfil de
usuario actual).
dotnet tool list --tool-path c:\global-tools
Vea también
Herramientas de .NET Core
Tutorial: Instalación y uso de una herramienta global de .NET Core mediante la CLI de .NET Core
Tutorial: Instalación y uso de una herramienta local de .NET Core mediante la CLI de .NET Core
dotnet tool restore
16/09/2020 • 2 minutes to read • Edit Online
NOMBRE
dotnet tool restore : instala en el equipo las herramientas locales de .NET Core que se encuentran en el ámbito del
directorio actual.
Sinopsis
dotnet tool restore
[--configfile <FILE>] [--add-source <SOURCE>]
[tool-manifest <PATH_TO_MANIFEST_FILE>] [--disable-parallel]
[--ignore-failed-sources] [--no-cache] [--interactive]
[-v|--verbosity <LEVEL>]
Descripción
El comando dotnet tool restore busca el archivo de manifiesto de las herramientas que está en el ámbito del
directorio actual e instala las herramientas que se indican en él. Para obtener información sobre los archivos de
manifiesto, vea Instalación de una herramienta local e Invocación de una herramienta local.
Opciones
--configfile <FILE>
Permite que el comando se detenga y espere la entrada o acción del usuario (por ejemplo, completar la
autenticación).
-h|--help
Establece el nivel de detalle del comando. Los valores permitidos son q[uiet] , m[inimal] , n[ormal] ,
d[etailed] y diag[nostic] .
Ejemplo
dotnet tool restore
Vea también
Herramientas de .NET Core
Tutorial: Instalación y uso de una herramienta local de .NET Core mediante la CLI de .NET Core
dotnet tool run
20/04/2020 • 2 minutes to read • Edit Online
NOMBRE
dotnet tool run : invoca una herramienta local.
Sinopsis
dotnet tool run <COMMAND NAME>
Descripción
El comando dotnet tool run busca los archivos de manifiesto de las herramientas que se encuentran en el ámbito
del directorio actual. Cuando encuentra una referencia a la herramienta especificada, ejecuta la herramienta. Para
obtener más información, vea Invocación de una herramienta local.
Argumentos
COMMAND_NAME
Opciones
-h|--help
Ejemplo
dotnet tool run dotnetsay
Vea también
Herramientas de .NET Core
Tutorial: Instalación y uso de una herramienta local de .NET Core mediante la CLI de .NET Core
dotnet tool uninstall
20/04/2020 • 3 minutes to read • Edit Online
NOMBRE
dotnet tool uninstall : desinstala la herramienta de .NET Core especificada del equipo.
Sinopsis
dotnet tool uninstall <PACKAGE_NAME> -g|--global
Descripción
El comando dotnet tool uninstall permite desinstalar del equipo herramientas de .NET Core. Para usar el
comando, especifique una de las siguientes opciones:
Para desinstalar una herramienta global que se instaló en la ubicación predeterminada, use la opción
--global .
Para desinstalar una herramienta global que se instaló en una ubicación personalizada, use la opción
--tool-path .
Para desinstalar una herramienta local, omita las opciones --global y --tool-path .
Las herramientas locales están disponibles a par tir del SDK de .NET Core 3.0.
Argumentos
PACKAGE_NAME
Nombre o identificador del paquete NuGet que contiene la herramienta de .NET Core que se quiere
desinstalar. Para conocer el nombre el paquete, use el comando dotnet tool list.
Opciones
-g|--global
Especifica que la herramienta que se va a quitar es de una instalación en el ámbito de los usuarios. No se
puede combinar con la opción --tool-path . Al omitir --global y --tool-path , se especifica que la
herramienta que se va a quitar es una herramienta local.
-h|--help
Ejemplos
dotnet tool uninstall -g dotnetsay
Vea también
Herramientas de .NET Core
Tutorial: Instalación y uso de una herramienta global de .NET Core mediante la CLI de .NET Core
Tutorial: Instalación y uso de una herramienta local de .NET Core mediante la CLI de .NET Core
dotnet tool update
16/09/2020 • 5 minutes to read • Edit Online
NOMBRE
dotnet tool update : actualiza la herramienta de .NET Core especificada en el equipo.
Sinopsis
dotnet tool update <PACKAGE_ID> -g|--global
[--add-source <SOURCE>] [--configfile <FILE>]
[--disable-parallel] [--framework <FRAMEWORK>]
[--ignore-failed-sources] [--interactive] [--no-cache]
[-v|--verbosity <LEVEL>] [--version <VERSION>]
Descripción
El comando dotnet tool update permite actualizar las herramientas de .NET Core en su equipo a la versión
estable más reciente del paquete. El comando desinstala y vuelve a instalar una herramienta, actualizándola de
facto. Para usar el comando, especifique una de las siguientes opciones:
Para actualizar una herramienta global que se instaló en la ubicación predeterminada, use la opción --global .
Para actualizar una herramienta global que se instaló en una ubicación personalizada, use la opción
--tool-path .
Para actualizar una herramienta local, use la opción --local .
Las herramientas locales están disponibles a par tir del SDK de .NET Core 3.0.
Argumentos
PACKAGE_ID
Nombre o identificador del paquete de NuGet que contiene la herramienta global de .NET Core que se
quiere actualizar. Para conocer el nombre el paquete, use el comando dotnet tool list.
Opciones
--add-source <SOURCE>
Permite que el comando se detenga y espere la entrada o acción del usuario (por ejemplo, completar la
autenticación).
--local
Actualice la herramienta y el manifiesto de la herramienta local. No se puede combinar con las opciones
--global o --tool-path .
--no-cache
Especifica la ubicación en la que está instalada la herramienta global. PATH puede ser una ruta absoluta o
relativa. No se puede combinar con la opción --global . Al omitir --global y --tool-path , se especifica
que la herramienta que se va a actualizar es una herramienta local.
--version <VERSION>
El intervalo de versiones del paquete de herramientas al que se actualiza. Esto no se puede usar para
degradar versiones, primero debe uninstall versiones más recientes.
-g|--global
Especifica que la actualización es para una herramienta del ámbito de los usuarios. No se puede combinar
con la opción --tool-path . Al omitir --global y --tool-path , se especifica que la herramienta que se va a
actualizar es una herramienta local.
-h|--help
Establece el nivel de detalle del comando. Los valores permitidos son q[uiet] , m[inimal] , n[ormal] ,
d[etailed] y diag[nostic] .
Ejemplos
dotnet tool update -g dotnetsay
Actualiza la herramienta global dotnetsay a la última versión de revisión, con una versión principal de 2 y
una versión secundaria de 0 .
dotnet tool update -g dotnetsay --version (2.0.*,2.1.4)
Actualiza la herramienta global dotnetsay a la versión más baja del intervalo especificado
(> 2.0.0 && < 2.1.4) ; se instalará la versión 2.1.0 . Para obtener más información sobre los intervalos de
versiones semánticas, consulte Intervalos de versiones de empaquetado de NuGet.
Vea también
Herramientas de .NET Core
Versionamiento semántico
Tutorial: Instalación y uso de una herramienta global de .NET Core mediante la CLI de .NET Core
Tutorial: Instalación y uso de una herramienta local de .NET Core mediante la CLI de .NET Core
dotnet vstest
12/05/2020 • 6 minutes to read • Edit Online
IMPORTANT
El comando dotnet vstest se sustituye por dotnet test , que ahora se puede usar para ejecutar ensamblados. Vea
dotnet test .
NOMBRE
dotnet-vstest : permite ejecutar pruebas a partir de los ensamblados especificados.
Sinopsis
dotnet vstest [<TEST_FILE_NAMES>] [--Blame] [--Diag <PATH_TO_LOG_FILE>]
[--Framework <FRAMEWORK>] [--InIsolation] [-lt|--ListTests <FILE_NAME>]
[--logger <LOGGER_URI/FRIENDLY_NAME>] [--Parallel]
[--ParentProcessId <PROCESS_ID>] [--Platform] <PLATFORM_TYPE>
[--Port <PORT>] [--ResultsDirectory<PATH>] [--Settings <SETTINGS_FILE>]
[--TestAdapterPath <PATH>] [--TestCaseFilter <EXPRESSION>]
[--Tests <TEST_NAMES>] [[--] <args>...]]
Descripción
El comando dotnet-vstest ejecuta la aplicación de línea de comandos VSTest.Console para ejecutar pruebas
unitarias automatizadas.
Argumentos
TEST_FILE_NAMES
Ejecutar pruebas desde los ensamblados especificados. Separar varios nombres de ensamblado de prueba
con espacios. Se admite caracteres comodín.
Opciones
--Blame
Ejecuta las pruebas en el modo de culpabilidad. Esta opción es útil para aislar las pruebas problemáticas que
hacen que el host de prueba se bloquee. Crea un archivo de salida en el directorio actual como
Sequence.xml que captura el orden de ejecución de pruebas antes del bloqueo.
--Diag <PATH_TO_LOG_FILE>
Permite registros detallados para la plataforma de prueba. Los registros se escriben en el archivo
proporcionado.
--Framework <FRAMEWORK>
Identificar la versión de .NET Framework usada en la ejecución de pruebas. Ejemplos de valores válidos son
.NETFramework,Version=v4.6 o .NETCoreApp,Version=v1.0 . Otros valores admitidos son Framework40 ,
Framework45 , FrameworkCore10 y FrameworkUap10 .
--InIsolation
Ejecuta las pruebas en un proceso aislado. De este modo, es menos probable que el proceso
vstest.console.exe se detenga por un error de las pruebas, pero es posible que las pruebas se ejecuten más
despacio.
-lt|--ListTests <FILE_NAME>
/logger:TfsPublisher;
Collection=<team project collection url>;
BuildName=<build name>;
TeamProject=<team project name>
[;Platform=<Defaults to "Any CPU">]
[;Flavor=<Defaults to "Debug">]
[;RunTitle=<title>]
Para registrar los resultados en un archivo de resultados de pruebas (TRX) de Visual Studio, use el
proveedor de registrador trx . Este modificador, crea un archivo en el directorio de resultados de
pruebas con un nombre de archivo de registro dado. Si no se proporciona LogFileName , se crea un
nombre de archivo único para contener los resultados de las pruebas.
--Parallel
Ejecuta pruebas en paralelo. De forma predeterminada, todos los núcleos disponibles en el equipo están
disponibles para su uso. Especifique un número explícito de núcleos mediante la configuración de la
propiedad MaxCpuCount en el nodo RunConfiguration del archivo runsettings.
--ParentProcessId <PROCESS_ID>
Identificar la arquitectura de la plataforma usada en la ejecución de pruebas. Valores válidos son x86 , x64
y ARM .
--Port <PORT>
Usar adaptadores de prueba personalizados desde una ruta de acceso especificada (si existe) en la serie de
pruebas.
--TestCaseFilter <EXPRESSION>
Ejecuta pruebas que coinciden con la expresión dada. <EXPRESSION> tiene el formato
<property>Operator<value>[|&<EXPRESSION>] , donde Operator es = , != o ~ . El operador ~ tiene
semántica "contains" y se aplica a las propiedades de cadena como DisplayName . Los paréntesis () se usan
para agrupar subexpresiones. Para obtener más información, vea Filtro TestCase.
--Tests <TEST_NAMES>
Ejecuta las pruebas con nombres que coinciden con los Separar varios valores con comas.
-?|--Help
Especifica argumentos adicionales para pasar al adaptador. Los argumentos se especifican como pares de
nombre-valor en el formato <n>=<v> , donde <n> es el nombre del argumento y <v> es el valor del
argumento. Use un espacio para separar varios argumentos.
Ejemplos
Ejecución de pruebas en mytestproject.dll:
Ejecución de pruebas en mytestproject.dll, exportación a una carpeta personalizada con nombre personalizado:
Vea también
Opciones de la línea de comandos para VSTest.Console.exe
referencia de scripts de dotnet-install
16/09/2020 • 11 minutes to read • Edit Online
NOMBRE
dotnet-install.ps1 | dotnet-install.sh : script usado para instalar el SDK de .NET Core y el entorno de
ejecución compartido.
Sinopsis
Windows:
Get-Help ./dotnet-install.ps1
Linux/macOS:
dotnet-install.sh --help
El script de bash también lee modificadores de PowerShell, por lo que puede usar modificadores de
PowerShell con el script en sistemas Linux y macOS.
Descripción
Los scripts dotnet-install realizan una instalación sin derechos administrativos del SDK de .NET Core,
que incluye la CLI de .NET Core y el entorno de tiempo de ejecución compartido. Hay dos scripts:
un script de PowerShell que funciona en Windows;
un script de Bash que funciona en Linux y macOS.
Propósito
El uso previsto de los scripts es en escenarios de integración continua (CI), donde:
el SDK debe instalarse sin interacción del usuario y sin derechos de administrador;
no es necesario que la instalación del SDK se mantenga en varias ejecuciones de CI.
Esta es la secuencia típica de eventos:
Se desencadena la CI.
CI instala el SDK mediante uno de estos scripts.
CI finaliza su trabajo y borra los datos temporales, incluida la instalación del SDK.
Para configurar un entorno de desarrollo o ejecutar aplicaciones, use los instaladores en lugar de estos
scripts.
Versión recomendada
Se recomienda usar la versión estable de los scripts:
Bash (Linux/macOS): https://dot.net/v1/dotnet-install.sh
PowerShell (Windows): https://dot.net/v1/dotnet-install.ps1
Comportamiento de los scripts
Ambos scripts tienen el mismo comportamiento. Descargan el archivo ZIP o tarball desde las entregas
de compilación de la CLI y proceden a instalarlo en la ubicación predeterminada o en una ubicación
especificada por -InstallDir|--install-dir .
De forma predeterminada, los scripts de instalación descargan el SDK y lo instalan. Si desea obtener
solo el tiempo de ejecución compartido, especifique el argumento -Runtime|--runtime .
De forma predeterminada, el script agrega la ubicación de instalación a $PATH para la sesión actual.
Para invalidar este comportamiento, especifique el argumento -NoPath|--no-path . El script no establece
la variable de entorno DOTNET_ROOT .
Antes de ejecutar el script, instale las dependencias necesarias.
Puede instalar una versión específica mediante el argumento -Version|--version . La versión debe
especificarse como un número de versión de tres partes, por ejemplo, 2.1.0 . Si no se especifica la
versión, el script instala la versión latest .
Los scripts de instalación no actualizan el Registro en Windows. Solo descargan los archivos binarios
comprimidos y los copian en una carpeta. Si desea que se actualicen los valores de las claves del
Registro, use los instaladores de .NET Core.
Opciones
-Architecture|--architecture <ARCHITECTURE>
Arquitectura de los archivos binarios de .NET Core para instalar. Los valores posibles son <auto>
, amd64 , x64 , x86 , arm64 y arm . El valor predeterminado es <auto> , que representa la
arquitectura de SO que se ejecuta en ese momento.
-AzureFeed|--azure-feed
Se utiliza como una cadena de consulta para anexar a la fuente de Azure. Permite cambiar la
dirección URL para usar cuentas de almacenamiento de blobs no público.
--help
Imprime la ayuda para el script. Solo se aplica al script de Bash. Para PowerShell, use
Get-Help ./dotnet-install.ps1 .
-InstallDir|--install-dir <DIRECTORY>
Especifica una ruta de acceso a un archivo global.json que se va a usar para determinar la
versión del SDK. El archivo global.json debe tener un valor para sdk:version .
-NoCdn|--no-cdn
Deshabilita la descarga desde Azure Content Delivery Network (CDN) y usa la fuente no
almacenada en caché directamente.
-NoPath|--no-path
Si se establece, el instalador usa el proxy al realizar solicitudes web. (Solo es válido para
Windows).
-ProxyBypassList <LIST_OF_URLS>
Si se establece con ProxyAddress , proporciona una lista de direcciones URL separadas por
comas que omiten el proxy. (Solo es válido para Windows).
ProxyUseDefaultCredentials
Si se establece, el instalador usa las credenciales del usuario actual cuando se usa la dirección del
proxy. (Solo es válido para Windows).
-Runtime|--runtime <RUNTIME>
--runtime-id <RID>
Especifica el identificador de entorno de ejecución para el que se van a instalar las herramientas.
Use linux-x64 para Linux portátil. (Solo es válido para Linux/macOS).
-SharedRuntime|--shared-runtime
NOTE
Este parámetro está obsoleto y puede quitarse en una versión futura del script. La alternativa
recomendada es la opción -Runtime|--runtime .
Se instalan simplemente los bits del entorno de tiempo de ejecución compartido, no el SDK
completo. Esta opción equivale a especificar -Runtime|--runtime dotnet .
-SkipNonVersionedFiles|--skip-non-versioned-files
Permite cambiar la dirección URL de la fuente no almacenada en caché que este instalador
utiliza. Le recomendamos que no cambie este valor.
-Verbose|--verbose
Ejemplos
Instale la versión compatible a largo plazo más reciente en la ubicación predeterminada:
Windows:
macOS y Linux:
macOS y Linux:
Obtenga el script e instale la versión 2.1.2 detrás de un proxy corporativo (solo Windows):
Obtenga el script e instale ejemplos de una línea para la CLI de .NET Core:
Windows:
# Run a separate PowerShell process because the script calls exit, so it will end the
current PowerShell session.
&powershell -NoProfile -ExecutionPolicy unrestricted -Command "
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &
([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-
install.ps1'))) <additional install-script args>"
macOS y Linux:
Vea también
Versiones de .NET Core
Archivo de descarga del SDK y .NET Core Runtime
dotnet add reference
04/05/2020 • 2 minutes to read • Edit Online
NOMBRE
dotnet add reference Agrega referencias entre proyectos (P2P) .
Sinopsis
dotnet add [<PROJECT>] reference [-f|--framework <FRAMEWORK>]
[--interactive] <PROJECT_REFERENCES>
Descripción
El comando dotnet add reference constituye una opción práctica para agregar referencias de proyecto a un
proyecto. Después de ejecutar el comando, los elementos <ProjectReference> se agregan al archivo del
proyecto.
<ItemGroup>
<ProjectReference Include="app.csproj" />
<ProjectReference Include="..\lib2\lib2.csproj" />
<ProjectReference Include="..\lib1\lib1.csproj" />
</ItemGroup>
Argumentos
PROJECT
Especifica el archivo del proyecto. Si no se especifica, el comando busca uno en el directorio actual.
PROJECT_REFERENCES
Referencias entre proyectos (P2P) que se van a agregar. Especifique uno o más proyectos. El patrón glob
se admite en sistemas basados en Unix/Linux.
Opciones
-f|--framework <FRAMEWORK>
Agrega referencias de proyecto solo cuando apunta a un marco específico con el formato TFM.
-h|--help
Permite que el comando se detenga y espere la entrada o acción del usuario (se suele utilizar para
completar la autenticación). Disponible desde el SDK de .NET Core 3.0.
Ejemplos
Agregar una referencia de proyecto:
NOMBRE
dotnet list reference : enumera las referencias entre proyectos.
Sinopsis
dotnet list [<PROJECT>] reference
Descripción
El comando dotnet list reference constituye una opción práctica para enumerar las referencias de proyecto de
un proyecto concreto.
Argumentos
PROJECT
El archivo del proyecto sobre el que actuar. Si no se especifica un archivo, el comando buscará uno en el
directorio actual.
Opciones
-h|--help
Ejemplos
Enumerar las referencias del proyecto para el proyecto especificado:
NOMBRE
dotnet remove reference : quita las referencias entre proyectos (P2P).
Sinopsis
dotnet remove [<PROJECT>] reference [-f|--framework <FRAMEWORK>]
<PROJECT_REFERENCES>
Descripción
El comando dotnet remove reference constituye una opción práctica para quitar referencias de proyecto de un
proyecto.
Argumentos
PROJECT
Referencias de proyecto a proyecto (P2P) que se van a quitar. Puede especificar uno o varios proyectos. Se admiten
patrones globales en terminales basados en Unix o Linux.
Opciones
-h|--help
Solo quita la referencia cuando el destino es un marco de trabajo específico con el formato TFM.
Ejemplos
Quitar una referencia de proyecto del proyecto especificado:
NOMBRE
dotnet add package : agrega una referencia de paquete a un archivo del proyecto.
Sinopsis
dotnet add [<PROJECT>] package <PACKAGE_NAME>
[-f|--framework <FRAMEWORK>] [--interactive]
[-n|--no-restore] [--package-directory <PACKAGE_DIRECTORY>]
[-s|--source <SOURCE>] [-v|--version <VERSION>]
Descripción
El comando dotnet add package constituye una opción práctica para agregar una referencia de paquete a un
archivo del proyecto. Después de ejecutar el comando, existe una comprobación de compatibilidad para
garantizar que el paquete es compatible con los marcos del proyecto. Si se pasa la comprobación, un elemento
<PackageReference> se agrega al archivo del proyecto y dotnet restore se ejecuta.
Por ejemplo, si agrega Newtonsoft.Json a ToDo.csproj se producirá un resultado similar al del siguiente ejemplo:
Writing C:\Users\me\AppData\Local\Temp\tmp95A8.tmp
info : Adding PackageReference for package 'Newtonsoft.Json' into project 'C:\projects\ToDo\ToDo.csproj'.
log : Restoring packages for C:\Temp\projects\consoleproj\consoleproj.csproj...
info : GET https://api.nuget.org/v3-flatcontainer/newtonsoft.json/index.json
info : OK https://api.nuget.org/v3-flatcontainer/newtonsoft.json/index.json 79ms
info : GET https://api.nuget.org/v3-flatcontainer/newtonsoft.json/12.0.1/newtonsoft.json.12.0.1.nupkg
info : OK https://api.nuget.org/v3-flatcontainer/newtonsoft.json/12.0.1/newtonsoft.json.12.0.1.nupkg 232ms
log : Installing Newtonsoft.Json 12.0.1.
info : Package 'Newtonsoft.Json' is compatible with all the specified frameworks in project
'C:\projects\ToDo\ToDo.csproj'.
info : PackageReference for package 'Newtonsoft.Json' version '12.0.1' added to file
'C:\projects\ToDo\ToDo.csproj'.
El archivo ToDo.csproj contiene ahora un elemento <PackageReference> para el paquete al que hace referencia.
Restauración implícita
No es necesario ejecutar dotnet restore porque lo ejecutan implícitamente todos los comandos que necesitan
que se produzca una restauración, como dotnet new , dotnet build , dotnet run , dotnet test , dotnet publish y
dotnet pack . Para deshabilitar la restauración implícita, use la opción --no-restore .
El comando dotnet restore sigue siendo válido en algunos escenarios donde tiene sentido realizar una
restauración explícita, como las compilaciones de integración continua en Azure DevOps Services o en los
sistemas de compilación que necesitan controlar explícitamente cuándo se produce la restauración.
Para obtener información sobre cómo administrar fuentes de NuGet, vea la documentación de dotnet restore .
Argumentos
PROJECT
Especifica el archivo del proyecto. Si no se especifica, el comando busca uno en el directorio actual.
PACKAGE_NAME
Opciones
-f|--framework <FRAMEWORK>
Permite que el comando se detenga y espere la entrada o acción del usuario (por ejemplo, completar la
autenticación). Disponible desde el SDK de .NET Core 2.1, versión 2.1.400 o posterior.
-n|--no-restore
Agrega una referencia de paquete sin realizar una vista previa de restauración y una comprobación de
compatibilidad.
--package-directory <PACKAGE_DIRECTORY>
Directorio donde quiere restaurar los paquetes. La ubicación predeterminada de restauración de paquetes
es %userprofile%\.nuget\packages en Windows y ~/.nuget/packages en macOS y Linux. Para obtener más
información, vea Administración de las carpetas de paquetes globales, de caché y temporales in NuGet.
-s|--source <SOURCE>
URI del origen del paquete NuGet que se usará durante la operación de restauración.
-v|--version <VERSION>
Versión del paquete. Consulte NuGet package versioning (Control de versiones de paquetes NuGet).
Ejemplos
Agregar un paquete de NuGet Newtonsoft.Json a un proyecto:
Vea también
Administración de las carpetas de paquetes globales, de caché y temporales en NuGet
Control de versiones de paquetes NuGet
dotnet list package
16/09/2020 • 6 minutes to read • Edit Online
NOMBRE
dotnet list package : muestra las referencias de paquete de un proyecto o una solución.
Sinopsis
dotnet list [<PROJECT>|<SOLUTION>] package [--config <SOURCE>]
[--deprecated]
[--framework <FRAMEWORK>] [--highest-minor] [--highest-patch]
[--include-prerelease] [--include-transitive] [--interactive]
[--outdated] [--source <SOURCE>]
Descripción
El comando dotnet list package ofrece una opción práctica para mostrar todas las referencias de paquete de
NuGet de una solución o un proyecto específico. Primero deberá crear el proyecto para tener los recursos
necesarios para que este comando se procese. En el ejemplo siguiente se muestra la salida del comando
dotnet list package para el proyecto SentimentAnalysis:
La columna Requested hace referencia a la versión de paquete especificada en el archivo del proyecto y puede ser
un intervalo. La columna Resolved muestra la versión que el proyecto usa actualmente y siempre se trata de un
valor único. Los paquetes que tienen (A) junto al nombre representan referencias implícitas de paquete que se
deducen de la configuración del proyecto (tipo de Sdk , propiedad <TargetFramework> o <TargetFrameworks> , etc.).
Use la opción --outdated para averiguar si hay disponibles más recientes de los paquetes que usa en los
proyectos. De manera predeterminada, --outdated muestra los paquetes estables más recientes, a menos que la
versión resuelta también sea una versión preliminar. Para incluir versiones preliminares cuando se muestren
versiones más recientes, especifique también la opción --include-prerelease . En los ejemplos siguientes se
muestra la salida del comando dotnet list package --outdated --include-prerelease para el mismo proyecto, tal
como en el ejemplo anterior:
The following sources were used:
https://api.nuget.org/v3/index.json
C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\
Si tiene que averiguar si el proyecto tiene dependencias transitivas, use la opción --include-transitive . Las
dependencias transitivas se producen cuando se agrega un paquete al proyecto que, a su vez, se basa en otro
paquete. En el ejemplo siguiente se muestra la salida de la ejecución del comando
dotnet list package --include-transitive en el proyecto HelloPlugin, que muestra paquetes de nivel superior y los
paquetes de los que dependen:
Argumentos
PROJECT | SOLUTION
El archivo de proyecto o solución donde se operará. Si no se especifica, el comando busca uno en el directorio
actual. Si se encuentra más de una solución o proyecto, se genera un error.
Opciones
--config <SOURCE>
Los orígenes de NuGet que se usarán al buscar paquetes más recientes. Requiere la opción --outdated .
--deprecated
Muestra solo los paquetes aplicables al marco de destino especificado. Para especificar varios marcos, repita
la opción varias veces. Por ejemplo: --framework netcoreapp2.2 --framework netstandard2.0 .
-h|--help
Considere solo los paquetes con un número de versión principal coincidente al buscar paquetes más
recientes. Requiere la opción --outdated o --deprecated .
--highest-patch
Considere solo los paquetes con número de versión principal y secundario coincidente al buscar paquetes
más recientes. Requiere la opción --outdated o --deprecated .
--include-prerelease
Considere los paquetes con versiones preliminares al buscar paquetes más recientes. Requiere la opción
--outdated o --deprecated .
--include-transitive
Enumera los paquetes transitivos, además de los paquetes de nivel superior. Al especificar esta opción,
recibe una lista de paquetes de los que dependen los paquetes de nivel superior.
--interactive
Permite que el comando se detenga y espere una entrada o una acción del usuario. Por ejemplo, para
completar la autenticación. Disponible desde el SDK de .NET Core 3.0.
--outdated
Los orígenes de NuGet que se usarán al buscar paquetes más recientes. Requiere la opción --outdated o
--deprecated .
Ejemplos
Muestre las referencias de paquete de un proyecto específico:
Muestre las referencias de paquete que tienen versiones más recientes disponibles, incluidas versiones
preliminares:
Name
dotnet remove package : quita la referencia de paquete de un archivo de proyecto.
Sinopsis
dotnet remove [<PROJECT>] package <PACKAGE_NAME>
Description
El comando dotnet remove package constituye una opción práctica para quitar una referencia de paquete NuGet
de un proyecto.
Argumentos
PROJECT
Especifica el archivo del proyecto. Si no se especifica, el comando busca uno en el directorio actual.
PACKAGE_NAME
Opciones
-h|--help
Ejemplos
Quite el paquete NuGet Newtonsoft.Json de un proyecto en el directorio actual:
IMPORTANT
Las herramientas de .NET Core se ejecutan con plena confianza. No instale una herramienta de .NET Core a menos
que confíe en el autor.
La salida muestra el comando que se usa para invocar la herramienta y la versión instalada, de forma
similar al ejemplo siguiente:
You can invoke the tool using the following command: dotnetsay
Tool 'dotnetsay' (version '2.1.4') was successfully installed.
La ubicación predeterminada de los archivos binarios de una herramienta depende del sistema operativo:
SO RUTA DE A C C ESO
Linux/macOS $HOME/.dotnet/tools
Windows %USERPROFILE%\.dotnet\tools
Esta ubicación se agrega a la ruta de acceso del usuario cuando se ejecuta el SDK por primera vez, por lo
que las herramientas globales se pueden invocar desde cualquier directorio sin especificar la ubicación de la
herramienta.
El acceso a las herramientas es específico del usuario, no de la máquina global. Una herramienta global solo
está disponible para el usuario que ha instalado la herramienta.
Instalación de una herramienta global en una ubicación personalizada
Para instalar una herramienta como una herramienta global, use la opción --tool-path del comando
dotnet tool install, tal como se muestra en los ejemplos siguientes.
En Windows:
En Linux o macOS:
El SDK de .NET Core no agrega esta ubicación automáticamente a la variable de entorno PATH. Para invocar
una herramienta de ruta de acceso de herramientas, tiene que asegurarse de que el comando está
disponible mediante uno de los métodos siguientes:
Agregue el directorio de instalación a la variable de entorno PATH.
Especifique la ruta de acceso completa a la herramienta al invocarla.
Invoque la herramienta desde el directorio de instalación.
Este comando crea un archivo de manifiesto denominado dotnet-tools.json en el directorio .config. Para
agregar una herramienta local al archivo de manifiesto, use el comando dotnet tool install y omita las
opciones --global y --tool-path , tal como se muestra en el ejemplo siguiente:
En la salida del comando se muestra el archivo de manifiesto en el que se encuentra la herramienta que
acaba de instalar, de manera similar al siguiente ejemplo:
You can invoke the tool from this directory using the following command:
dotnet tool run dotnetsay
Tool 'dotnetsay' (version '2.1.4') was successfully installed.
Entry is added to the manifest file /home/name/botsay/.config/dotnet-tools.json.
En el ejemplo siguiente se muestra un archivo de manifiesto con dos herramientas locales instaladas:
{
"version": 1,
"isRoot": true,
"tools": {
"botsay": {
"version": "1.0.0",
"commands": [
"botsay"
]
},
"dotnetsay": {
"version": "2.1.3",
"commands": [
"dotnetsay"
]
}
}
}
Normalmente, una herramienta local se agrega al directorio raíz del repositorio. Después de insertar el
archivo de manifiesto en el repositorio, los desarrolladores que extraen código del repositorio obtienen el
archivo de manifiesto más reciente. Para instalar todas las herramientas enumeradas en el archivo de
manifiesto, ejecutan el comando dotnet tool restore :
dotnet tool restore
La salida muestra la versión y el comando de cada herramienta, de forma similar al ejemplo siguiente:
Tal como se muestra en este ejemplo, la lista muestra las herramientas locales. Para ver las herramientas
globales, use la opción --global y, para ver herramientas de ruta de acceso de herramientas, use la opción
--tool-path .
dotnetsay
dotnet-doc
Si el comando comienza con el prefijo dotnet- , una manera alternativa de invocar la herramienta es usar el
comando dotnet y omitir el prefijo del comando de la herramienta. Por ejemplo, si el comando es
dotnet-doc , el siguiente comando invoca la herramienta:
dotnet doc
Sin embargo, en el siguiente escenario no se puede usar el comando dotnet para invocar una herramienta
global:
Una herramienta global y una herramienta local tienen el mismo comando con el prefijo dotnet- .
Quiere invocar la herramienta global desde un directorio que está en el ámbito de la herramienta local.
En este escenario, dotnet doc y dotnet dotnet-doc invocan a la herramienta local. Para invocar la
herramienta global, use el comando por sí solo:
dotnet-doc
Si el comando tiene el prefijo dotnet- , puede incluir u omitir el prefijo al invocar la herramienta. Por
ejemplo, si el comando es dotnet-doc , cualquiera de los siguientes ejemplos invoca la herramienta local:
En el caso de una herramienta local, el SDK encuentra el primer archivo de manifiesto que contiene el
identificador de paquete mediante la búsqueda en el directorio actual y en los directorios principales. Si no
hay ningún identificador del paquete en ningún archivo de manifiesto, el SDK agrega una nueva entrada al
archivo de manifiesto más cercano.
Para obtener instrucciones sobre el uso de la herramienta, escriba uno de los siguientes comandos o vea el
sitio web de la herramienta:
<command> --help
dotnet <command> --help
Si una herramienta no se puede instalar o ejecutar, consulte Solución de problemas de uso de herramientas
de .NET Core.
Vea también
Tutorial: Creación de una herramienta de .NET Core mediante la CLI de .NET Core
Tutorial: Instalación y uso de una herramienta global de .NET Core mediante la CLI de .NET Core
Tutorial: Instalación y uso de una herramienta local de .NET Core mediante la CLI de .NET Core
Solución de problemas de uso de herramientas de
.NET Core
16/09/2020 • 14 minutes to read • Edit Online
Es posible que surjan problemas al intentar instalar o ejecutar una herramienta de .NET Core, la cual puede ser
global o local. En este artículo se describen las causas principales más comunes y algunas posibles soluciones.
Could not execute because the specified command or file was not found.
Possible reasons for this include:
* You misspelled a built-in dotnet command.
* You intended to execute a .NET Core program, but dotnet-xyz does not exist.
* You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on
the PATH.
El nombre del archivo ejecutable determina cómo se invoca la herramienta. En la siguiente tabla se describe el
formato:
<toolName>.exe <toolName>
Herramientas globales
Las herramientas globales pueden instalarse en el directorio predeterminado o en una ubicación específica.
Los directorios predeterminados son:
SO RUTA DE A C C ESO
Linux/macOS $HOME/.dotnet/tools
Windows %USERPROFILE%\.dotnet\tools
Si intenta ejecutar una herramienta global, compruebe que la variable del entorno PATH de su máquina
contiene la ruta de acceso donde instaló la herramienta global y que el archivo ejecutable está en esa ruta
de acceso.
La primera vez que se usa, la CLI de .NET Core intenta agregar la ubicación predeterminada a la variable de
entorno PATH. Pero hay algunos escenarios en los que la ubicación podría no agregarse a PATH
automáticamente:
Si usa Linux y ha instalado el SDK de .NET Core mediante el uso de archivos .tar.gz en lugar de “apt-get”
o “rpm”.
Si usa macOS 10.15 “Catalina” o versiones posteriores.
Si usa macOS 10.14 “Mojave” o versiones anteriores y ha instalado el SDK de .NET Core mediante el uso
de archivos .tar.gz en lugar de .pkg.
Si ha instalado el SDK de .NET Core 3.0 y ha establecido la variable de entorno
DOTNET_ADD_GLOBAL_TOOLS_TO_PATH en false .
Si ha instalado el SDK de .NET Core 2.2 o versiones anteriores y ha establecido la variable de entorno
DOTNET_SKIP_FIRST_TIME_EXPERIENCE en true .
En estos casos, o si especificó la opción --tool-path , la variable de entorno PATH del equipo no contiene
automáticamente la ruta de acceso donde se instaló la herramienta global. En tal caso, anexe la ubicación
de la herramienta (por ejemplo, $HOME/.dotnet/tools ) a la variable de entorno PATH usando el método
que el shell proporcione para actualizar variables de entorno. Para obtener más información, vea
Herramientas de .NET Core.
Herramientas locales
Si intenta ejecutar una herramienta local, compruebe que hay un archivo de manifiesto denominado
dotnet-tools.json en el directorio actual o en cualquiera de sus directorios principales. Este archivo también
puede encontrarse en una carpeta denominada .config en cualquier lugar de la jerarquía de carpetas del
proyecto, en lugar de en la carpeta raíz. Si dotnet-tools.json existe, ábralo y busque la herramienta que
intenta ejecutar. Si el archivo no contiene una entrada para "isRoot": true , busque más arriba en la
jerarquía de archivos otros archivos de manifiesto de la herramienta.
Si intenta ejecutar una herramienta de .NET Core que se instaló con una ruta de acceso especificada, debe
incluir dicha ruta de acceso al usar la herramienta. Un ejemplo de uso de una herramienta instalada en una
ruta de acceso de herramientas es:
..\<toolDirectory>\dotnet-<toolName>
No se encontró el runtime
Las herramientas de .NET Core son aplicaciones dependientes del marco, lo que significa que se basan en un
runtime de .NET Core instalado en el equipo. Si no se encuentra el runtime esperado, se siguen las reglas
normales de puesta al día de runtime de .NET Core:
Una aplicación avanza a la versión de revisión más alta de las versiones principal y secundaria especificadas.
Si no hay ningún runtime que coincida con un número de versión principal y secundaria, se usa la siguiente
versión secundaria más alta.
Esta puesta al día no se produce entre versiones preliminares del runtime ni entre versiones preliminares y
versiones de lanzamiento. Por lo tanto, las herramientas de .NET Core creadas con versiones preliminares
deben ser recompiladas, publicadas nuevamente y reinstaladas por el autor.
La puesta al día no se producirá de forma predeterminada en dos escenarios comunes:
Solo están disponibles las versiones anteriores del runtime. La puesta al día solo selecciona versiones
posteriores del runtime.
Solo están disponibles las versiones posteriores principales del runtime. La puesta al día no traspasa los límites
de la versión principal.
Si una aplicación no encuentra el runtime apropiado, no se puede ejecutar y notifica un error.
Puede averiguar qué runtimes de .NET Core están instalados en su máquina con uno de los comandos siguientes:
dotnet --list-runtimes
dotnet --info
Si cree que la herramienta debería ser compatible con la versión de runtime que tiene instalada actualmente,
puede ponerse en contacto con el autor de la herramienta para ver si puede actualizar el número de versión o la
compatibilidad con varios destinos. Una vez que se vuelva a compilar y a publicar el paquete de herramientas en
NuGet con un número de versión actualizado, podrá actualizar su copia. Mientras tanto, la solución más rápida es
instalar una versión del runtime que funcione con la herramienta que intenta ejecutar. Para descargar una versión
específica del runtime de .NET Core, visite la página de descarga de .NET Core.
Si instala el SDK de .NET Core en una ubicación que no es la predeterminada, debe establecer la variable de
entorno DOTNET_ROOT en el directorio que contiene el archivo ejecutable dotnet .
Tool '{0}' failed to install. This failure may have been caused by:
* You are attempting to install a preview release and did not use the --version option to specify the
version.
* A package by this name was found, but it was not a .NET Core tool.
* The required NuGet feed cannot be accessed, perhaps because of an Internet connection problem.
* You mistyped the name of the tool.
Para ayudar a diagnosticar estos errores, los mensajes de NuGet se muestran directamente al usuario, junto con el
mensaje anterior. El mensaje de NuGet puede ayudarle a identificar el problema.
Cumplimiento de la nomenclatura de los paquetes
Microsoft ha cambiado sus instrucciones sobre los identificadores de paquetes para las herramientas, lo que ha
dado lugar a que no se encuentren diversas herramientas con el nombre previsto. Según las nuevas instrucciones,
todas las herramientas de Microsoft deben tener el prefijo “Microsoft”. Este prefijo está reservado y solo se puede
usar para los paquetes firmados con un certificado autorizado de Microsoft.
Durante la transición, algunas herramientas de Microsoft tendrán el formato anterior del identificador de paquete,
mientras que otras tendrán el nuevo formato:
A medida que se actualicen los identificadores de paquetes, deberá usar el nuevo identificador de paquete para
obtener las actualizaciones más recientes. Los paquetes con el nombre de herramienta simplificado estarán en
desuso.
Versiones preliminares
Intenta instalar una versión preliminar y no ha usado la opción --version para especificar la versión.
Las herramientas de .NET Core que se encuentran en versión preliminar deben especificarse con una parte del
nombre para indicar que están en versión preliminar. No es necesario incluir todo el nombre de la versión
preliminar. Si los números de versión tienen el formato esperado, puede usar algo parecido al ejemplo siguiente:
dotnet tool install -g --version 1.1.0-pre <toolName>
Vea también
Herramientas de .NET Core
Tutorial: Creación de una herramienta de .NET Core
mediante la CLI de .NET Core
16/09/2020 • 7 minutes to read • Edit Online
Requisitos previos
SDK 3.1 de NET Core o una versión posterior.
Este tutorial y el siguiente tutorial para las herramientas globales se aplican al SDK de .NET Core 2.1 y
versiones posteriores, porque las herramientas globales están disponibles a partir de esa versión. Pero en
este tutorial se da por supuesto que tiene instalada la versión 3.1 o posterior para que tenga la opción de
continuar con el tutorial de herramientas locales. Las herramientas locales están disponibles a partir del
SDK de .NET Core 3.0. Los procedimientos para crear una herramienta son los mismos tanto si se usan
como una herramienta global o como una herramienta local.
Un editor de texto o un editor de código de su elección.
Crear un proyecto
1. Abra un símbolo del sistema y cree una carpeta denominada repositorio.
2. Desplácese hasta la carpeta repository y escriba el comando siguiente:
cd microsoft.botsay
Agregar el código
1. Abra el archivo Program.cs con el editor de código.
2. Agregue la siguiente directiva using al principio del archivo:
using System.Reflection;
3. Reemplace el método Main con el siguiente código para procesar los argumentos de la línea de comandos
para la aplicación.
Console.WriteLine($"botsay v{versionString}");
Console.WriteLine("-------------");
Console.WriteLine("\nUsage:");
Console.WriteLine(" botsay <message>");
return;
}
Si no se pasó ningún argumento, se muestra un mensaje de ayuda breve. De lo contrario, todos los
argumentos se concatenan en una sola cadena y se imprimen llamando al método ShowBot que se crea en
el paso siguiente.
4. Agregue un nuevo método denominado ShowBot que toma un parámetro de cadena. El método imprime
el mensaje y una imagen de un robot usando líneas de texto.
static void ShowBot(string message)
{
string bot = $"\n {message}";
bot += @"
__________________
\
\
....
....'
....
..........
.............'..'..
................'..'.....
.......'..........'..'..'....
........'..........'..'..'.....
.'....'..'..........'..'.......'.
.'..................'... ......
. ......'......... .....
. _ __ ......
.. # ## ......
.... . .......
...... ....... ............
................ ......................
........................'................
......................'..'...... .......
.........................'..'..... .......
........ ..'.............'..'.... ..........
..'..'... ...............'....... ..........
...'...... ...... .......... ...... .......
........... ....... ........ ......
....... '...'.'. '.'.'.' ....
....... .....'.. ..'.....
.. .......... ..'........
............ ..............
............. '..............
...........'.. .'.'............
............... .'.'.............
.............'.. ..'..'...........
............... .'..............
......... ..............
.....
";
Console.WriteLine(bot);
}
Probar la aplicación
Ejecute el proyecto y observe la salida. Pruebe estas variaciones en la línea de comandos para ver resultados
diferentes:
dotnet run
dotnet run -- "Hello from the bot"
dotnet run -- Hello from the bot
Empaquetado de la herramienta
Antes de que pueda empaquetar y distribuir la aplicación como una herramienta, debe modificar el archivo de
proyecto.
1. Abra el archivo microsoft.botsay.csproj y agregue tres nuevos nodos XML al final del nodo
<PropertyGroup> :
<PackAsTool>true</PackAsTool>
<ToolCommandName>botsay</ToolCommandName>
<PackageOutputPath>./nupkg</PackageOutputPath>
<ToolCommandName> es un elemento opcional que especifica el comando que invocará a la herramienta una
vez instalada. Si no se proporciona este elemento, el nombre de comando para la herramienta es el
nombre del archivo de proyecto sin la extensión .csproj.
<PackageOutputPath> es un elemento opcional que determina dónde se generará el paquete NuGet. El
paquete NuGet es el que la CLI de .NET Core utiliza para instalar la herramienta.
El archivo del proyecto debe ser similar al siguiente ejemplo:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PackAsTool>true</PackAsTool>
<ToolCommandName>botsay</ToolCommandName>
<PackageOutputPath>./nupkg</PackageOutputPath>
</PropertyGroup>
</Project>
dotnet pack
Solucionar problemas
Si recibe un mensaje de error al seguir el tutorial, vea Solución de problemas de uso de herramientas de
.NET Core.
Pasos siguientes
En este tutorial, ha creado una aplicación de consola y la ha empaquetado como una herramienta. Para aprender a
usar la herramienta como una herramienta global, avance al siguiente tutorial.
Instalación y uso de una herramienta global
Si lo prefiere, puede omitir el tutorial de herramientas globales y pasar directamente al tutorial de herramientas
locales.
Instalación y uso de una herramienta local
Tutorial: Instalación y uso de una herramienta global
de .NET Core mediante la CLI de .NET Core
16/09/2020 • 4 minutes to read • Edit Online
Requisitos previos
Complete el primer tutorial de esta serie.
El parámetro --global indica a la CLI de .NET Core que instale los archivos binarios de la herramienta en
una ubicación predeterminada que se agrega automáticamente a la variable de entorno PATH.
El parámetro --add-source indica a la CLI de .NET Core que use temporalmente el directorio ./nupkg
como una fuente de origen adicional para los paquetes NuGet. Ha asignado un nombre único al paquete
para asegurarse de que solo se encontrará en el directorio ./nupkg, no en el sitio de Nuget.org.
En la salida se muestra el comando que se ha usado para llamar a la herramienta y la versión instalada:
You can invoke the tool using the following command: botsay
Tool 'microsoft.botsay' (version '1.0.0') was successfully installed.
2. Invoque la herramienta:
NOTE
Si se produce un error en este comando, es posible que tenga que abrir un nuevo terminal para actualizar el valor
de PATH.
En Linux o macOS:
El parámetro --tool-path indica a la CLI de .NET Core que instale los archivos binarios de la herramienta
en la ubicación especificada. Si el directorio no existe, se crea. Este directorio no se agrega
automáticamente a la variable de entorno PATH.
En la salida se muestra el comando que se ha usado para llamar a la herramienta y la versión instalada:
You can invoke the tool using the following command: botsay
Tool 'microsoft.botsay' (version '1.0.0') was successfully installed.
2. Invoque la herramienta:
En Windows:
En Linux o macOS:
En Linux o macOS:
Solucionar problemas
Si recibe un mensaje de error al seguir el tutorial, vea Solución de problemas de uso de herramientas de
.NET Core.
Pasos siguientes
En este tutorial, ha instalado y usado una herramienta como una herramienta global. Para instalar y usar la
misma herramienta como una herramienta local, vaya al siguiente tutorial.
Instalación y uso de herramientas locales
Tutorial: Instalación y uso de una herramienta local
de .NET Core mediante la CLI de .NET Core
16/09/2020 • 7 minutes to read • Edit Online
Requisitos previos
Complete el primer tutorial de esta serie.
Instale el entorno de ejecución de .NET Core 2.1.
En este tutorial, instalará y usará una herramienta que tiene como destino .NET Core 2.1, por lo que debe
tener ese entorno de ejecución instalado en el equipo. Para instalar la versión 2.1 del entorno de
ejecución, vaya a la página de descarga de .NET Core 2.1 y busque el vínculo de instalación del entorno
de ejecución en la columna Ejecutar aplicaciones: entorno de ejecución .
cd ..
The template "Dotnet local tool manifest file" was created successfully.
{
"version": 1,
"isRoot": true,
"tools": {}
}
Este comando agrega la herramienta al archivo de manifiesto que ha creado en el paso anterior. En la salida del
comando se muestra el archivo de manifiesto en el que se encuentra la herramienta que acaba de instalar:
You can invoke the tool from this directory using the following command:
'dotnet tool run botsay' or 'dotnet botsay'
Tool 'microsoft.botsay' (version '1.0.0') was successfully installed.
Entry is added to the manifest file /home/name/repository/.config/dotnet-tools.json
{
"version": 1,
"isRoot": true,
"tools": {
"microsoft.botsay": {
"version": "1.0.0",
"commands": [
"botsay"
]
}
}
}
Usar la herramienta
Para invocar la herramienta, ejecute el comando dotnet tool run desde la carpeta repository:
Tool 'dotnetsay' was successfully updated from version '2.1.3' to version '2.1.4'
(manifest file /home/name/repository/.config/dotnet-tools.json).
El comando update busca el primer archivo de manifiesto que contiene el identificador del paquete y lo
actualiza. Si no hay ningún identificador del paquete en ningún archivo de manifiesto que esté en el ámbito de
la búsqueda, el SDK agrega una nueva entrada al archivo de manifiesto más cercano. El ámbito de búsqueda va
hacia los directorios principales hasta que se encuentra un archivo de manifiesto con isRoot = true .
Solucionar problemas
Si recibe un mensaje de error al seguir el tutorial, consulte Solución de problemas de uso de herramientas de
.NET Core.
Vea también
Para obtener más información, vea Herramientas de .NET Core.
Información general de global.json
16/09/2020 • 20 minutes to read • Edit Online
Esquema de global.JSON
sdk
Tipo: object
VA LO R C O M P O RTA M IEN TO
latestMajor Usa el SDK de .NET Core instalado superior con una versión
principal que es mayor o igual que el valor especificado.
Si no se encuentra, se produce un error.
msbuild-sdks
Tipo: object
Permite controlar la versión del SDK del proyecto en un lugar, en vez de hacerlo en cada proyecto individual. Para
más información, vea Resolución de los SDK de proyecto.
Ejemplos
En el ejemplo siguiente se muestra cómo no usar versiones preliminares:
{
"sdk": {
"allowPrerelease": false
}
}
En este ejemplo se muestra cómo usar la versión superior instalada que es mayor o igual que la versión
especificada. El JSON mostrado no permite ninguna versión del SDK anterior a 2.2.200 y permite 2.2.200 o
cualquier versión posterior, incluidos 3.0.xxx y 3.1.xxx.
{
"sdk": {
"version": "2.2.200",
"rollForward": "latestMajor"
}
}
{
"sdk": {
"version": "3.1.100",
"rollForward": "disable"
}
}
En este ejemplo se muestra cómo usar la versión de revisión y banda de características más reciente instalada de
una versión principal y secundaria concreta. El JSON mostrado no permite ninguna versión del SDK anterior a
3.1.102 y permite 3.1.102 o cualquier versión 3.1.xxx posterior, como 3.1.103 o 3.1.200.
{
"sdk": {
"version": "3.1.102",
"rollForward": "latestFeature"
}
}
En este ejemplo se muestra cómo usar la versión de revisión superior instalada de una versión específica. El JSON
mostrado no permite ninguna versión del SDK anterior a 3.1.102 y permite 3.1.102 o cualquier versión 3.1.1xx
posterior, como 3.1.103 o 3.1.199.
{
"sdk": {
"version": "3.1.102",
"rollForward": "latestPatch"
}
}
Trabaja con una versión preliminar del SDK de .NET Core. Puede definir la versión del SDK a través de
un archivo global.json en el proyecto actual. Más información en https://go.microsoft.com/fwlink/?
linkid=869452.
Las versiones del SDK de .NET Core tienen un historial y el compromiso de ser de alta calidad. Pero si no
quiere usar una versión preliminar, vea las distintas estrategias que puede aplicar con el SDK de .NET Core
3.0 o una versión posterior en la sección allowPrerelease. En el caso de los equipos que nunca han tenido
instalado un SDK o un runtime de .NET Core 3.0 o superior, debe crear un archivo global.json y especificar
la versión exacta que quiere usar.
La advertencia siguiente indica que el proyecto tiene como destino EF Core 1.0 ó 1.1, que no es compatible
con el SDK de .NET Core 2.1 y versiones posteriores:
A partir del SDK de .NET Core 2.1 (versión 2.1.300), el comando dotnet ef se incluye en el SDK. Para
compilar el proyecto, instale el SDK de .NET Core 2.0 (versión 2.1.201) o versiones anteriores en el equipo
y defina la versión del SDK deseada mediante el archivo global.json. Para obtener más información sobre
el comando dotnet ef , vea EF Core .NET Command-line Tools (Herramientas de línea de comandos de EF
Core .NET).
Vea también
Resolución de los SDK de proyecto
Telemetría del SDK de .NET Core
16/09/2020 • 10 minutes to read • Edit Online
El SDK de .NET Core incluye una característica de telemetría que recopila datos de uso e información de excepción
cuando se bloquea el CLI de .NET Core. El CLI de .NET Core incluye el SDK de .NET Core y es el conjunto de verbos
que permiten compilar, probar y publicar las aplicaciones de .NET Core. Es importante que el equipo de .NET
entienda cómo se usan las herramientas con el fin de mejorarlas. Con la información sobre los errores, el equipo
consigue resolver problemas y corregir errores.
Los datos recopilados son anónimos y se publican de forma agregada bajo la licencia de atribución de Creative
Commons.
Ámbito
dotnet tiene dos funciones: ejecutar aplicaciones y ejecutar comandos de la CLI. La telemetría no se recopila
cuando se usa dotnet para iniciar una aplicación con el siguiente formato:
dotnet [path-to-app].dll
La telemetría se recopila cuando se usa cualquiera de los comandos de la CLI de .NET Core, como:
dotnet build
dotnet pack
dotnet run
Divulgación
El SDK de .NET Core muestra texto similar al siguiente cuando se ejecuta por primera vez uno de los comandos de
la CLI de .NET Core (por ejemplo, dotnet build ). El texto puede variar ligeramente según la versión del SDK que
ejecute. Esta experiencia de "primera vista" es la forma en que Microsoft le notifica sobre la recopilación de datos.
Telemetry
---------
The .NET Core tools collect usage data in order to help us improve your experience. The data is anonymous. It
is collected by Microsoft and shared with the community. You can opt-out of telemetry by setting the
DOTNET_CLI_TELEMETRY_OPTOUT environment variable to '1' or 'true' using your favorite shell.
Para deshabilitar este mensaje y el mensaje de bienvenida de .NET Core, establezca la variable de entorno
DOTNET_NOLOGO en true . Tenga en cuenta que esta variable no tiene ningún efecto sobre la exclusión de la
telemetría.
Puntos de datos
La característica de telemetría no recopila datos personales, como direcciones de correo electrónico o nombres de
usuario. No examina el código ni extrae datos de nivel de proyecto, como el nombre, el repositorio o el autor. Los
datos se envían de forma segura a los servidores de Microsoft con tecnología de Azure Monitor, se conservan bajo
acceso restringido y se publican bajo controles de seguridad estrictos de sistemas seguros de Azure Storage.
La protección de su privacidad es importante para nosotros. Si sospecha que la telemetría está recopilando datos
confidenciales o que los datos se están tratando de forma no segura o inapropiada, informe de un problema en el
repositorio de dotnet/cli o envíenos un correo electrónico a dotnet@microsoft.com para que lo investiguemos.
La característica de telemetría recopila los siguientes datos:
Opciones recopiladas
Determinados comandos envían datos adicionales. Un subconjunto de comandos envía el primer argumento:
Un subconjunto de comandos envía las opciones seleccionadas, si se usan, junto con sus valores:
O P C IÓ N C O M A N DO S
A excepción de --verbosity y --sdk-package-version , se aplica un algoritmo hash a los demás valores a partir del
SDK de .NET Core 2.1.100.
System.IO.IOException
at System.ConsolePal.WindowsConsoleStream.Write(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
at System.IO.StreamWriter.Write(Char[] buffer)
at System.IO.TextWriter.WriteLine()
at System.IO.TextWriter.SyncTextWriter.WriteLine()
at Microsoft.DotNet.Cli.Utils.Reporter.WriteLine()
at Microsoft.DotNet.Tools.Run.RunCommand.EnsureProjectIsBuilt()
at Microsoft.DotNet.Tools.Run.RunCommand.Execute()
at Microsoft.DotNet.Tools.Run.RunCommand.Run(String[] args)
at Microsoft.DotNet.Cli.Program.ProcessArgs(String[] args, ITelemetry telemetryClient)
at Microsoft.DotNet.Cli.Program.Main(String[] args)
Vea también
Datos de telemetría de la CLI de .NET Core
Fuente de referencia de telemetría (repositorio dotnet/sdk)
Acceso con privilegios elevados para comandos de
dotnet
13/04/2020 • 8 minutes to read • Edit Online
Los procedimientos recomendados de desarrollo de software sirven de guía a los desarrolladores para escribir
software que requiera la menor cantidad de privilegios. Sin embargo, algunos programas, como las herramientas
de supervisión de rendimiento, requieren permiso del administrador debido a las reglas del sistema operativo. En
las siguientes instrucciones se describen los escenarios admitidos para escribir dicho software con .NET Core.
Los siguientes comandos se pueden ejecutar con privilegios elevados:
Comandos dotnet tool , como dotnet tool install.
dotnet run --no-build
dotnet-core-uninstall
No se recomienda ejecutar otros comandos con privilegios elevados. En concreto, no se recomienda usar
privilegios elevados con los comandos que usa MSBuild, como dotnet restore, dotnet build y dotnet run. Los
problemas de administración de permisos son el principal problema cuando un usuario cambia varias veces entre
una cuenta restringida y otra raíz después de emitir comandos de dotnet. Como usuario restringido, es posible que
no tenga acceso al archivo generado por un usuario raíz. Hay maneras de resolver esta situación, pero, para
empezar, no es necesario que surjan.
Puede ejecutar comandos como raíz siempre y cuando no cambie repetidas veces entre una cuenta restringida y
otra raíz. Por ejemplo, los contenedores de Docker se ejecutan como raíz de forma predeterminada, por lo que
tienen esta característica.
"%ProgramFiles%\dotnet-tools\TOOLCOMMAND"
Opción 2 Agregue la carpeta recién creada a %Path% . Solo necesita realizar esta operación una vez.
Y ejecute con:
TOOLCOMMAND
Herramientas locales
Las herramientas locales se limitan al árbol de subdirectorio, por usuario. Cuando se ejecutan con privilegios
elevados, las herramientas locales comparten un entorno de usuario con privilegios restringidos en el entorno con
privilegios elevados. En Linux y macOS, esto da como resultado archivos que establecen con acceso de solo usuario
raíz. Si el usuario cambia a una cuenta restringida, el usuario ya no podrá acceder a los archivos ni escribir en ellos.
Por tanto, no se recomienda instalar las herramientas que necesiten permisos elevados como herramientas locales.
En su lugar, use la opción --tool-path y las instrucciones anteriores para las herramientas globales.
dotnet build
sudo ./bin/Debug/netcoreapp3.0/APPLICATIONNAME
Mediante el comando dotnet run con la marca —no-build para evitar que se generen nuevos archivos
binarios:
dotnet build
sudo dotnet run --no-build
Vea también
Información general sobre las herramientas globales de .NET Core
Cómo habilitar la finalización con tabulación para la
CLI de .NET Core
16/09/2020 • 4 minutes to read • Edit Online
Si ese comando no funciona, asegúrese de que está instalado el SDK de .NET Core 2.0 o una versión superior. Si
está instalado, pero el comando sigue sin funcionar, asegúrese de que dotnet se resuelva como mínimo en la
versión 2.0 del SDK de .NET Core. Use el comando dotnet --version para ver en qué versión de dotnet se
resuelve la ruta de acceso actual. Para obtener más información, vea Selección de la versión de .NET Core que se va
a usar.
Ejemplos
Estos son algunos ejemplos de lo que proporciona la finalización con tabulación:
EN T RA DA SE C O N VIERT E EN P O RQ UE
dotnet add p ⇥⇥ dotnet add package Al presionar la tecla Tab una segunda
vez aparece la siguiente sugerencia.
dotnet add package Microsoft ⇥ dotnet add package Los resultados se devuelven por orden
Microsoft.ApplicationInsights.Web alfabético.
PowerShell
Para agregar finalización con tabulación a PowerShell para la CLI de .NET Core, cree o edite el perfil almacenado
en la variable $PROFILE . Para obtener más información, vea Cómo crear el perfil y Los perfiles y la directiva de
ejecución.
Agregue el código siguiente al perfil:
Bash
Para agregar finalización con tabulación al shell de bash para la CLI de .NET Core, agregue el código siguiente al
archivo .bashrc :
_dotnet_bash_complete()
{
local word=${COMP_WORDS[COMP_CWORD]}
local completions
completions="$(dotnet complete --position "${COMP_POINT}" "${COMP_LINE}" 2>/dev/null)"
if [ $? -ne 0 ]; then
completions=""
fi
zsh
Para agregar finalización con tabulación al shell de zsh para la CLI de .NET Core, agregue el código siguiente al
archivo .zshrc :
_dotnet_zsh_complete()
{
local completions=("$(dotnet complete "$words")")
reply=( "${(ps:\n:)completions}" )
}
En este documento se describe el uso del SDK de .NET Core y sus herramientas en un servidor de compilación. El
conjunto de herramientas de .NET Core funciona tanto de forma interactiva, donde un desarrollador escribe
comandos en un símbolo del sistema, como de manera automática, donde un servidor de integración continua (CI)
ejecuta un script de compilación. Los comandos, las opciones, las entradas y las salidas son los mismos, y solo lo
que el usuario especifica sirve para adquirir las herramientas y un sistema para compilar la aplicación. Este
documento se centra en escenarios de adquisición de herramientas de integración continua, donde además se
ofrecen recomendaciones sobre cómo diseñar y estructurar los scripts de compilación.
NOTE
Azure DevOps Ser vices
Cuando se utiliza el script del instalador, las dependencias nativas no se instalan automáticamente. Debe instalarlas en caso
de el sistema operativo no las incluya. Para obtener más información, vea Dependencias y requisitos de .NET Core.
Ejemplos de configuración de CI
En esta sección se explica un procedimiento de instalación manual con un script de PowerShell o de Bash, además
de incluir una descripción de varias soluciones de CI de software como servicio (SaaS). Las soluciones de CI de
SaaS tratadas son Travis CI, AppVeyor y Azure Pipelines.
Instalación manual
Cada servicio de SaaS tiene sus propios métodos para crear y configurar un proceso de compilación. Si usa una
solución de SaaS distinta a las que indican o necesita realizar alguna personalización aparte de la compatibilidad
preconfigurada, debe realizar al menos alguna configuración manual.
En general, para realizar una instalación manual, es necesario que adquiera una versión de las herramientas o las
últimas compilaciones nocturnas de las herramientas y que, después, ejecute el script de compilación. Puede usar
un script de PowerShell o de Bash para orquestar los comandos de .NET Core o utilizar un archivo del proyecto en
el que se describa el proceso de compilación. En la sección de orquestación se ofrece más información sobre estas
opciones.
Después de crear un script que realiza una instalación manual del servidor de compilación de CI, úselo en el
equipo de desarrollo para compilar el código de forma local con fines de pruebas. Cuando confirme que el script
se ejecuta de forma correcta en el entorno local, impleméntelo en el servidor de compilación de CI. Un script de
PowerShell relativamente sencillo demuestra cómo obtener el SDK de NET Core e instalarlo en un servidor de
compilación de Windows:
$ErrorActionPreference="Stop"
$ProgressPreference="SilentlyContinue"
# Run the build process now. Implement your build script here.
Debe proporcionar la implementación para el proceso de compilación al final del script. El script adquiere las
herramientas y, después, ejecuta el proceso de compilación. En los equipos UNIX, el siguiente script de Bash realiza
las acciones descritas en el script de PowerShell de forma similar:
#!/bin/bash
INSTALLDIR="cli-tools"
CLI_VERSION=1.0.1
DOWNLOADER=$(which curl)
if [ -d "$INSTALLDIR" ]
then
rm -rf "$INSTALLDIR"
fi
mkdir -p "$INSTALLDIR"
echo Downloading the CLI installer.
$DOWNLOADER https://dot.net/v1/dotnet-install.sh > "$INSTALLDIR/dotnet-install.sh"
chmod +x "$INSTALLDIR/dotnet-install.sh"
echo Installing the CLI requested version $CLI_VERSION. Please wait, installation may take a few minutes.
"$INSTALLDIR/dotnet-install.sh" --install-dir "$INSTALLDIR" --version $CLI_VERSION
if [ $? -ne 0 ]
then
echo Download of $CLI_VERSION version of the CLI failed. Exiting now.
exit 0
fi
echo The CLI has been installed.
LOCALDOTNET="$INSTALLDIR/dotnet"
# Run the build process now. Implement your build script here.
Travis CI
Puede configurar Travis CI para instalar el SDK de .NET Core con el lenguaje csharp y la clave dotnet . Para más
información, consulte los documentos oficiales de Travis CI en Building a C#, F#, or Visual Basic Project
(Compilación de un proyecto de C#, F# o Visual Basic). Al acceder a la información de Travis CI, observará que el
identificador de lenguaje language: csharp de cuyo mantenimiento se encarga la comunidad funciona con todos
los lenguajes de .NET, incluidos F# y Mono.
Travis CI se ejecuta tanto en trabajos de macOS como de Linux en una matriz de compilación, donde debe
especificar una combinación de runtime, entorno y exclusiones/inclusiones para aceptar las combinaciones de
compilación de la aplicación. Para más información, vea el artículo Customizing the Build (Personalización de la
compilación) en la documentación de Travis CI. Las herramientas basadas en MSBuild incluyen los runtimes LTS
(1.0.x) y Current (1.1.x) en el paquete; por tanto, cuando instala el SDK, recibe todo lo que necesita para la
compilación.
AppVeyor
AppVeyor instala el SDK de .NET Core 1.0.1 con la imagen de trabajo de compilación de Visual Studio 2017 . Hay
disponibles otras imágenes de compilación con distintas versiones del SDK de .NET Core. Para más información,
consulte el ejemplo appveyor.yml y el artículo Build worker images (Imágenes de trabajo de compilación) en los
documentos de AppVeyor.
Los archivos binarios del SDK de .NET Core se descargan y descomprimen en un subdirectorio con la utilización
del script de instalación y, después, se agregan a la variable de entorno PATH . Agregue una matriz de compilación
para ejecutar las pruebas de integración con varias versiones del SDK de .NET Core:
environment:
matrix:
- CLI_VERSION: 1.0.1
- CLI_VERSION: Latest
install:
# See appveyor.yml example for install script
Orquestación de la compilación
En la mayor parte de este documento se describe cómo adquirir las herramientas de .NET Core y configurar varios
servicios de CI sin ofrecer información sobre cómo orquestar o compilar realmente el código con .NET Core. Las
opciones para estructurar el proceso de compilación dependen de muchos factores que no se pueden tratar aquí
en términos generales. Para más información sobre cómo orquestar las compilaciones con cada tecnología,
explore los recursos y los ejemplos que se proporciona en los conjuntos de documentación de Travis CI, AppVeyor
y Azure Pipelines.
Dos enfoques generales que se aplican para estructurar el proceso de compilación del código de .NET Core con
herramientas de .NET Core consisten en utilizar directamente MSBuild o en usar los comandos de la línea de
comandos de .NET Core. El enfoque que debe adoptar depende de lo cómo que se sienta con cada uno de ellos y
de los inconvenientes que presente su complejidad. MSBuild ofrece la posibilidad de expresar el proceso de
compilación como tareas y objetivos, pero presenta la complejidad añadida de tener que aprender la sintaxis del
archivo de proyecto de MSBuild. Quizá sea más sencillo usar las herramientas de línea de comandos de .NET Core,
pero, en este caso, es necesario escribir la lógica de orquestación en un lenguaje de scripting como bash o
PowerShell.
Vea también
Descargas de .NET: Linux
Desarrollo de bibliotecas con la CLI de .NET Core
16/09/2020 • 22 minutes to read • Edit Online
En este artículo se explica cómo escribir bibliotecas para .NET con la CLI de .NET Core. La CLI proporciona una
experiencia eficaz y de bajo nivel que funciona en todos los SO compatibles. De todos modos puede seguir
compilando bibliotecas con Visual Studio; si esa es la experiencia de su preferencia, consulte la guía sobre Visual
Studio.
Requisitos previos
Necesita la CLI y el SDK de .NET Core que están instalados en la máquina.
En las secciones de este documento que se refieren a las versiones de .NET Framework, necesita tener instalado
.NET Framework en una máquina con Windows.
Además, si desea admitir destinos de .NET Framework anteriores, deberá instalar paquetes de destino o
desarrollador desde la página de archivos de descarga de .NET. Consulte la tabla siguiente:
2.0, 3.0 y 3.5 Entorno de ejecución de .NET Framework 3.5 SP1 (o una
versión posterior a Windows 8)
. N ET
STA N DA
RD 1. 0 1. 1 1. 2 1. 3 1. 4 1. 5 1. 6 2. 0 2. 1
.NET 1.0 1.0 1.0 1.0 1.0 1.0 1.0 2.0 3.0
Core
.NET 4.5 4.5 4.5.1 4.6 4.6.1 4.6.1 2 4.6.1 2 4.6.1 2 N/A3
Framew
ork 1
. N ET
STA N DA
RD 1. 0 1. 1 1. 2 1. 3 1. 4 1. 5 1. 6 2. 0 2. 1
Mono 4.6 4.6 4.6 4.6 4.6 4.6 4.6 5.4 6.4
Xamarin 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.14 12.16
.iOS
Xamarin 3.0 3.0 3.0 3.0 3.0 3.0 3.0 3.8 5.16
.Mac
Xamarin 7.0 7.0 7.0 7.0 7.0 7.0 7.0 8.0 10.0
.Android
Platafor 10.0 10.0 10.0 10.0 10.0 10.0.16 10.0.16 10.0.16 TBD
ma 299 299 299
universa
l de
Window
s
Unity 2018.1 2018.1 2018.1 2018.1 2018.1 2018.1 2018.1 2018.1 TBD
1 Las versiones que se muestran de .NET Framework se aplican al SDK de .NET Core 2.0 y versiones posteriores de la herramienta. Las versiones
anteriores usaban una asignación diferente para .NET Standard 1.5 y versiones posteriores. Puede descargar herramientas para .NET Core para Visual
Studio 2015 si no se puede actualizar a Visual Studio 2017 ni a una versión posterior.
2 Las versiones siguientes representan las reglas que usa NuGet para determinar si una determinada biblioteca de .NET Standard es aplicable. Aunque
NuGet considera a .NET Framework 4.6.1 compatible con .NET Standard (versiones 1.5 a 2.0) hay varios problemas con el consumo de bibliotecas de
.NET Standard que se compilaron para esas versiones desde proyectos de .NET Framework 4.6.1. Para los proyectos de .NET Framework que tengan
que usar estas bibliotecas, se recomienda actualizar el proyecto para destinarlo a .NET Framework 4.7.2 o una versión posterior.
3 .NET Framework no admitirá .NET Standard 2.1 o versiones posteriores. Para más detalles, vea el anuncio de .NET Standard 2.1.
Las columnas representan las versiones de .NET Standard. Cada celda de encabezado es un vínculo a un
documento que muestra qué API se han agregado en esa versión de .NET Standard.
Las filas representan las diferentes implementaciones de .NET.
El número de versión de cada celda indica la versión mínima de la implementación que necesitará para tener
como destino dicha versión de .NET Standard.
Para ver una tabla interactiva, consulte Versiones de .NET Standard.
Este es el significado de la tabla para crear una biblioteca:
La versión de .NET Standard que elija será un equilibrio entre el acceso a las API más recientes y la capacidad de
apuntar a más implementaciones .NET y más versiones de .NET Standard. Puede controlar el intervalo de
versiones y plataformas de destino si elige una versión de netstandardX.X (donde X.X es un número de
versión) y la agrega al archivo del proyecto ( .csproj o .fsproj ).
En función de sus necesidades, tiene tres opciones principales cuando el destino es .NET Standard.
1. Puede usar la versión predeterminada de .NET Standard que se proporciona con plantillas, netstandard1.4
, que le permite acceder a la mayoría de API en .NET Standard mientras sigue siendo compatible con UWP,
.NET Framework 4.6.1 y .NET Standard 2.0.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
</PropertyGroup>
</Project>
2. Puede usar una versión anterior o posterior de .NET Standard modificando el valor en el nodo
TargetFramework de su archivo del proyecto.
Las versiones del estándar .NET son compatibles con versiones anteriores. Eso significa que las bibliotecas
netstandard1.0 se pueden ejecutar en plataformas netstandard1.1 y versiones superiores. Sin embargo,
no hay compatibilidad con versiones posteriores. Las plataformas de .NET Standard anteriores no pueden
hacer referencia a las posteriores. Esto significa que las bibliotecas netstandard1.0 no pueden hacer
referencia a las bibliotecas que tienen como destino netstandard1.1 o una versión superior. Seleccione la
versión estándar que tiene la combinación adecuada de compatibilidad con API y plataformas que
necesita. Por ahora le recomendamos netstandard1.4 .
3. Si desea tener como destino .NET Framework versión 4.0 o inferior, o bien desea usar una API disponible
en .NET Framework pero no disponible en el estándar .NET (por ejemplo, System.Drawing ), lea las
secciones siguientes y sepa cómo tener compatibilidad con múltiples versiones.
Tenga en cuenta que algunas de las versiones de .NET Framework que se usan aquí ya no cuentan con soporte
técnico. Consulte las P+F sobre la directiva del ciclo de vida de soporte técnico de .NET Framework sobre las
versiones sin soporte técnico.
Si quiere llegar a la mayor cantidad posible de desarrolladores y proyectos, use .NET Framework 4.0 como el
destino de línea base. Para tener .NET Framework como destino, comience utilizando el moniker de la plataforma
de destino (TFM) correcto que corresponda a la versión de .NET Framework que desea admitir.
VERSIÓ N DE . N ET F RA M EW O RK T FM
Después, inserte el TFM en la sección TargetFramework de su archivo del proyecto. El siguiente es un ejemplo de
cómo podría escribir una biblioteca que tenga como destino .NET Framework 4.0:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net40</TargetFramework>
</PropertyGroup>
</Project>
Y listo. Aunque esto solo hace la compilación para .NET Framework 4, puede usar la biblioteca en las versiones
más recientes de .NET Framework.
Es posible que deba tener como destino versiones anteriores de .NET Framework cuando el proyecto admite .NET
Framework y .NET Core. En este escenario, si desea usar API más recientes y construcciones de lenguaje para los
destinos más recientes, use las directivas #if en el código. También es posible que tenga que agregar distintos
paquetes y dependencias para cada plataforma que tiene como destino para incluir las distintas API necesarias
para cada caso.
Por ejemplo, digamos que tiene una biblioteca que realiza operaciones de red a través de HTTP. En el estándar
.NET y .NET Framework versión 4.5 o superiores, puede usar la clase HttpClient del espacio de nombres
System.Net.Http . Sin embargo, las versiones anteriores de .NET Framework no tienen la clase HttpClient , por lo
que, en su lugar, podría usar la clase WebClient del espacio de nombres System.Net para esas versiones.
Su archivo del proyecto podría tener la siguiente apariencia:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.4;net40;net45</TargetFrameworks>
</PropertyGroup>
<!-- Need to conditionally bring in references for the .NET Framework 4.0 target -->
<ItemGroup Condition="'$(TargetFramework)' == 'net40'">
<Reference Include="System.Net" />
</ItemGroup>
<!-- Need to conditionally bring in references for the .NET Framework 4.5 target -->
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<Reference Include="System.Net.Http" />
<Reference Include="System.Threading.Tasks" />
</ItemGroup>
</Project>
El sistema de compilación conoce los siguientes símbolos del preprocesador que se usan en las directivas #if :
namespace MultitargetLib
{
public class Library
{
#if NET40
private readonly WebClient _client = new WebClient();
private readonly object _locker = new object();
#else
private readonly HttpClient _client = new HttpClient();
#endif
#if NET40
// .NET Framework 4.0 does not have async/await
public string GetDotNetCount()
{
string url = "https://www.dotnetfoundation.org/";
Si crea este proyecto con dotnet build , observará tres directorios en la carpeta bin/ :
net40/
net45/
netstandard1.4/
Cada uno de ellos contiene los archivos .dll para cada destino.
NOTE
Esto usa algunos comandos de la CLI de .NET Core. Vea dotnet new y dotnet sln para obtener más información.
mkdir SolutionWithSrcAndTest
cd SolutionWithSrcAndTest
dotnet new sln
dotnet new classlib -o MyProject
dotnet new xunit -o MyProject.Test
dotnet sln add MyProject/MyProject.csproj
dotnet sln add MyProject.Test/MyProject.Test.csproj
/SolutionWithSrcAndTest
|__SolutionWithSrcAndTest.sln
|__MyProject/
|__MyProject.Test/
2. Vaya al directorio del proyecto de prueba y agregue una referencia a MyProject.Test desde MyProject .
cd MyProject.Test
dotnet add reference ../MyProject/MyProject.csproj
dotnet restore
dotnet build
No es necesario ejecutar dotnet restore porque lo ejecutan implícitamente todos los comandos que
necesitan que se produzca una restauración, como dotnet new , dotnet build , dotnet run , dotnet test ,
dotnet publish y dotnet pack . Para deshabilitar la restauración implícita, use la opción --no-restore .
El comando dotnet restore sigue siendo válido en algunos escenarios donde tiene sentido realizar una
restauración explícita, como las compilaciones de integración continua en Azure DevOps Services o en los
sistemas de compilación que necesitan controlar explícitamente cuándo se produce la restauración.
Para obtener información sobre cómo administrar fuentes de NuGet, vea la documentación de
dotnet restore .
4. Compruebe que xUnit se ejecuta mediante la ejecución del comando dotnet test . Si decide usar MSTest,
entonces debe ejecutarse en su lugar el ejecutor de la consola de MSTest.
Y listo. Ahora puede probar la biblioteca en todas las plataformas; para ello, use herramientas de línea de
comandos. Para seguir con las pruebas ahora que ya está todo configurado, probar la biblioteca es un proceso
muy simple:
1. Haga los cambios en la biblioteca.
2. Ejecute las pruebas desde la línea de comandos, en el directorio de prueba, con el comando dotnet test .
using AwesomeLibrary.CSharp;
open AwesomeLibrary.FSharp
Escenarios de consumo similares a este significan que las API a las que se tiene acceso deben tener una
estructura distinta para C# y para F#. Un enfoque común para lograrlo es factorizar toda la lógica de una
biblioteca en un proyecto central, con los proyectos de C# y F# definiendo los niveles de API que hacen llamadas
a ese proyecto central. En el resto de la sección se usarán los siguientes nombres:
AwesomeLibrar y.Core : un proyecto central que contiene toda la lógica de la biblioteca
AwesomeLibrar y.CSharp : un proyecto con API públicas pensado para el consumo en C#
AwesomeLibrar y.FSharp : un proyecto con API públicas pensado para el consumo en F#
Puede ejecutar los siguientes comandos en su terminal para generar la misma estructura de esta guía:
Los archivos del proyecto para AwesomeLibrar y.CSharp y AwesomeLibrar y.FSharp ahora harán referencia
a AwesomeLibrar y.Core como un destino ProjectReference . Puede comprobar esto inspeccionando los
archivos del proyecto y observando lo siguiente en ellos:
<ItemGroup>
<ProjectReference Include="..\AwesomeLibrary.Core\AwesomeLibrary.Core.csproj" />
</ItemGroup>
Puede agregar esta sección a cada archivo del proyecto manualmente si prefiere no usar la CLI de .NET Core.
Estructura de una solución
Otro aspecto importante de las soluciones de varios proyectos es establecer una buena estructura de proyecto
general. Puede organizar el código de la manera que quiera, y siempre y cuando vincule cada proyecto a su
archivo de solución con dotnet sln add , podrá ejecutar dotnet restore y dotnet build en el nivel de solución.
Plantillas personalizadas para dotnet new
16/09/2020 • 18 minutes to read • Edit Online
El SDK de .NET Core cuenta con muchas plantillas ya instaladas y listas para su uso. El comando dotnet new no es
solo la forma de usar una plantilla, sino también cómo instalarlas y desinstalarlas. A partir de .NET Core 2.0, puede
crear sus propias plantillas personalizadas para cualquier tipo de proyecto, como una aplicación, un servicio, una
herramienta o una biblioteca de clases. Incluso puede crear una plantilla que genere uno o más archivos
independientes, como un archivo de configuración.
Puede instalar plantillas personalizadas desde un paquete NuGet en cualquier fuente NuGet, haciendo referencia a
un archivo .nupkg de NuGet directamente o especificando un directorio del sistema de archivos que contenga la
plantilla. El motor de plantillas ofrece características que le permiten reemplazar valores, incluir y excluir archivos
y ejecutar operaciones de procesamiento personalizadas cuando se usa la plantilla.
El motor de plantillas es de código abierto, y el repositorio de código en línea está en dotnet/templating en
GitHub. Puede encontrar más plantillas, incluidas las plantillas de terceros, en Plantillas disponibles para dotnet
new en GitHub. Para obtener información sobre cómo crear y usar plantillas personalizadas, vea Cómo crear sus
propias plantillas para dotnet new y la Wiki del repositorio de GitHub dotnet/templating.
NOTE
Los ejemplos de plantillas están disponibles en el repositorio de GitHub dotnet/dotnet-template-samples. Sin embargo,
aunque estos ejemplos son un buen recurso para aprender cómo funcionan las plantillas, el repositorio está archivado y no
recibe mantenimiento. Los ejemplos pueden no estar actualizados y ya no funcionan.
Para seguir un tutorial y crear una plantilla, vea el tutorial Creación de una plantilla personalizada para dotnet
new.
Plantillas predeterminadas .NET
Cuando instala el SDK de .NET Core, recibe más de una docena de plantillas integradas para crear proyectos y
archivos, incluidas las aplicaciones de consola, bibliotecas de clases, proyectos de prueba unitaria, aplicaciones de
ASP.NET Core (incluidos los proyectos Angular y React), y archivos de configuración. Para enumerar las plantillas
integradas, ejecute el comando dotnet new con la opción -l|--list :
Configuración
Una plantilla consta de las siguientes partes:
Archivos de origen y carpetas.
Un archivo de configuración (template.json).
Archivos de origen y carpetas
Los archivos de origen y las carpetas incluyen todos los archivos y carpetas que quiera que el motor de plantilla
use cuando se ejecuta el comando dotnet new <TEMPLATE> . El motor de plantillas está diseñado para usar
proyectos ejecutables como código fuente para generar proyectos. Esto presenta varias ventajas:
El motor de plantillas no necesita que inserte tokens especiales en su código de origen del proyecto.
Los archivos de código no son archivos especiales ni se modifican de ninguna manera para que funcionen con
el motor de plantillas. Por lo tanto, las herramientas que usa normalmente para trabajar con los proyectos
también funcionan con el contenido de la plantilla.
Compile, ejecute y depure sus proyectos de plantilla de la forma en que lo hace para cualquiera de sus otros
proyectos.
Puede crear rápidamente una plantilla de un proyecto existente simplemente agregando un archivo de
configuración ./.template.config/template.json al proyecto.
Los archivos y las carpetas que se almacenan en la plantilla no se limitan a tipos de proyectos .NET formales. Los
archivos de origen y las carpetas pueden constar de cualquier contenido que quiera crear cuando se use la
plantilla, incluso si el motor de plantillas genera solo un archivo como su salida.
Los archivos que genera la plantilla se pueden modificar según la lógica y la configuración que ha proporcionado
en el archivo de configuración template.json. El usuario puede invalidar esta configuración pasando las opciones
al comando dotnet new <TEMPLATE> . Un ejemplo común de una lógica personalizada es proporcionar un nombre
para una clase o variable en el archivo de código que se implementa mediante una plantilla.
template.json
El archivo template.json se coloca en una carpeta .template.config en el directorio raíz de la plantilla. El archivo
proporciona información de configuración al motor de plantillas. La configuración mínima necesita los miembros
que se muestran en la tabla siguiente, que es suficiente para crear una plantilla funcional.
El esquema completo del archivo template.json puede encontrarse en el Almacenamiento del esquema JSON. Para
más información sobre el archivo template.json, consulte la wiki de plantillas dotnet.
Ejemplo
Por ejemplo, esta es una carpeta de plantillas que tiene dos archivos de contenido: console.cs y readme.txt. Tenga
en cuenta que existe la carpeta requerida denominada .template.config que contiene el archivo template.json.
└───mytemplate
│ console.cs
│ readme.txt
│
└───.template.config
template.json
{
"$schema": "http://json.schemastore.org/template",
"author": "Travis Chau",
"classifications": [ "Common", "Console" ],
"identity": "AdatumCorporation.ConsoleTemplate.CSharp",
"name": "Adatum Corporation Console Application",
"shortName": "adatumconsole"
}
La carpeta mytemplate es un paquete de plantilla instalable. Una vez instalado el paquete, shortName se puede
utilizar con el comando dotnet new . Por ejemplo, dotnet new adatumconsole generaría los archivos console.cs y
readme.txt en la carpeta actual.
Un paquete de plantillas, en forma de un paquete NuGet .nupkg, requiere que todas las plantillas se almacenan en
la carpeta content dentro del paquete. Hay algunas opciones de configuración más para agregar a un archivo
.csproj para asegurarse de que el paquete .nupkg generado se puede instalar como un paquete de plantilla:
1. El valor <IncludeContentInPack> se establece en true para incluir cualquier archivo que el proyecto establece
como content en el paquete NuGet.
2. El valor <IncludeBuildOutput> se establece en false para excluir todos los archivos binarios generados por el
compilador desde el paquete NuGet.
3. El valor <ContentTargetFolders> se establece en content . Esto garantiza que los archivos establecidos como
content se almacenan en la carpeta content carpeta en el paquete NuGet. Esta carpeta del paquete NuGet la
analiza el sistema de plantillas dotnet.
Una manera sencilla de excluir todos los archivos de código para que el proyecto de su plantilla no los compile es
usando el elemento <Compile Remove="**\*" /> del archivo de proyecto, dentro de un elemento <ItemGroup> .
Una manera sencilla de estructurar su paquete de plantillas es colocar todas las plantillas en carpetas individuales
y luego cada carpeta de plantillas dentro de una carpeta templates que se encuentra en el mismo directorio que el
archivo .csproj . De esta manera, puede usar un solo elemento del proyecto para incluir todos los archivos y
carpetas en templates como content . Dentro de un elemento <ItemGroup> , cree un elemento
<Content Include="templates\**\*" Exclude="templates\**\bin\**;templates\**\obj\**" /> .
Este es un archivo .csproj de ejemplo que sigue todas las pautas anteriores. Empaqueta la carpeta secundaria
templates en la carpeta del paquete content y excluye cualquier archivo de código de la compilación.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageType>Template</PackageType>
<PackageVersion>1.0</PackageVersion>
<PackageId>AdatumCorporation.Utility.Templates</PackageId>
<Title>AdatumCorporation Templates</Title>
<Authors>Me</Authors>
<Description>Templates to use when creating an application for Adatum Corporation.</Description>
<PackageTags>dotnet-new;templates;contoso</PackageTags>
<TargetFramework>netstandard2.0</TargetFramework>
<IncludeContentInPack>true</IncludeContentInPack>
<IncludeBuildOutput>false</IncludeBuildOutput>
<ContentTargetFolders>content</ContentTargetFolders>
</PropertyGroup>
<ItemGroup>
<Content Include="templates\**\*" Exclude="templates\**\bin\**;templates\**\obj\**" />
<Compile Remove="**\*" />
</ItemGroup>
</Project>
En el siguiente ejemplo se muestra la estructura de archivos y carpetas de usar .csproj para crear un paquete de
plantillas. El archivo MyDotnetTemplates.csproj y la carpeta templates se encuentran en la raíz de un directorio
denominado project_folder. La carpeta templates contiene dos plantillas: mytemplate1 y mytemplate2. Cada
plantilla tiene archivos de contenido y una carpeta .template.config con un archivo de configuración template.json.
project_folder
│ MyDotnetTemplates.csproj
│
└───templates
├───mytemplate1
│ │ console.cs
│ │ readme.txt
│ │
│ └───.template.config
│ template.json
│
└───mytemplate2
│ otherfile.cs
│
└───.template.config
template.json
dotnet new -u
El primer nivel de los elementos situados después de Currently installed items: son los identificadores usados
en la desinstalación de una plantilla. Y en el ejemplo anterior, se enumeran Microsoft.DotNet.Common.ItemTemplates
y Microsoft.DotNet.Common.ProjectTemplates.3.0 . Si la plantilla se instaló mediante una ruta de acceso del sistema
de archivos, este identificador será la ruta de acceso de la carpeta .template.config.
Si el paquete se instaló mediante la especificación de una ruta de acceso a la carpeta .template.config, use esa ruta
de acceso absoluta para desinstalar el paquete. Puede ver la ruta de acceso absoluta de la plantilla en el resultado
proporcionado por el comando dotnet new -u . Para obtener más información, consulte la sección Obtención de
una lista de plantillas instaladas anterior.
Vea también
Creación de una plantilla personalizada para dotnet new (tutorial)
Wiki del repositorio de GitHub dotnet/templating
Repositorio de GitHub dotnet/dotnet-template-samples
How to create your own templates for dotnet new (Cómo crear sus propias plantillas para dotnet new)
Esquema template.json en el Almacenamiento del esquema JSON
Tutorial: Creación de una plantilla de elemento
16/09/2020 • 11 minutes to read • Edit Online
Con .NET Core, puede crear e implementar plantillas que generan proyectos, archivos e inclusos recursos. Este
tutorial es el primero de una serie que enseña a crear, instalar y desinstalar plantillas para usarlas con el
comando dotnet new .
En esta parte de la serie, aprenderá a:
Crear una clase para una plantilla de elemento.
Crear el archivo y la carpeta de configuración de la plantilla.
Instalar una plantilla desde una ruta de acceso de archivo.
Probar una plantilla de elemento.
Desinstalar una plantilla de elemento.
Requisitos previos
SDK de .NET Core 2.2 o versiones posteriores.
Leer el artículo de referencia Plantillas personalizadas para dotnet new.
En el artículo de referencia se explican los aspectos básicos de las plantillas y cómo se unen. Parte de esta
información se repetirá en este tutorial.
Abra un terminal y vaya a la carpeta working\templates.
parent_folder
├───test
└───working
└───templates
Cree un archivo nuevo denominado CommonExtensions.cs y ábralo con el editor de texto que prefiera. Esta
clase proporcionará un método de extensión denominado Reverse que invierte el contenido de una cadena.
Pegue el código siguiente y guarde el archivo:
using System;
namespace System
{
public static class StringExtensions
{
public static string Reverse(this string value)
{
var tempArray = value.ToCharArray();
Array.Reverse(tempArray);
return new string(tempArray);
}
}
}
Ahora que creó el contenido de la plantilla, debe crear su configuración en la carpeta raíz de la plantilla.
working
└───templates
└───extensions
└───.template.config
template.json
Abra template.json con el editor de texto que prefiera, pegue el código JSON siguiente y guárdelo.
{
"$schema": "http://json.schemastore.org/template",
"author": "Me",
"classifications": [ "Common", "Code" ],
"identity": "ExampleTemplate.StringExtensions",
"name": "Example templates: string extensions",
"shortName": "stringext",
"tags": {
"language": "C#",
"type": "item"
}
}
Este archivo de configuración contiene todos los valores de la plantilla. Puede ver los valores básicos, como
name y shortName , pero también hay un valor tags/type que está establecido en item . De este modo, la
plantilla se clasifica como una plantilla de elemento. No hay ninguna restricción en el tipo de plantilla que crea.
Los valores item y project son nombres comunes que .NET Core recomienda para que los usuarios puedan
filtrar fácilmente el tipo de plantilla que buscan.
El elemento classifications representa la columna tags que ve cuando ejecuta dotnet new y obtiene una lista
de plantillas. Los usuarios también pueden hacer una búsqueda según las etiquetas de clasificación. No
confunda la propiedad tags del archivo *.json con la lista de etiquetas classifications . Lamentablemente, son
dos elementos que tienen nombres similares. El esquema completo del archivo template.json puede encontrarse
en el Almacenamiento del esquema JSON. Para más información sobre el archivo template.json, consulte la wiki
de plantillas dotnet.
Ahora que tiene un archivo .template.config/template.json válido, la plantilla está lista para instalarla. En el
terminal, vaya a la carpeta extensions y ejecute el comando siguiente para instalar la plantilla ubicada en la
carpeta actual:
En Windows : dotnet new -i .\
En Linux o macOS : dotnet new -i ./
Este comando genera la lista de las plantillas instaladas, que debería incluir la suya.
Options:
-h, --help Displays help for this command.
-l, --list Lists templates containing the specified name. If no name is specified, lists all
templates.
Restore succeeded.
Ejecute el proyecto.
dotnet run
Hello World!
Luego, ejecute dotnet new stringext para generar CommonExtensions.cs desde la plantilla.
Cambie el código en Program.cs para invertir la cadena "Hello World" con el método de extensión que se
proporciona en la plantilla.
Console.WriteLine("Hello World!".Reverse());
dotnet run
!dlroW olleH
¡Enhorabuena! Ha creado e implementado una plantilla de elemento con .NET Core. Como preparación para la
próxima parte de esta serie de tutoriales, debe desinstalar la plantilla que creó. Asegúrese de eliminar también
todos los archivos de la carpeta test. Esto le permitirá volver a un estado limpio listo para la próxima sección
importante de este tutorial.
Desinstalación de la plantilla
Como instaló la plantilla a través de la ruta de acceso de archivo, debe desinstalarla con la ruta de acceso de
archivo absoluta . Ejecute el comando dotnet new -u para ver una lista de las plantillas instaladas. Su plantilla
debe aparecer en último lugar. Use la ruta de acceso mostrada para desinstalar la plantilla con el comando
dotnet new -u <ABSOLUTE PATH TO TEMPLATE DIRECTORY> .
dotnet new -u
NUnit3.DotNetNew.Template
Templates:
NUnit 3 Test Project (nunit) C#
NUnit 3 Test Item (nunit-test) C#
NUnit 3 Test Project (nunit) F#
NUnit 3 Test Item (nunit-test) F#
NUnit 3 Test Project (nunit) VB
NUnit 3 Test Item (nunit-test) VB
C:\working\templates\extensions
Templates:
Example templates: string extensions (stringext) C#
Pasos siguientes
En este tutorial, creó una plantilla de elemento. Para aprender a crear una plantilla de proyecto, siga con esta
serie de tutoriales.
Creación de una plantilla de proyecto
Tutorial: Creación de una plantilla de proyecto
16/09/2020 • 10 minutes to read • Edit Online
Con .NET Core, puede crear e implementar plantillas que generan proyectos, archivos e inclusos recursos. Este
tutorial es el segundo de una serie que enseña a crear, instalar y desinstalar plantillas para usarlas con el comando
dotnet new .
Requisitos previos
Complete la parte 1 de esta serie de tutoriales.
Abra un terminal y vaya a la carpeta working\templates.
working
└───templates
└───consoleasync
consoleasync.csproj
Program.cs
Modificación de Program.cs
Abra el archivo program.cs. El proyecto de la consola no usa un punto de entrada asincrónico, por lo que vamos a
agregarlo. Cambie el código por lo siguiente y guarde el archivo.
using System;
using System.Threading.Tasks;
namespace consoleasync
{
class Program
{
static async Task Main(string[] args)
{
await Console.Out.WriteAsync("Hello World with C# 8.0!");
}
}
}
Modificación de consoleasync.csproj
Actualicemos la versión del lenguaje C# que usa el proyecto a la versión 8.0. Edite el archivo consoleasync.csproj y
agregue el valor <LangVersion> a un nodo <PropertyGroup> .
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
</Project>
Compilar el proyecto
Antes de completar una plantilla de proyecto, debe probarla para asegurarse de que se compila y ejecuta
correctamente.
En el terminal, ejecute el comando siguiente.
dotnet run
Puede eliminar las carpetas obj y bin creadas si usa dotnet run . La eliminación de estos archivos garantizar que la
plantilla solo incluya los archivos relacionados con la plantilla y no cualquier archivo que resulte de una acción de
compilación.
Ahora que creó el contenido de la plantilla, debe crear su configuración en la carpeta raíz de la plantilla.
working
└───templates
└───consoleasync
└───.template.config
template.json
Abra template.json con el editor de texto que prefiera, pegue el código JSON siguiente y guárdelo.
{
"$schema": "http://json.schemastore.org/template",
"author": "Me",
"classifications": [ "Common", "Console", "C#8" ],
"identity": "ExampleTemplate.AsyncProject",
"name": "Example templates: async project",
"shortName": "consoleasync",
"tags": {
"language": "C#",
"type": "project"
}
}
Este archivo de configuración contiene todos los valores de la plantilla. Puede ver los valores básicos, como name
y shortName , pero también hay un valor tags/type que está establecido en project . Esto diseña la plantilla como
una plantilla de proyecto. No hay ninguna restricción en el tipo de plantilla que crea. Los valores item y project
son nombres comunes que .NET Core recomienda para que los usuarios puedan filtrar fácilmente el tipo de
plantilla que buscan.
El elemento classifications representa la columna tags que ve cuando ejecuta dotnet new y obtiene una lista
de plantillas. Los usuarios también pueden hacer una búsqueda según las etiquetas de clasificación. No confunda
la propiedad tags del archivo .json con la lista de etiquetas classifications . Lamentablemente, son dos
elementos que tienen nombres similares. El esquema completo del archivo template.json puede encontrarse en el
Almacenamiento del esquema JSON. Para más información sobre el archivo template.json, consulte la wiki de
plantillas dotnet.
Ahora que tiene un archivo .template.config/template.json válido, la plantilla está lista para instalarla. Antes de
instalar la plantilla, asegúrese de eliminar cualquier archivo o carpeta de archivos adicional que no quiere que se
incluya en la plantilla, como las carpetas bin o obj. En el terminal, vaya a la carpeta consoleasync y ejecute
dotnet new -i .\ para instalar la plantilla ubicada en la carpeta actual. Si usa un sistema operativo Linux o
macOS, use una barra diagonal: dotnet new -i ./ .
Este comando genera la lista de las plantillas instaladas, que debería incluir la suya.
dotnet new -i .\
Options:
-h, --help Displays help for this command.
-l, --list Lists templates containing the specified name. If no name is specified, lists all
templates.
dotnet run
¡Enhorabuena! Ha creado e implementado una plantilla de proyecto con .NET Core. Como preparación para la
próxima parte de esta serie de tutoriales, debe desinstalar la plantilla que creó. Asegúrese de eliminar también
todos los archivos de la carpeta test. Esto le permitirá volver a un estado limpio listo para la próxima sección
importante de este tutorial.
Desinstalación de la plantilla
Como instaló la plantilla a través de una ruta de acceso de archivo, debe desinstalarla con la ruta de acceso de
archivo absoluta . Ejecute el comando dotnet new -u para ver una lista de las plantillas instaladas. Su plantilla
debe aparecer en último lugar. Use la ruta de acceso mostrada para desinstalar la plantilla con el comando
dotnet new -u <ABSOLUTE PATH TO TEMPLATE DIRECTORY> .
dotnet new -u
Verá un resultado similar al siguiente.
NUnit3.DotNetNew.Template
Templates:
NUnit 3 Test Project (nunit) C#
NUnit 3 Test Item (nunit-test) C#
NUnit 3 Test Project (nunit) F#
NUnit 3 Test Item (nunit-test) F#
NUnit 3 Test Project (nunit) VB
NUnit 3 Test Item (nunit-test) VB
C:\working\templates\consoleasync
Templates:
Example templates: async project (consoleasync) C#
Pasos siguientes
En este tutorial creó una plantilla de proyecto. Para información sobre cómo empaquetar la plantilla de elemento y
la de proyecto en un archivo fácil de usar, continúe con esta serie de tutoriales.
Creación de un paquete de plantillas
Tutorial: Creación de un paquete de plantillas
16/09/2020 • 10 minutes to read • Edit Online
Con .NET Core, puede crear e implementar plantillas que generan proyectos, archivos e inclusos recursos. Este
tutorial es el tercero de una serie que enseña a crear, instalar y desinstalar plantillas para usarlas con el comando
dotnet new .
Requisitos previos
Complete la parte 1 y la parte 2 de esta serie de tutoriales.
En este tutorial se usan las dos plantillas que se crearon en las dos primeras partes de este tutorial. Puede
usar otra plantilla siempre que la copie como una carpeta en la carpeta working\templates\ .
Abra un terminal y vaya a la carpeta working\ .
Restore succeeded.
A continuación, abra el archivo templatepack.csproj en su editor favorito y reemplace el contenido por el XML
siguiente:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageType>Template</PackageType>
<PackageVersion>1.0</PackageVersion>
<PackageId>AdatumCorporation.Utility.Templates</PackageId>
<Title>AdatumCorporation Templates</Title>
<Authors>Me</Authors>
<Description>Templates to use when creating an application for Adatum Corporation.</Description>
<PackageTags>dotnet-new;templates;contoso</PackageTags>
<TargetFramework>netstandard2.0</TargetFramework>
<IncludeContentInPack>true</IncludeContentInPack>
<IncludeBuildOutput>false</IncludeBuildOutput>
<ContentTargetFolders>content</ContentTargetFolders>
</PropertyGroup>
<ItemGroup>
<Content Include="templates\**\*" Exclude="templates\**\bin\**;templates\**\obj\**" />
<Compile Remove="**\*" />
</ItemGroup>
</Project>
El valor <PropertyGroup> del XML anterior se divide en tres grupos. El primer grupo trata con las propiedades
requeridas para un paquete de NuGet. Los tres valores <Package están relacionados con las propiedades del
paquete de NuGet para identificar el paquete en una fuente NuGet. En concreto, el valor <PackageId> se usa para
desinstalar el paquete de plantillas con un solo nombre en lugar de una ruta de acceso a un directorio. También se
puede usar para instalar el paquete de plantillas desde una fuente NuGet. Los valores restantes, como <Title> y
<PackageTags> , están relacionados con los metadatos que aparecen en la fuente NuGet. Para más información
sobre la configuración de NuGet, consulte el artículo sobre propiedades de NuGet y MSBuild.
La configuración <TargetFramework> se debe establecer de manera que MSBuild se ejecute correctamente al
ejecutar el comando pack para compilar y empaquetar el proyecto.
Los tres últimos valores están relacionados con la configuración correcta del proyecto para incluir las plantillas en
la carpeta correspondiente del paquete de NuGet cuando se crea.
<ItemGroup> contiene dos valores. En primer lugar, el valor <Content> incluye todo lo que hay en la carpeta
templates como contenido. También se establece para excluir cualquier carpeta bin o carpeta obj para evitar que se
incluya cualquier código compilado (si probó y compiló las plantillas). En segundo lugar, el valor <Compile> excluye
todos los archivos de código de la compilación, independientemente de dónde estén ubicados. Esto evita que el
proyecto que se usa para crear un paquete de plantillas intente compilar el código en la jerarquía de carpetas
templates.
Compilación e instalación
Guarde este archivo y, a continuación, ejecute el comando pack.
dotnet pack
dotnet pack
A continuación, instale el archivo del paquete de plantillas con el comando dotnet new -i PATH_TO_NUPKG_FILE .
Options:
-h, --help Displays help for this command.
-l, --list Lists templates containing the specified name. If no name is specified, lists all
templates.
Si cargó el paquete de NuGet en una fuente NuGet, puede usar el comando dotnet new -i PACKAGEID , donde
PACKAGEID es igual que el valor <PackageId> del archivo .csproj. Este identificador de paquete es igual que el
identificador del paquete de NuGet.
dotnet new -u
Template Instantiation Commands for .NET Core CLI
NUnit3.DotNetNew.Template
Templates:
NUnit 3 Test Project (nunit) C#
NUnit 3 Test Item (nunit-test) C#
NUnit 3 Test Project (nunit) F#
NUnit 3 Test Item (nunit-test) F#
NUnit 3 Test Project (nunit) VB
NUnit 3 Test Item (nunit-test) VB
AdatumCorporation.Utility.Templates
Templates:
Example templates: async project (consoleasync) C#
Example templates: string extensions (stringext) C#
Ejecute dotnet new -u AdatumCorporation.Utility.Templates para desinstalar la plantilla. El comando dotnet new
generará información de ayuda sobre que debe omitir las plantillas que instaló previamente.
¡Enhorabuena! Ya instaló y desinstaló un paquete de plantillas.
Pasos siguientes
Para más información sobre las plantillas, que en gran parte ya conoce, consulte el artículo Plantillas
personalizadas para dotnet new.
Wiki del repositorio de GitHub dotnet/templating
Repositorio de GitHub dotnet/dotnet-template-samples
Esquema template.json en el Almacenamiento del esquema JSON
SDK de proyectos de .NET Core
16/09/2020 • 12 minutes to read • Edit Online
Los proyectos de .NET Core están asociados a un kit de desarrollo de software (SDK). Cada SDK de proyecto es un
conjunto de destinos de MSBuild y tareas asociadas que se encarga de compilar, empaquetar y publicar código. Un
proyecto que hace referencia a un SDK de proyecto en ocasiones se denomina proyecto de estilo SDK.
SDK disponibles
Los siguientes SDK están disponibles para .NET Core:
El SDK de .NET Core es el SDK base de .NET Core. Los otros SDK hacen referencia al SDK de .NET Core, y los
proyectos asociados a los demás SDK disponen de todas las propiedades del SDK de .NET Core. El SDK Web, por
ejemplo, depende de los SDK de .NET Core y de Razor.
También puede crear un SDK propio que se puede distribuir a través de NuGet.
Archivos de proyecto
Los proyectos de .NET Core se basan en el formato MSBuild. Los archivos de proyecto, que tienen extensiones
como .csproj para los proyectos de C# y .fsproj para los de F#, están en formato XML. El elemento raíz de un
archivo de proyecto de MSBuild es Project. El elemento Project tiene un atributo Sdk opcional que especifica qué
SDK (y versión) se van a usar. Para usar las herramientas de .NET Core y compilar el código, establezca el atributo
Sdk en uno de los identificadores de la tabla SDK disponibles.
<Project Sdk="Microsoft.NET.Sdk">
...
</Project>
Para especificar un SDK que proviene de NuGet, incluya la versión al final del nombre, o bien especifique el nombre
y la versión en el archivo global.json.
<Project Sdk="MSBuild.Sdk.Extras/2.0.54">
...
</Project>
Al hacer referencia a un SDK de una de estas formas, se simplifican enormemente los archivos de proyecto de
.NET Core. Durante la evaluación del proyecto, MSBuild agrega importaciones implícitas para Sdk.props en la
parte superior del archivo del proyecto y para Sdk.targets en la inferior.
<Project>
<!-- Implicit top import -->
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
...
<!-- Implicit bottom import -->
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>
TIP
En un equipo con Windows, los archivos Sdk.props y Sdk.targets se pueden encontrar en la carpeta
%ProgramFiles%\dotnet\sdk\[versión]\Sdks\Microsoft.NET.Sdk\Sdk.
Errores de compilación
Si define explícitamente cualquiera de estos elementos en el archivo del proyecto, es probable que obtenga un
error de compilación "NETSDK1022" similar al siguiente:
Se han incluido elementos de compilación duplicados. El SDK de .NET incluye elementos de compilación del
directorio del proyecto de forma predeterminada. Puede quitar estos elementos del archivo de proyecto o
establecer la propiedad “EnableDefaultCompileItems” en “false” si quiere incluirlos explícitamente en el archivo
del proyecto.
Para resolver los errores, lleve a cabo una de las siguientes acciones:
Quite los elementos Compile , EmbeddedResource o None explícitos que coincidan con los implícitos
enumerados en la tabla anterior.
Establezca la propiedad EnableDefaultItems en false para deshabilitar toda la inclusión de archivos
implícita:
<PropertyGroup>
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
Si quiere especificar que se publiquen archivos con la aplicación, puede seguir usando los mecanismos de
MSBuild conocidos para ello, por ejemplo, el elemento Content .
Deshabilite de forma selectiva solo los globs Compile , EmbeddedResource o None estableciendo la propiedad
EnableDefaultCompileItems , EnableDefaultEmbeddedResourceItems o EnableDefaultNoneItems en false :
<PropertyGroup>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
<EnableDefaultNoneItems>false</EnableDefaultNoneItems>
</PropertyGroup>
Si solo deshabilita los globs Compile , el Explorador de soluciones de Visual Studio sigue mostrando
elementos *.cs como parte del proyecto, incluidos como elementos None . Para deshabilitar el glob None
implícito, establezca EnableDefaultNoneItems en false .
Personalización de la compilación
Hay varias maneras de personalizar una compilación. Es posible que quiera invalidar una propiedad y pasarla como
argumento a un comando msbuild o dotnet. También puede agregar la propiedad al archivo del proyecto o a un
archivo Directory.Build.props. Para obtener una lista de propiedades útiles para los proyectos de .NET Core,
consulte Referencia de MSBuild para proyectos de SDK de .NET Core.
Destinos personalizados
Los proyectos de .NET Core pueden empaquetar propiedades y destinos de MSBuild personalizados para su uso en
proyectos que consuman el paquete. Use este tipo de extensibilidad cuando quiera:
Extender el proceso de compilación.
Acceder a artefactos del proceso de compilación, como los archivos generados.
Inspeccionar una configuración en la que se va a invocar la compilación.
Para agregar propiedades o destinos de compilación personalizados, coloque los archivos con el formato
<package_id>.targets o <package_id>.props (por ejemplo, Contoso.Utility.UsefulStuff.targets ) en la carpeta
build del proyecto.
El fragmento de código XML siguiente es de un archivo .csproj que indica al comando dotnet pack lo que debe
empaquetar. El elemento <ItemGroup Label="dotnet pack instructions"> coloca los archivos de destino en la carpeta
build dentro del paquete. El elemento <Target Name="CollectRuntimeOutputs" BeforeTargets="_GetPackageFiles">
coloca los ensamblados y los archivos .json en la carpeta build.
<Project Sdk="Microsoft.NET.Sdk">
...
<ItemGroup Label="dotnet pack instructions">
<Content Include="build\*.targets">
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
</Content>
</ItemGroup>
<Target Name="CollectRuntimeOutputs" BeforeTargets="_GetPackageFiles">
<!-- Collect these items inside a target that runs after build but before packaging. -->
<ItemGroup>
<Content Include="$(OutputPath)\*.dll;$(OutputPath)\*.json">
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
</Content>
</ItemGroup>
</Target>
...
</Project>
Para consumir un destino personalizado en el proyecto, agregue un elemento PackageReference que apunte al
paquete y su versión. A diferencia de las herramientas, el paquete de destinos personalizados se incluye en el cierre
de dependencia del proyecto que lo usa.
Puede configurar cómo se usa el destino personalizado. Como es un destino de MSBuild, puede depender de un
destino concreto, ejecutarse después de otro destino o bien invocarse de forma manual mediante el comando
dotnet msbuild -t:<target-name> . Pero para proporcionar una mejor experiencia de usuario, puede combinar las
herramientas y los destinos personalizados por proyecto. En este escenario, la herramienta por proyecto acepta los
parámetros necesarios y los traduce en la invocación de dotnet msbuild necesaria que ejecuta el destino. Puede
ver una muestra de esta clase de sinergia en el repositorio de ejemplos de MVP Summit 2016 Hackathon del
proyecto dotnet-packer .
Vea también
Instalación de .NET Core
Procedimiento para usar los SDK de proyecto de MSBuild
Empaquetado de destinos y propiedades de MSBuild personalizados con NuGet
Referencia de MSBuild para proyectos del SDK de
.NET Core
16/09/2020 • 17 minutes to read • Edit Online
Esta página es una referencia de las propiedades y los elementos de MSBuild que puede usar para configurar
proyectos de .NET Core.
NOTE
Esta página es un trabajo en curso y no muestra todas las propiedades de MSBuild útiles para el SDK de .NET Core. Para
obtener una lista de las propiedades comunes de MSBuild, vea Propiedades comunes de MSBuild.
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
Para obtener más información, vea Plataformas de destino en proyectos de estilo SDK.
TargetFrameworks
Use la propiedad TargetFrameworks cuando quiera que la aplicación tenga varias plataformas como destino. Para
obtener una lista de los monikers de plataforma de destino válidos, vea Plataformas de destino en proyectos de
estilo SDK.
NOTE
Esta propiedad se omite si se especifica TargetFramework (singular).
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net462</TargetFrameworks>
</PropertyGroup>
Para obtener más información, vea Plataformas de destino en proyectos de estilo SDK.
NetStandardImplicitPackageVersion
NOTE
Esta propiedad solo se aplica a los proyectos que usan netstandard1.x . No se aplica a los que usan netstandard2.x .
Use la propiedad NetStandardImplicitPackageVersion si quiere especificar una versión del marco que sea inferior a
la de la versión del metapaquete. El archivo del proyecto del ejemplo siguiente tiene como destino netstandard1.3
pero usa la versión 1.6.0 de NETStandard.Library .
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<NetStandardImplicitPackageVersion>1.6.0</NetStandardImplicitPackageVersion>
</PropertyGroup>
<PropertyGroup>
...
<PackageId>ClassLibDotNetStandard</PackageId>
<Version>1.0.0</Version>
<Authors>John Doe</Authors>
<Company>Contoso</Company>
</PropertyGroup>
<PropertyGroup>
<RuntimeIdentifier>ubuntu.16.04-x64</RuntimeIdentifier>
</PropertyGroup>
RuntimeIdentifiers
La propiedad RuntimeIdentifiers permite especificar una lista delimitada por puntos y coma de identificadores de
tiempo ejecución (RID) para el proyecto. Use esta propiedad si tiene que publicar para varios entornos de
ejecución. RuntimeIdentifiers se usa en el momento de la restauración para asegurarse de que los recursos
adecuados están en el gráfico.
TIP
RuntimeIdentifier (singular) puede proporcionar compilaciones más rápidas cuando solo se requiere un entorno de
ejecución.
<PropertyGroup>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;ubuntu.16.04-x64</RuntimeIdentifiers>
</PropertyGroup>
TrimmerRootAssembly
El elemento TrimmerRootAssembly permite excluir del recorte un ensamblado. El recorte es el proceso de quitar de
una aplicación empaquetada las partes que no se han usado del tiempo de ejecución. En algunos casos, el recorte
podría quitar de forma incorrecta las referencias necesarias.
El siguiente código XML excluye del recorte el ensamblado System.Security .
<ItemGroup>
<TrimmerRootAssembly Include="System.Security" />
</ItemGroup>
UseAppHost
La propiedad UseAppHost se presentó en la versión 2.1.400 del SDK de .NET Core. Controla si se crea o no un
archivo ejecutable nativo para una implementación. Un archivo ejecutable nativo es obligatorio para las
implementaciones independientes.
En .NET Core 3.0 y versiones posteriores, se crea de forma predeterminada un ejecutable dependiente del marco
de trabajo. Establezca la propiedad UseAppHost en false para deshabilitar la generación del archivo ejecutable.
<PropertyGroup>
<UseAppHost>false</UseAppHost>
</PropertyGroup>
Para obtener más información sobre la implementación, vea Implementación de aplicaciones .NET Core.
Propiedades de compilación
EmbeddedResourceUseDependentUponConvention
LangVersion
EmbeddedResourceUseDependentUponConvention
La propiedad EmbeddedResourceUseDependentUponConvention define si los nombres del archivo de manifiesto del
recurso se generan a partir de la información de tipo de los archivos de código fuente que se ubican
conjuntamente con archivos de recursos. Por ejemplo, si Form1.resx está en la misma carpera que Form1.cs, y
EmbeddedResourceUseDependentUponConvention se establece en true , el archivo .resources generado toma su
nombre del primer tipo que se define en Form1.cs. Por ejemplo, si MyNamespace.Form1 es el primer tipo definido en
Form1.cs, el nombre de archivo generado es myNameSpace.Form1.Resources.
NOTE
Si se especifican los metadatos LogicalName , ManifestResourceName o DependentUpon para un elemento
EmbeddedResource , el nombre del archivo de manifiesto generado para ese archivo de recurso se basa en esos metadatos.
De forma predeterminada, en un nuevo proyecto de .NET Core, esta propiedad se establece en true . Si se
establece en false y no se especifica ningún metadato LogicalName , ManifestResourceName o DependentUpon para
el elemento EmbeddedResource del archivo de proyecto, el nombre del archivo de manifiesto del recurso se basa en
el espacio de nombres raíz del proyecto y en la ruta de acceso relativa al archivo .resx. Para más información,
consulte Denominación de los archivos de manifiesto de recurso.
<PropertyGroup>
<EmbeddedResourceUseDependentUponConvention>true</EmbeddedResourceUseDependentUponConvention>
</PropertyGroup>
LangVersion
La propiedad LangVersion permite especificar una versión concreta del lenguaje de programación. Por ejemplo, si
quiere acceder a las características en vista previa de C#, establezca LangVersion en preview .
<PropertyGroup>
<LangVersion>preview</LangVersion>
</PropertyGroup>
Para obtener más información, vea Control de versiones del lenguaje C#.
<PropertyGroup>
<AnalysisLevel>preview</AnalysisLevel>
</PropertyGroup>
VA L UE SIGN IF IC A DO
CodeAnalysisTreatWarningsAsErrors
La propiedad CodeAnalysisTreatWarningsAsErrors le permite configurar si las advertencias de análisis de código se
deben tratar como advertencias e interrumpir la compilación. Si usa la marca -warnaserror al compilar los
proyectos, las advertencias de análisis de código de .NET también se tratan como errores. Si solo quiere que las
advertencias del compilador se traten como errores, puede establecer la propiedad
CodeAnalysisTreatWarningsAsErrors de MSBuild en false en el archivo del proyecto.
<PropertyGroup>
<CodeAnalysisTreatWarningsAsErrors>false</CodeAnalysisTreatWarningsAsErrors>
</PropertyGroup>
EnableNETAnalyzers
De forma predeterminada, el análisis de código de .NET está habilitado para los proyectos que tienen como destino
.NET 5.0 o una versión posterior. Puede habilitar el análisis de código de .NET para los proyectos que tienen como
destino versiones anteriores de .NET estableciendo la propiedad EnableNETAnalyzers en "true". Para deshabilitar el
análisis de código en cualquier proyecto, establezca esta propiedad en false .
<PropertyGroup>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>
TIP
Otra forma de habilitar el análisis de código de .NET en proyectos que tienen como destino versiones de .NET anteriores a
.NET 5.0 es establecer la propiedad AnalysisLevel en latest .
<PropertyGroup>
<ConcurrentGarbageCollection>false</ConcurrentGarbageCollection>
</PropertyGroup>
InvariantGlobalization
La propiedad InvariantGlobalization configura si la aplicación se ejecuta en modo invariable de globalización, lo
que significa que no tiene acceso a datos específicos de la referencia cultural. Establezca el valor en true para
ejecutar en el modo invariable de globalización. Para obtener más información, consulte Modo invariable.
<PropertyGroup>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
RetainVMGarbageCollection
La propiedad RetainVMGarbageCollection configura el recolector de elementos no utilizados para colocar los
segmentos de memoria eliminados en una lista en espera para su uso futuro o para liberarlos. Al establecer el
valor en true , se indica al recolector de elementos no utilizados que coloque los segmentos en una lista en
espera. Para obtener más información, vea Retain VM (Conservar VM).
<PropertyGroup>
<RetainVMGarbageCollection>true</RetainVMGarbageCollection>
</PropertyGroup>
ServerGarbageCollection
La propiedad ServerGarbageCollection configura si la aplicación usa la recolección de elementos no utilizados de
estación de trabajo o la de servidor. Establezca el valor en true para usar la recolección de elementos no
utilizados de servidor. Para obtener más información, vea Estación de trabajo frente a servidor.
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
ThreadPoolMaxThreads
La propiedad ThreadPoolMaxThreads configura el número máximo de subprocesos para el grupo de subprocesos
de trabajo. Para obtener más información, consulte Máximo de subprocesos.
<PropertyGroup>
<ThreadPoolMaxThreads>20</ThreadPoolMaxThreads>
</PropertyGroup>
ThreadPoolMinThreads
La propiedad ThreadPoolMinThreads configura el número mínimo de subprocesos para el grupo de subprocesos de
trabajo. Para obtener más información, consulte Mínimo de subprocesos.
<PropertyGroup>
<ThreadPoolMinThreads>4</ThreadPoolMinThreads>
</PropertyGroup>
TieredCompilation
La propiedad TieredCompilation configura si el compilador Just-In-Time (JIT) usa la compilación en niveles.
Establezca el valor en false para deshabilitar la compilación en niveles. Para obtener más información, vea
Compilación en niveles.
<PropertyGroup>
<TieredCompilation>false</TieredCompilation>
</PropertyGroup>
TieredCompilationQuickJit
La propiedad TieredCompilationQuickJit configura si el compilador JIT usa JIT rápido. Establezca el valor en false
para deshabilitar JIT rápido. Para obtener más información, vea JIT rápido.
<PropertyGroup>
<TieredCompilationQuickJit>false</TieredCompilationQuickJit>
</PropertyGroup>
TieredCompilationQuickJitForLoops
La propiedad TieredCompilationQuickJitForLoops configura si el compilador JIT usa JIT rápido en métodos que
contienen bucles. Establezca el valor en true para habilitar JIT rápido en métodos que contienen bucles. Para
obtener más información, vea JIT rápido para bucles.
<PropertyGroup>
<TieredCompilationQuickJitForLoops>true</TieredCompilationQuickJitForLoops>
</PropertyGroup>
<PropertyGroup>
<AssetTargetFallback>net461</AssetTargetFallback>
</PropertyGroup>
PackageReference
El elemento PackageReference define una referencia a un paquete NuGet.
El atributo Include especifica el identificador del paquete. El atributo Version especifica la versión o el intervalo
de versiones. Para obtener más información sobre cómo especificar una versión mínima, una máxima, un intervalo
o una coincidencia exacta, vea Intervalos de versiones. También puede agregar los metadatos IncludeAssets ,
ExcludeAssets y PrivateAssets a una referencia de proyecto.
El fragmento del archivo del proyecto del ejemplo siguiente hace referencia al paquete System.Runtime.
<ItemGroup>
<PackageReference Include="System.Runtime" Version="4.3.0" />
</ItemGroup>
Para obtener más información, vea Referencias de paquete en un archivo del proyecto.
ProjectReference
El elemento ProjectReference define una referencia a otro proyecto. El proyecto al que se hace referencia se
agrega como una dependencia del paquete NuGet, es decir, se trata como PackageReference .
El atributo Includeespecifica la ruta de acceso al proyecto. También puede agregar los metadatos IncludeAssets ,
ExcludeAssets y PrivateAssets a una referencia de proyecto.
El fragmento del archivo de proyecto de este ejemplo hace referencia a un proyecto denominado Project2 .
<ItemGroup>
<ProjectReference Include="..\Project2.csproj" />
</ItemGroup>
Referencia
El elemento Reference define una referencia a un archivo de ensamblado.
El atributo Include especifica el nombre del archivo y los metadatos HintPath especifican la ruta de acceso al
ensamblado.
<ItemGroup>
<Reference Include="MyAssembly">
<HintPath>..\..\Assemblies\MyAssembly.dll</HintPath>
</Reference>
</ItemGroup>
<PropertyGroup>
<RestoreIgnoreFailedSource>true</RestoreIgnoreFailedSource>
</PropertyGroup>
Vea también
Referencia del esquema de MSBuild
Propiedades comunes de MSBuild
Propiedades de MSBuild para pack de NuGet
Propiedades de MSBuild para restore de NuGet
Personalización de una compilación
Plataformas de destino en proyectos de
estilo SDK
16/09/2020 • 8 minutes to read • Edit Online
Cuando se dirige a un marco en una aplicación o biblioteca, debe especificar el conjunto de API
que quiere poner a disposición de la aplicación o biblioteca. La plataforma de destino se
especifica en el archivo del proyecto, mediante monikers de la plataforma de destino (TFM).
Una aplicación o biblioteca puede tener como destino una versión de .NET Standard. Las
versiones de .NET Standard representan conjuntos estandarizados de API en todas las
implementaciones de .NET. Por ejemplo, una biblioteca puede tener como destino .NET Standard
1.6 y obtener acceso a las API que funcionan en .NET Core y .NET Framework con el mismo
código base.
Una aplicación o biblioteca también puede tener como destino una implementación específica de
.NET para obtener acceso a las API específicas de la implementación. Por ejemplo, una aplicación
que tenga como destino Xamarin.iOS (por ejemplo, Xamarin.iOS10 ) obtiene acceso a
contenedores de API de iOS proporcionados por Xamarin para iOS 10, o una aplicación que
tenga como destino la Plataforma universal de Windows (UWP, uap10.0 ) tiene acceso a las API
que compilan para dispositivos que ejecutan Windows 10.
Para algunas plataformas de destino (por ejemplo, .NET Framework), las API se definen mediante
los ensamblados que el marco instala en un sistema y pueden incluir las API del marco de trabajo
de la aplicación (por ejemplo, ASP.NET).
Para plataformas de destino basadas en paquetes (por ejemplo, .NET Standard y .NET Core), las
API se definen mediante los paquetes incluidos en la aplicación o biblioteca. Un metapaquete es
un paquete de NuGet que no tiene contenido propio, pero que es una lista de dependencias
(otros paquetes). Una plataforma de destino basada en paquetes de NuGet especifica
implícitamente un metapaquete que hace referencia a todos los paquetes que forman el marco
de trabajo.
VERSIÓ N DE . N ET M O N IK ER DE L A IM P L EM EN TA DO
F RA M EW O RK DE L AT EST P L ATA F O RM A DE VERSIÓ N DE . N ET
DEST IN O VERSIÓ N ESTA B L E DEST IN O ( T F M ) STA N DA RD
VERSIÓ N DE . N ET F RA M EW O RK DE DEST IN O T FM
Silverlight sl4
sl5
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.4;net40;net45</TargetFrameworks>
</PropertyGroup>
<!-- Conditionally obtain references for the .NET Framework 4.0 target -->
<ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
<Reference Include="System.Net" />
</ItemGroup>
<!-- Conditionally obtain references for the .NET Framework 4.5 target -->
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<Reference Include="System.Net.Http" />
<Reference Include="System.Threading.Tasks" />
</ItemGroup>
</Project>
Dentro de su aplicación o biblioteca, puede escribir código condicional para compilar para cada
plataforma de destino:
public class MyClass
{
static void Main()
{
#if NET40
Console.WriteLine("Target framework: .NET Framework 4.0");
#elif NET45
Console.WriteLine("Target framework: .NET Framework 4.5");
#else
Console.WriteLine("Target framework: .NET Standard 1.4");
#endif
}
}
La lista completa de símbolos de preprocesador para plataformas de destino de .NET Core es:
T F M EN DESUSO REP L A C EM EN T
aspnet50 netcoreapp
aspnetcore50
dnxcore50
dnx
dnx45
dnx451
dnx452
T F M EN DESUSO REP L A C EM EN T
dotnet netstandard
dotnet50
dotnet51
dotnet52
dotnet53
dotnet54
dotnet55
dotnet56
netcore50 uap10.0
win netcore45
win8 netcore45
win81 netcore451
win10 uap10.0
winrt netcore45
Vea también
Desarrollo de bibliotecas con herramientas multiplataforma
.NET Standard
Control de versiones de .NET Core
Repositorio de GitHub dotnet/standard
Repositorio de GitHub de herramientas de NuGet
Framework Profiles in .NET (Perfiles de marco de trabajo en .NET)
Adiciones al formato csproj para .NET Core
16/09/2020 • 32 minutes to read • Edit Online
En este documento se describen los cambios que se han agregado a los archivos de proyecto como parte del cambio
de project.json a csproj y MSBuild. Para obtener más información sobre la sintaxis y la referencia del archivo de
proyecto general, consulte la documentación del archivo de proyecto de MSBuild.
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net462</TargetFrameworks>
</PropertyGroup>
Recomendaciones
Como se hace referencia implícitamente a los metapaquetes Microsoft.NETCore.App o NETStandard.Library , estos
son los procedimientos recomendados:
Si el destino es .NET Core o .NET Standard, nunca incluya una referencia explícita a los metapaquetes
Microsoft.NETCore.App o NETStandard.Library mediante un elemento <PackageReference> en el archivo de
proyecto.
Si necesita una versión concreta del runtime cuando el destino es .NET Core, debe usar la propiedad
<RuntimeFrameworkVersion> del proyecto (por ejemplo, 1.0.4 ) en lugar de hacer referencia al metapaquete.
Esto puede ocurrir si está usando implementaciones autocontenidas y necesita una versión de revisión
específica del tiempo de ejecución de LTS 1.0.0, por ejemplo.
Si necesita una versión concreta del metapaquete NETStandard.Library cuando el destino es .NET Standard, puede
usar la propiedad <NetStandardImplicitPackageVersion> y establecer la versión necesaria.
No agregue referencias a los metapaquetes Microsoft.NETCore.App y NETStandard.Library ni las actualice
explícitamente en proyectos de .NET Framework. Si se necesita alguna versión de NETStandard.Library al usar un
paquete NuGet basado en .NET Standard, NuGet instala automáticamente esa versión.
Problema conocido: el SDK de .NET Core 2.1 solo admite esta sintaxis cuando el proyecto también usa
Microsoft.NET.Sdk.Web. Esto se resuelve en el SDK de .NET Core 2.2.
Estas referencias a los metapaquetes de ASP.NET Core tienen un comportamiento ligeramente distinto de los
paquetes más habituales de NuGet. Las implementaciones dependientes del marco de las aplicaciones que usan
estos metapaquetes aprovechan automáticamente el marco de uso compartido de ASP.NET Core. Al usar los
metapaquetes, no se implementa ningún recurso de los paquetes NuGet de ASP.NET Core a los que se hace
referencia con la aplicación, porque el marco de uso compartido de ASP.NET Core ya contiene estos recursos. Los
recursos del marco de uso compartido están optimizados para que la plataforma de destino mejore el tiempo de
inicio de la aplicación. Para más información sobre el marco de uso compartido, consulte Empaquetado de
distribución de .NET Core.
Si se especifica una versión, se trata como la versión mínima del marco de uso compartido de ASP.NET Core para las
implementaciones dependientes del marco y como una versión exacta de las implementaciones autocontenidas. Esto
puede deberse a las siguientes consecuencias:
Si la versión de ASP.NET Core instalada en el servidor es anterior a la versión especificada en PackageReference,
no se iniciará el proceso de .NET Core. Por lo general, las actualizaciones del metapaquete están disponibles en
NuGet.org antes de que se aparezcan en entornos de hospedaje como Azure. Actualizar la versión de
PackageReference a ASP.NET Core podría provocar un error en una aplicación implementada.
Si la aplicación se implementa como una implementación autocontenida, es posible que no contenga las
actualizaciones de seguridad más recientes a .NET Core. Cuando no se especifica una versión, el SDK puede incluir
automáticamente la versión más reciente de ASP.NET Core en la implementación autocontenida.
Si tiene globs en el proyecto e intenta crearlo usando el SDK más reciente, le aparecerá el siguiente error:
Se han incluido elementos de compilación duplicados. El SDK de .NET incluye elementos de compilación del
directorio del proyecto de forma predeterminada. Puede quitar estos elementos del archivo de proyecto o
establecer la propiedad “EnableDefaultCompileItems” en “false” si quiere incluirlos explícitamente en el archivo
del proyecto.
Para evitar este error, puede quitar los elementos Compile explícitos que coinciden con los que aparecen en la tabla
anterior o establecer la propiedad <EnableDefaultCompileItems> en false de esta forma:
<PropertyGroup>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
Al establecer esta propiedad en false , se deshabilita la inclusión implícita y se revierte el comportamiento de SDK
anteriores en los que había que especificar los globs predeterminados del proyecto.
Este cambio no modifica los mecanismos principales de otras inclusiones. En cambio, si quiere especificar, por
ejemplo, que algunos archivos se publiquen con la aplicación, puede seguir usando los mecanismos con los que está
familiarizado en csproj (por ejemplo, el elemento <Content> ).
<EnableDefaultCompileItems> solo deshabilita globs Compile , pero no afecta a otros globs, como el glob None
implícito, que también se aplica a elementos *.cs. Por eso, el Explorador de soluciones sigue mostrando elementos
*.cs como parte del proyecto, incluso como elementos None . Del mismo modo, puede establecer
<EnableDefaultNoneItems> en false para deshabilitar el glob None implícito, de esta forma:
<PropertyGroup>
<EnableDefaultNoneItems>false</EnableDefaultNoneItems>
</PropertyGroup>
Para deshabilitar todos los globs implícitos , puede establecer la propiedad <EnableDefaultItems> en false como
en el ejemplo siguiente:
<PropertyGroup>
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
Si el proyecto tiene varios marcos de destino, los resultados del comando deben centrarse solo en uno de ellos,
especificándolo como una propiedad de MSBuild:
dotnet msbuild -p:TargetFramework=netcoreapp3.1 -pp:fullproject.xml
Adiciones
Atributo Sdk
El elemento raíz <Project> del archivo .csproj tiene un nuevo atributo denominado Sdk . Sdk especifica qué SDK
usará el proyecto. El SDK, como se describe en el documento sobre capas, es un conjunto de tareas y destinos de
MSBuild que pueden generar código de .NET Core. Los siguientes SDK están disponibles para .NET Core:
1. El SDK de .NET Core con el id. de Microsoft.NET.Sdk
2. El SDK web de .NET Core con el id. de Microsoft.NET.Sdk.Web
3. El SDK de la biblioteca de clases de Razor de .NET Core con el id. Microsoft.NET.Sdk.Razor
4. El servicio de trabajo de .NET Core con el id. de Microsoft.NET.Sdk.Worker (a partir de .NET Core 3.0)
5. WinForms y WPF de .NET Core con el id. de Microsoft.NET.Sdk.WindowsDesktop (a partir de .NET Core 3.0)
Debe tener el conjunto de atributos Sdk establecido en uno de esos id. del elemento <Project> para poder usar las
herramientas de .NET Core y generar el código.
PackageReference
Elemento <PackageReference> que especifica una dependencia de NuGet en el proyecto. El atributo Include
especifica el identificador del paquete.
Versión
El atributo Version necesario especifica la versión del paquete que se va a restaurar. El atributo respeta las reglas del
esquema de intervalo de versiones de NuGet. El comportamiento predeterminado es una versión mínima, incluida
una coincidencia. Por ejemplo, la especificación de Version="1.2.3" es equivalente a la notación de NuGet [1.2.3, )
y significa que el paquete resuelto tendrá la versión 1.2.3 si está disponible o, de lo contrario, una superior.
IncludeAssets, ExcludeAssets y PrivateAssets
El atributo IncludeAssets especifica qué recursos que pertenecen al paquete especificado por <PackageReference> se
deben consumir. De forma predeterminada, se incluyen todos los recursos del paquete.
El atributo ExcludeAssets especifica qué recursos que pertenecen al paquete especificado por <PackageReference>
no se deben consumir.
El atributo PrivateAssets especifica qué recursos que pertenecen al paquete especificado por <PackageReference> se
deben consumir, pero no pasar al proyecto siguiente. Cuando este atributo no existe, los recursos Analyzers , Build
y ContentFiles son privados de forma predeterminada.
NOTE
PrivateAssets es equivalente al elemento project.json/xproj SuppressParent .
Estos atributos pueden contener uno o varios de los siguientes elementos, separados por punto y coma ; si aparece
más de uno:
Compile : el contenido de la carpeta lib está disponible para compilación.
Runtime : el contenido de la carpeta runtime está distribuido.
ContentFiles : se usa el contenido de la carpeta contentfiles.
Build : se usan los archivos props/targets de la carpeta build.
Native : el contenido de recursos nativos se copia en la carpeta output en runtime.
Analyzers : se usan los analizadores.
DotnetCliToolReference
Un elemento <DotNetCliToolReference> especifica la herramienta de la CLI que el usuario quiere restaurar en el
contexto del proyecto. Es un sustituto del nodo tools de project.json.
Tenga en cuenta que DotNetCliToolReference está ahora en desuso en favor de las herramientas locales de .NET Core.
Versión
Version especifica la versión del paquete que se va a restaurar. El atributo respeta las reglas del esquema de
versiones de NuGet. El comportamiento predeterminado es una versión mínima, incluida una coincidencia. Por
ejemplo, la especificación de Version="1.2.3" es equivalente a la notación de NuGet [1.2.3, ) y significa que el
paquete resuelto tendrá la versión 1.2.3 si está disponible o, de lo contrario, una superior.
RuntimeIdentifiers
El elemento de propiedad <RuntimeIdentifiers> permite especificar una lista delimitada por puntos y coma de
identificadores de tiempo ejecución (RID) para el proyecto. Los RID permiten publicar implementaciones
autocontenidas.
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;ubuntu.16.04-x64</RuntimeIdentifiers>
RuntimeIdentifier
El elemento de propiedad <RuntimeIdentifier> permite especificar solo un identificador de tiempo ejecución (RID)
para el proyecto. El RID permite publicar una implementación autocontenida.
<RuntimeIdentifier>ubuntu.16.04-x64</RuntimeIdentifier>
Use <RuntimeIdentifiers> (en plural) en su lugar si tiene que publicar para varios entornos de ejecución.
<RuntimeIdentifier> puede proporcionar compilaciones más rápidas cuando solo se requiere un entorno de
ejecución.
PackageTargetFallback
El elemento de propiedad <PackageTargetFallback> permite especificar un conjunto de destinos compatibles que se
van a usar al restaurar paquetes. Está diseñado para permitir que los paquetes que usan la dotnet TxM (destino x
moniker) funcionen con paquetes que no declaran una dotnet TxM. Si el proyecto usa la dotnet TxM, todos los
paquetes de los que depende también deben tener una dotnet TxM, a menos que agregue el elemento
<PackageTargetFallback> a su proyecto a fin de permitir que las plataformas sin dotnet sean compatibles con dotnet.
En el ejemplo siguiente se proporcionan los elementos Fallback para todos los destinos del proyecto:
<PackageTargetFallback>
$(PackageTargetFallback);portable-net45+win8+wpa81+wp8
</PackageTargetFallback >
En el ejemplo siguiente se especifican los elementos Fallback solo para el destino netcoreapp3.1 :
<PackageTargetFallback Condition="'$(TargetFramework)'=='netcoreapp3.1'">
$(PackageTargetFallback);portable-net45+win8+wpa81+wp8
</PackageTargetFallback >
Eventos de compilación
La forma en que se especifican los eventos anteriores y posteriores a la compilación en el archivo de proyecto ha
cambiado. No se recomiendan las propiedades PreBuildEvent y PostBuildEvent en el formato de proyecto de estilo
SDK, ya que las macros como $(ProjectDir) no se resuelven. Por ejemplo, el código siguiente ya no se admite:
<PropertyGroup>
<PreBuildEvent>"$(ProjectDir)PreBuildEvent.bat" "$(ProjectDir)..\" "$(ProjectDir)" "$(TargetDir)"
</PreBuildEvent>
</PropertyGroup>
En los proyectos de estilo SDK, use un destino de MSBuild denominado PreBuild o PostBuild , y establezca la
propiedad BeforeTargets para PreBuild , o bien la propiedad AfterTargets para PostBuild . Para el ejemplo
anterior, use el código siguiente:
NOTE
Puede usar cualquier nombre para los destinos de MSBuild, pero el IDE de Visual Studio reconoce los destinos PreBuild y
PostBuild , por lo que se recomienda usar esos nombres para poder editar los comandos en el IDE de Visual Studio.
compound-expression = 1*1(simple-expression /
simple-expression "WITH" license-exception-id /
compound-expression "AND" compound-expression /
compound-expression "OR" compound-expression ) /
"(" compound-expression ")" )
NOTE
Solo se puede especificar una de estos elementos cada vez: PackageLicenseExpression , PackageLicenseFile o
PackageLicenseUrl .
PackageLicenseFile
Ruta de acceso a un archivo de licencia dentro del paquete si usa una licencia que no tiene asignado un identificador
SPDX, o se trata de una licencia personalizada (en caso contrario, se prefiere PackageLicenseExpression )
Reemplaza PackageLicenseUrl , no se puede combinar con PackageLicenseExpression y requiere Visual Studio
versión 15.9.4 y el SDK de .NET 2.1.502, 2.2.101 o una versión posterior.
Deberá asegurarse de que el archivo de licencia está empaquetado; para ello, agréguelo explícitamente al proyecto.
Ejemplo de uso:
<PropertyGroup>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
</PropertyGroup>
<ItemGroup>
<None Include="licenses\LICENSE.txt" Pack="true" PackagePath="$(PackageLicenseFile)"/>
</ItemGroup>
PackageLicenseUrl
Una dirección URL a la licencia que se aplica al paquete. (en desuso desde Visual Studio 15.9.4, SDK de .NET 2.1.502 y
2.2.101)
PackageIcon
Ruta de acceso a una imagen del paquete que se va a usar como icono del paquete. Más información sobre los
metadatos de icon . PackageIconUrl está en desuso en favor de PackageIcon.
PackageReleaseNotes
Notas de la versión para el paquete.
PackageTags
Una lista de etiquetas delimitada por punto y coma que designa el paquete.
PackageOutputPath
Determina la ruta de acceso de salida en la que se va a quitar el paquete empaquetado. El valor predeterminado es
$(OutputPath) .
IncludeSymbols
Este valor booleano indica si el paquete debe crear un paquete de símbolos adicionales cuando se empaqueta el
proyecto. El formato del paquete de símbolos se controla mediante la propiedad SymbolPackageFormat .
SymbolPackageFormat
Especifica el formato del paquete de símbolos. Si es "symbols.nupkg", se crea un paquete de símbolos heredado con
una extensión .symbols.nupkg que contiene archivos PDB, DLL y otros archivos de salida. Si es "snupkg", se crea un
paquete de símbolos snupkg que contiene los archivos PDB portátiles. El valor predeterminado es "symbols.nupkg".
IncludeSource
Este valor booleano indica si el proceso de empaquetado debe crear un paquete de origen. El paquete de origen
contiene el código fuente de la biblioteca, así como archivos PDB. Los archivos de origen se colocan en el directorio
src/ProjectName , en el archivo de paquete resultante.
IsTool
Especifica si se copian todos los archivos de salida en la carpeta tools en lugar de la carpeta lib. Esto es diferente de
un elemento DotNetCliTool , que se especifica estableciendo el elemento PackageType en el archivo .csproj.
RepositoryUrl
Especifica la dirección URL del repositorio donde reside el código fuente del paquete o desde el que se está creando.
RepositoryType
Especifica el tipo del repositorio. El valor predeterminado es “git”.
RepositoryBranch
Especifica el nombre de la rama de origen en el repositorio. Cuando el proyecto se empaqueta en un paquete NuGet,
se agrega a los metadatos del paquete.
RepositoryCommit
Confirmación o conjunto de cambios opcionales de repositorio para indicar en qué origen se ha compilado el
paquete. RepositoryUrl también se debe especificar para que esta propiedad se incluya. Cuando el proyecto se
empaqueta en un paquete NuGet, esta confirmación o conjunto de cambios se agrega a los metadatos del paquete.
NoPackageAnalysis
Especifica que el paquete no debe ejecutar el análisis de paquetes después de crear el paquete.
MinClientVersion
Especifica la versión mínima del cliente de NuGet que puede instalar este paquete, aplicada por nuget.exe y el
Administrador de paquetes de Visual Studio.
IncludeBuildOutput
Este valor booleano especifica si se deben empaquetar los ensamblados de salida de la compilación en el archivo
.nupkg o no.
IncludeContentInPack
Este valor booleano especifica si los elementos del tipo Content se incluirán automáticamente en el paquete
resultante. De manera predeterminada, es true .
BuildOutputTargetFolder
Especifica la carpeta en la que se colocarán los ensamblados de salida. Los ensamblados de salida (y otros archivos
de salida) se copian en sus respectivas carpetas de marco.
ContentTargetFolders
Esta propiedad especifica la ubicación predeterminada a la que deben ir todos los archivos de contenido si no se
especifica PackagePath para ellos. El valor predeterminado es “content;contentFiles”.
NuspecFile
Ruta de acceso relativa o absoluta al archivo .nuspec que se usa para el empaquetado.
NOTE
Si se especifica el archivo .nuspec, se usa exclusivamente para la información de empaquetado y no se usa ninguna de la
información de los proyectos.
NuspecBasePath
Ruta de acceso base para el archivo .nuspec.
NuspecProperties
Lista separada por punto y coma de pares clave=valor.
Propiedades de AssemblyInfo
Los atributos de ensamblado que solían estar presentes en un archivo AssemblyInfo ahora se generan
automáticamente a partir de las propiedades.
Propiedades por atributo
Como se muestra en la tabla siguiente, cada atributo tiene una propiedad que controla el contenido y otra que
deshabilita su generación:
Notas:
El comportamiento predeterminado de AssemblyVersion y FileVersion consiste en adoptar el valor de
$(Version) sin sufijo. Por ejemplo, si $(Version) es 1.2.3-beta.4 , entonces el valor sería 1.2.3 .
El valor predeterminado de InformationalVersion es el de $(Version) .
InformationalVersion tiene $(SourceRevisionId) anexado si la propiedad está presente. Puede deshabilitarse
mediante IncludeSourceRevisionInInformationalVersion .
Las propiedades Copyright y Description también se utilizan para metadatos de NuGet.
Configuration se comparte con todos los procesos de compilación y se establece mediante el parámetro
--configuration de los comandos dotnet .
GenerateAssemblyInfo
Un valor booleano que habilita o deshabilita toda la generación de AssemblyInfo. El valor predeterminado es true .
GeneratedAssemblyInfoFile
La ruta de acceso del archivo de información del ensamblado generado. De forma predeterminada, se trata de un
archivo del directorio $(IntermediateOutputPath) (obj).
Administración de dependencias en aplicaciones
.NET Core
16/09/2020 • 2 minutes to read • Edit Online
En este artículo se explica cómo agregar y quitar dependencias mediante la modificación del archivo de proyecto o
mediante la CLI.
El elemento <PackageReference>
El archivo del proyecto <PackageReference> tiene la estructura siguiente:
El atributo Include especifica el identificador del paquete que se va a agregar al proyecto. El atributo Version
especifica la versión que se va a obtener. Las versiones se especifican en función de las reglas de versión de NuGet.
NOTE
Si no está familiarizado con la sintaxis del archivo del proyecto, vea la documentación de Referencia de proyectos de MSBuild
para obtener más información.
Use condiciones para agregar una dependencia que solo está disponible en un destino específico, tal como se
muestra en el ejemplo siguiente:
En el ejemplo anterior, la dependencia solo será válida si la compilación sucede para ese destino dado. El elemento
$(TargetFramework) de la condición es una propiedad de MSBuild que se está configurando en el proyecto. En las
aplicaciones .NET Core más comunes, no es necesario hacer esto.
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.2" />
</ItemGroup>
</Project>
Vea también
Referencias de paquete en archivos de proyecto
Comando dotnet list package
Herramientas adicionales de .NET Core
16/09/2020 • 4 minutes to read • Edit Online
En esta sección se recopila una lista de herramientas compatibles con las funcionalidades de .NET Core y que las
amplían, además de las herramientas de la CLI de .NET Core.
La herramienta de desinstalación de .NET Core ( dotnet-core-uninstall ) permite quitar los SDK y los entornos en
tiempo de ejecución de .NET Core de un sistema. Hay una colección de opciones disponible para especificar las
versiones que desea desinstalar.
La herramienta es compatible con Windows y macOS. Linux no se admite actualmente.
En Windows, la herramienta solo puede desinstalar los SDK y entornos en tiempo de ejecución que se instalaron
mediante uno de los siguientes instaladores:
El instalador del SDK y del entorno en tiempo de ejecución de .NET Core.
El instalador de Visual Studio en versiones anteriores a Visual Studio 2019, versión 16.3.
En macOS, la herramienta solo puede desinstalar los SDK y entornos en tiempo de ejecución ubicados en la
carpeta /usr/local/share/dotnet.
Debido a estas limitaciones, es posible que la herramienta no pueda desinstalar todos los SDK y los entornos en
tiempo de ejecución de .NET Core de la máquina. Puede usar el comando dotnet --info para buscar todos los
SDK y los entornos en tiempo de ejecución de .NET Core instalados, incluidos los entornos en tiempo de ejecución
y SDK que esta herramienta no puede quitar. El comando dotnet-core-uninstall list muestra qué SDK se pueden
desinstalar con la herramienta.
Instalación de la herramienta
Puede descargar la herramienta de desinstalación de .NET Core desde la página de versiones de la herramienta y
encontrar el código fuente en el repositorio dotnet/cli-lab de GitHub.
NOTE
La herramienta requiere elevación para desinstalar los SDK y los entornos en tiempo de ejecución de .NET Core. Por lo tanto,
debe instalarse en un directorio protegido contra escritura, como C:\Archivos de programa en Windows o /usr/local/bin en
macOS. Consulte también Acceso con privilegios elevados para comandos de dotnet. Para obtener más información, vea las
instrucciones de instalación detalladas.
Ejecución de la herramienta
En los pasos siguientes se muestra el enfoque recomendado para ejecutar la herramienta de desinstalación:
Paso 1: Mostrar los SDK y los entornos en tiempo de ejecución de .NET Core instalados
Paso 2: Realizar un simulacro
Paso 3: Desinstalar los SDK y los entornos en tiempo de ejecución de .NET Core
Paso 4: Eliminar la carpeta de reserva de NuGet (opcional)
Paso 1: Mostrar los SDK y los entornos en tiempo de ejecución de .NET Core instalados
El comando dotnet-core-uninstall list enumera los SDK y los entornos en tiempo de ejecución de .NET Core
instalados que se pueden quitar con esta herramienta. Visual Studio puede necesitar algunos SDK y entornos en
tiempo de ejecución, que se muestran con una nota de por qué no se recomienda desinstalarlos.
NOTE
En la mayoría de los casos, la salida del comando dotnet-core-uninstall list no coincidirá con la lista de versiones
instaladas en la salida de dotnet --info . En concreto, esta herramienta no mostrará las versiones instaladas mediante
archivos ZIP ni administradas por Visual Studio (cualquier versión instalada con Visual Studio 2019 16.3 o posterior). Una
manera de comprobar si una versión está administrada por Visual Studio es verla en Add or Remove Programs , donde las
versiones administradas de Visual Studio se marcan como tales en sus nombres para mostrar.
dotnet-core-uninstall list
Sinopsis
Opciones
Windows
macOS
--aspnet-runtime
Enumera todos los entornos en tiempo de ejecución de ASP.NET Core que se pueden desinstalar con esta
herramienta.
--hosting-bundle
Enumera todos los conjuntos de hospedaje de .NET Core que se pueden desinstalar con esta herramienta.
--runtime
Enumera todos los entornos en tiempo de ejecución de .NET Core que se pueden desinstalar con esta
herramienta.
--sdk
Enumera todos los SDK de .NET Core que se pueden desinstalar con esta herramienta.
-v, --verbosity <LEVEL>
Establece el nivel de detalle. Los valores permitidos son q[uiet] , m[inimal] , n[ormal] , d[etailed] y
diag[nostic] . El valor predeterminado es normal .
--x64
Enumera todos los SDK y entornos en tiempo de ejecución de .NET Core x64 que se pueden desinstalar con
esta herramienta.
--x86
Enumera todos los SDK y entornos en tiempo de ejecución de .NET Core x86 que se pueden desinstalar con
esta herramienta.
Ejemplos
Enumerar todos los SDK y entornos en tiempo de ejecución de .NET Core que se pueden quitar con esta
herramienta:
dotnet-core-uninstall list
Enumerar todos los SDK y entornos en tiempo de ejecución de .NET Core x64:
dotnet-core-uninstall list --x64
Argumentos
VERSION
La versión especificada que se va a desinstalar. Puede enumerar varias versiones una detrás de la otra,
separadas por espacios. También se admiten los archivos de respuesta.
TIP
Los archivos de respuesta son una alternativa a la colocación de todas las versiones en la línea de comandos. Son
archivos de texto, normalmente con una extensión *.rsp y cada versión aparece en una línea independiente. Para
especificar un archivo de respuesta para el argumento VERSION , use el carácter @ seguido inmediatamente del
nombre del archivo de respuesta.
Opciones
Windows
macOS
--all
Quita solo los SDK y los entornos en tiempo de ejecución de .NET Core que tienen una versión menor que la
versión especificada. La versión especificada permanece instalada.
--all-but <VERSIONS>[ <VERSION>...]
Quita todos los SDK y entornos en tiempo de ejecución de .NET Core, excepto las versiones especificadas.
--all-but-latest
Quita los SDK y los entornos en tiempo de ejecución de .NET Core, excepto la versión más alta.
--all-lower-patches
Quita los SDK y los entornos en tiempo de ejecución de .NET Core reemplazados por revisiones superiores.
Esta opción protege el archivo global.json.
--all-previews
Quita los SDK y los entornos en tiempo de ejecución de .NET Core marcados como versiones preliminares.
--all-previews-but-latest
Quita los SDK y los entornos en tiempo de ejecución de .NET Core marcados como versiones preliminares,
excepto la versión preliminar más alta.
--aspnet-runtime
Quita los SDK y los entornos en tiempo de ejecución de .NET Core que coinciden con la versión
major.minor especificada.
--runtime
Establece el nivel de detalle. Los valores permitidos son q[uiet] , m[inimal] , n[ormal] , d[etailed] y
diag[nostic] . El valor predeterminado es normal .
--x64
Se debe usar con --sdk , --runtime y --aspnet-runtime para quitar los SDK o los entornos en tiempo de
ejecución x64.
--x86
Se debe usar con --sdk , --runtime y --aspnet-runtime para quitar los SDK o los entornos en tiempo de
ejecución x86.
--force Fuerza la eliminación de las versiones que Visual Studio puede usar.
Notas:
1. Se requiere exactamente uno de los valores --sdk , --runtime , --aspnet-runtime y --hosting-bundle .
2. --all , --all-below , --all-but , --all-but-latest , --all-lower-patches , --all-previews ,
--all-previews-but-latest , --major-minor y [<VERSION>...] son valores exclusivos.
3. Si no se especifica --x64 o --x86 , se quitarán tanto x64 como x86.
Ejemplos
NOTE
De forma predeterminada, los SDK y los entornos en tiempo de ejecución de .NET Core que puede necesitar Visual Studio u
otros SDK no se incluyen en la salida de dotnet-core-uninstall dry-run . En los siguientes ejemplos, es posible que
algunos de los SDK y entornos en tiempo de ejecución especificados no se incluyan en la salida, según el estado de la
máquina. Para incluir todos los SDK y entornos en tiempo de ejecución, agréguelos explícitamente como argumentos o use la
opción --force .
Realizar un simulacro de la eliminación de todos entornos en tiempo de ejecución de .NET Core que se han
reemplazado por revisiones superiores:
Realizar un simulacro de la eliminación de todos los SDK de .NET Core inferiores a la versión 2.2.301 :
Paso 3: Desinstalar los SDK y los entornos en tiempo de ejecución de .NET Core
dotnet-core-uninstall remove desinstala los SDK y los entornos en tiempo de ejecución de .NET Core especificados
por una colección de opciones. La herramienta no se puede usar para desinstalar los SDK y entornos en tiempo de
ejecución con la versión 5.0 o posterior.
Dado que esta herramienta tiene un comportamiento destructivo, es altamente recomendable que realice un
simulacro antes de ejecutar el comando remove. El simulacro le mostrará qué SDK y entornos en tiempo de
ejecución de .NET Core se quitarán cuando use el comando remove . Consulte ¿Puedo quitar una versión? para
saber qué SDK y entornos en tiempo de ejecución se pueden quitar de forma segura.
Cau t i on
Argumentos
VERSION
La versión especificada que se va a desinstalar. Puede enumerar varias versiones una detrás de la otra,
separadas por espacios. También se admiten los archivos de respuesta.
TIP
Los archivos de respuesta son una alternativa a la colocación de todas las versiones en la línea de comandos. Son
archivos de texto, normalmente con una extensión *.rsp y cada versión aparece en una línea independiente. Para
especificar un archivo de respuesta para el argumento VERSION , use el carácter @ seguido inmediatamente del
nombre del archivo de respuesta.
Opciones
Windows
macOS
--all
Quita solo los SDK y los entornos en tiempo de ejecución de .NET Core que tienen una versión menor que la
versión especificada. La versión especificada permanece instalada.
--all-but <VERSIONS>[ <VERSION>...]
Quita todos los SDK y entornos en tiempo de ejecución de .NET Core, excepto las versiones especificadas.
--all-but-latest
Quita los SDK y los entornos en tiempo de ejecución de .NET Core, excepto la versión más alta.
--all-lower-patches
Quita los SDK y los entornos en tiempo de ejecución de .NET Core reemplazados por revisiones superiores.
Esta opción protege el archivo global.json.
--all-previews
Quita los SDK y los entornos en tiempo de ejecución de .NET Core marcados como versiones preliminares.
--all-previews-but-latest
Quita los SDK y los entornos en tiempo de ejecución de .NET Core marcados como versiones preliminares,
excepto la versión preliminar más alta.
--aspnet-runtime
Quita los SDK y los entornos en tiempo de ejecución de .NET Core que coinciden con la versión
major.minor especificada.
--runtime
Solo quita los entornos en tiempo de ejecución de .NET Core.
--sdk
Establece el nivel de detalle. Los valores permitidos son q[uiet] , m[inimal] , n[ormal] , d[etailed] y
diag[nostic] . El valor predeterminado es normal .
--x64
Se debe usar con --sdk , --runtime y --aspnet-runtime para quitar los SDK o los entornos en tiempo de
ejecución x64.
--x86
Se debe usar con --sdk , --runtime y --aspnet-runtime para quitar los SDK o los entornos en tiempo de
ejecución x86.
-y, --yes Ejecuta el comando sin requerir una confirmación sí o no.
--force Fuerza la eliminación de las versiones que Visual Studio puede usar.
Notas:
1. Se requiere exactamente uno de los valores --sdk , --runtime , --aspnet-runtime y --hosting-bundle .
2. --all , --all-below , --all-but , --all-but-latest , --all-lower-patches , --all-previews ,
--all-previews-but-latest , --major-minor y [<VERSION>...] son valores exclusivos.
3. Si no se especifica --x64 o --x86 , se quitarán tanto x64 como x86.
Ejemplos
NOTE
De forma predeterminada, los SDK y los entornos en tiempo de ejecución de .NET Core que puede necesitar Visual Studio u
otros SDK se mantienen. En los siguientes ejemplos, es posible que algunos de los SDK y entornos en tiempo de ejecución
especificados se mantengan, según el estado de la máquina. Para quitar todos los SDK y entornos en tiempo de ejecución,
agréguelos explícitamente como argumentos o use la opción --force .
Quitar todos los entornos en tiempo de ejecución de .NET Core excepto la versión 3.0.0-preview6-27804-01
sin necesidad de la confirmación S/N:
Quitar todos los SDK de .NET Core 1.1 sin necesidad de la confirmación de S/N:
Quitar todos los SDK de .NET Core que se puedan quitar con seguridad con esta herramienta:
dotnet-core-uninstall remove --all --sdk
Quitar todos los SDK de .NET Core que puede quitar esta herramienta, incluidos los SDK que puede
necesitar Visual Studio (no recomendado):
Quitar todos los SDK de .NET Core que se especifican en el archivo de respuesta versions.rsp
2.2.300
2.1.700
Desinstalación de la herramienta.
Windows
macOS
1. Abra Agregar o quitar programas .
2. Busque Microsoft .NET Core SDK Uninstall Tool .
3. Seleccione Desinstalar .
Uso de la herramienta WCF Web Service Reference
Provider
18/03/2020 • 6 minutes to read • Edit Online
Durante años, muchos desarrolladores de Visual Studio han disfrutado de la productividad que ofrecía la
herramienta Agregar referencia de ser vicio cuando sus proyectos de .NET Framework necesitaban acceder a
servicios web. La herramienta WCF Web Ser vice Reference Provider es una extensión de servicio conectada
de Visual Studio que proporciona una experiencia similar a la función Agregar referencia de servicio para
proyectos de .NET Core y ASP.NET Core. Esta herramienta recupera metadatos de un servicio web en la solución
actual, en una ubicación de red o desde un archivo WSDL, y genera un archivo de origen compatible con .NET
Core. El archivo contiene el código de proxy de cliente e Windows Communication Foundation (WCF) y podrá
usarlo para acceder al servicio web.
IMPORTANT
Solo debe hacer referencia a servicios desde un origen de confianza. Si agrega referencias desde un origen que no es de
confianza podría poner en peligro la seguridad.
Requisitos previos
Visual Studio 2017, versión 15.5 o versiones posteriores
En este artículo se usa la plantilla de proyecto Aplicación web de ASP.NET Core para explicar cómo agregar
una referencia de servicio web de WCF al proyecto:
1. En el Explorador de soluciones, haga doble clic en el nodo Ser vicios conectados del proyecto (para un
proyecto de .NET Core o .NET Standard, esta opción está disponible cuando hace clic con el botón derecho
en el nodo Dependencias del proyecto en el Explorador de soluciones).
Aparece la página Ser vicios conectados , como se muestra en esta imagen:
2. En la página Ser vicios conectados , haga clic en Microsoft WCF Web Ser vice Reference Provider .
Se abrirá el asistente para Configurar referencia de ser vicio web de WCF :
3. Seleccione un servicio.
3a. Hay varias opciones de búsqueda de servicios disponibles en el asistente para Configurar referencia
de ser vicio web de WCF :
Para buscar servicios definidos en la solución actual, haga clic en el botón Detectar .
Para buscar servicios hospedados en una dirección específica, escriba la dirección URL del servicio en el
cuadro Dirección y haga clic en el botón Ir .
Para seleccionar un archivo WSDL que contiene la información de metadatos del servicio web, haga clic
en el botón Examinar .
3b. Seleccione el servicio de la lista de resultados de búsqueda en el cuadro Ser vicios . Si es necesario,
escriba el espacio de nombres para el código generado en el cuadro de texto Espacio de nombres
correspondiente.
3c. Haga clic en el botón Siguiente para abrir las páginas Opciones de tipo de datos y Opciones de
cliente . O bien, haga clic en el botón Finalizar para usar las opciones predeterminadas.
4. El formulario Opciones de tipo de datos permite ajustar los valores de configuración de la referencia de
servicio generada:
NOTE
La opción de la casilla Reutilizar tipos en los ensamblados a los que se hace referencia es útil cuando se
definen los tipos de datos necesarios para la generación de código de referencia de servicio en uno de los
ensamblados de referencia del proyecto. Es importante volver a usar esos tipos de datos existentes para evitar
problemas de tiempo de ejecución o conflictos de tipo de tiempo de compilación.
Puede haber un retraso mientras se carga la información de tipo, en función del número de dependencias
del proyecto y otros factores de rendimiento del sistema. El botón Finalizar está deshabilitado durante la
carga, a menos que la casilla Reutilizar tipos en los ensamblados a los que se hace referencia esté
desactivada.
5. Haga clic en Finalizar cuando haya terminado.
Mientras muestra el progreso, la herramienta:
Descarga los metadatos del servicio WCF.
Genera el código de referencia de servicio en un archivo con el nombre reference.cs y lo agrega al proyecto
bajo el nodo Ser vicios conectados .
Actualiza el archivo de proyecto (.csproj) con las referencias del paquete NuGet necesarias para compilarlo y
ejecutarlo en la plataforma de destino.
Cuando se completan estos procesos, puede crear una instancia del tipo de cliente WCF generado e invocar las
operaciones del servicio.
Vea también
Introducción a las aplicaciones de Windows Communication Foundation
Servicios de Windows Communication Foundation y servicios de datos WCF en Visual Studio
Características compatibles con WCF en .NET Core
Preguntas y comentarios
Si tiene alguna pregunta o comentario, notifíquelo en la Comunidad de desarrolladores mediante la herramienta
Notificar un problema.
Notas de la versión
Eche un vistazo a las notas de la versión para obtener información actualizada sobre la versión, incluidos los
problemas conocidos.
Herramienta dotnet-svcutil de WCF para .NET Core
16/09/2020 • 6 minutes to read • Edit Online
La herramienta dotnet-svcutil de Windows Communication Foundation (WCF) es una herramienta de .NET que
recupera metadatos de un servicio web en una ubicación de red o de un archivo WSDL, y genera una clase de WCF
que contiene métodos de proxy de cliente que acceden a las operaciones del servicio web.
Similar a la herramienta de utilidad de metadatos de Ser viceModel (Svcutil.exe) para proyectos de .NET
Framework, dotnet-svcutil es una herramienta de línea de comandos para generar una referencia de servicio
web compatible con proyectos de .NET Core y .NET Standard.
La herramienta dotnet-svcutil es una alternativa al proveedor de servicios conectados de Visual Studio WCF
Web Ser vice Reference que se distribuyó por primera vez en la versión 15.5 de Visual Studio 2017. La
herramienta multiplataforma dotnet-svcutil , como herramienta de .NET, está disponible en Linux, macOS y
Windows.
IMPORTANT
Solo debe hacer referencia a servicios desde un origen de confianza. Si agrega referencias desde un origen que no es de
confianza podría poner en peligro la seguridad.
Requisitos previos
dotnet-svcutil 2.x
dotnet-svcutil 1.x
SDK de .NET Core 2.1 o versiones posteriores
Su editor de código favorito
Introducción
En el ejemplo siguiente se le guía por los pasos necesarios para agregar una referencia de servicio web a un
proyecto web de .NET Core e invocar el servicio. Creará una aplicación web de .NET Core denominada HelloSvcutil
y agregará una referencia a un servicio web que implementa el siguiente contrato:
[ServiceContract]
public interface ISayHello
{
[OperationContract]
string Hello(string name);
}
En este ejemplo, se da por hecho que el servicio web se hospedará en la siguiente dirección:
http://contoso.com/SayHello.svc
Desde una ventana de comandos de Windows, Mac OS o Linux, siga estos pasos:
1. Cree un directorio denominado HelloSvcutil para el proyecto y hágalo su directorio actual, como en el
ejemplo siguiente:
mkdir HelloSvcutil
cd HelloSvcutil
2. Cree un nuevo proyecto web de C# en ese directorio mediante el comando dotnet new del modo siguiente:
4. Ejecute el comando dotnet-svcutil para generar el archivo de referencia del servicio web de la siguiente
manera:
dotnet-svcutil 2.x
dotnet-svcutil 1.x
dotnet-svcutil http://contoso.com/SayHello.svc
dotnet restore
2. Busque el nombre de la clase de cliente y la operación que quiera usar. Reference.cs contendrá una clase
que se hereda de System.ServiceModel.ClientBase , con métodos que pueden usarse para llamar a las
operaciones del servicio. En este ejemplo, quiere llamar a la operación Hello del servicio SayHello.
ServiceReference.SayHelloClient es el nombre de la clase de cliente, y tiene un método llamado
HelloAsync que se puede usar para llamar a la operación.
3. Abra el archivo Startup.cs en el editor y agregue una directiva using al espacio de nombres de la
referencia de servicio en la parte superior:
using ServiceReference;
4. Edite el método Configure para invocar el servicio web. Para ello, cree una instancia de la clase que se
hereda de ClientBase y llame al método en el objeto de cliente:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
dotnet run
6. Vaya a la dirección URL indicada en la consola (por ejemplo, http://localhost:5000 ) en el explorador web.
Debería ver los siguientes resultados: "Hello dotnet-svcutil!"
Para obtener una descripción detallada de los parámetros de la herramienta dotnet-svcutil , invoque la
herramienta pasando el parámetro de ayuda del siguiente modo:
dotnet-svcutil 2.x
dotnet-svcutil 1.x
dotnet-svcutil --help
Preguntas y comentarios
Si tiene preguntas o comentarios, abra un problema en GitHub. También puede revisar las preguntas o problemas
que ya se han planteado en el repositorio de WCF en GitHub.
Notas de la versión
Eche un vistazo a las notas de la versión para obtener información actualizada sobre la versión, incluidos los
problemas conocidos.
Información
Paquete NuGet svcutil dotnet
Uso de dotnet-svcutil.xmlserializer en .NET Core
16/09/2020 • 3 minutes to read • Edit Online
Requisitos previos
SDK de .NET Core 2.1 o versiones posteriores
Su editor de código favorito
Puede usar el comando dotnet --info para comprobar qué versiones del SDK de .NET Core y del Runtime ya ha
instalado.
Introducción
Para usar dotnet-svcutil.xmlserializer en una aplicación de consola de .NET Core:
1. Crea un servicio WCF denominado "MyWCFService" mediante la plantilla predeterminada "Aplicación del
servicio WCF" en .NET Framework. Agregue el atributo [XmlSerializerFormat] al método de servicio similar
al siguiente:
[ServiceContract]
public interface IService1
{
[XmlSerializerFormat]
[OperationContract(Action = "http://tempuri.org/IService1/GetData", ReplyAction =
"http://tempuri.org/IService1/GetDataResponse")]
string GetData(int value);
}
2. Cree una aplicación de consola de .NET Core como aplicación cliente de WCF que tenga como destino .NET
Core 2.1 o versiones posteriores. Por ejemplo, cree una aplicación denominada "MyWCFClient" con el
comando siguiente:
Para asegurarse de que el proyecto está destinado a .NET Core 2.1 o una versión posterior, inspeccione el
elemento XML TargetFramework del archivo de proyecto:
<TargetFramework>netcoreapp2.1</TargetFramework>
class Program
{
static void Main(string[] args)
{
var myBinding = new BasicHttpBinding();
var myEndpoint = new EndpointAddress("http://localhost:2561/Service1.svc"); //Fill your
service url here
var myChannelFactory = new ChannelFactory<IService1>(myBinding, myEndpoint);
IService1 client = myChannelFactory.CreateChannel();
string s = client.GetData(1);
((ICommunicationObject)client).Close();
}
}
[ServiceContract]
public interface IService1
{
[XmlSerializerFormat]
[OperationContract(Action = "http://tempuri.org/IService1/GetData", ReplyAction =
"http://tempuri.org/IService1/GetDataResponse")]
string GetData(int value);
}
Al ejecutar el comando se debería agregar una entrada al archivo de proyecto similar a la siguiente:
<ItemGroup>
<DotNetCliToolReference Include="dotnet-svcutil.xmlserializer" Version="1.0.0" />
</ItemGroup>
6. Compile la aplicación ejecutando dotnet build . Si todo se realiza correctamente, se genera un ensamblado
denominado MyWCFClient.XmlSerializers.dll en la carpeta de salida. Si la herramienta no pudo generar el
ensamblado, verá advertencias en la salida de la compilación.
7. Inicie el servicio WCF, por ejemplo, mediante la ejecución de http://localhost:2561/Service1.svc en el
explorador. Después, inicie la aplicación cliente, que se cargará automáticamente y utilizará los
serializadores generados previamente en tiempo de ejecución.
Usar el generador de serializador XML de Microsoft
en .NET Core
16/09/2020 • 5 minutes to read • Edit Online
Este tutorial muestra cómo usar el generador de serializador XML de Microsoft en una aplicación .NET Core de C#.
Este tutorial ayuda a:
Cómo crear una aplicación .NET Core
Cómo agregar una referencia al paquete Microsoft.XmlSerializer.Generator
Cómo editar MyApp.csproj para agregar dependencias
Cómo agregar una clase y un XmlSerializer
Cómo compilar y ejecutar la aplicación
Tal y como sucede con el generador de serializador de XML (sgen.exe) para .NET Framework, el paquete
Microsoft.XmlSerializer.Generator de NuGet es el equivalente para proyectos de .NET Core y .NET Standard. Crea un
ensamblado de serialización de XML para los tipos contenidos en un ensamblado para mejorar el rendimiento de
inicio de la serialización XML al serializar o deserializar objetos de esos tipos con XmlSerializer.
Requisitos previos
Para realizar este tutorial:
SDK de .NET Core 2.1 o versiones posteriores.
Su editor de código favorito.
TIP
¿Es necesario instalar un editor de código? Pruebe Visual Studio.
<ItemGroup>
<PackageReference Include="Microsoft.XmlSerializer.Generator" Version="1.0.0" />
</ItemGroup>
Agregar otra sección ItemGroup para admitir la herramienta CLI de .NET Core
Agregue estas líneas después de la sección ItemGroup que hemos inspeccionado:
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.XmlSerializer.Generator" Version="1.0.0" />
</ItemGroup>
dotnet run
NOTE
dotnet run llama a dotnet build para asegurarse de que los destinos de la compilación se han creado y, después, llama a
dotnet <assembly.dll> para ejecutar la aplicación de destino.
IMPORTANT
Los comandos y los pasos que se muestran en este tutorial para ejecutar la aplicación se usan solo durante el desarrollo. Una
vez que esté listo para implementar la aplicación, eche un vistazo a las diferentes estrategias de implementación para
aplicaciones .NET Core y al comando dotnet publish .
Si todo se realiza correctamente, se genera un ensamblado con el nombre .dll MyApp.XmlSerializers.dll en la
carpeta de salida.
¡Enhorabuena! Acaba de:
Crear una aplicación .NET Core.
Agregar una referencia al paquete Microsoft.XmlSerializer.Generator.
Editar MyApp.csproj para agregar dependencias.
Agregue una clase y un XmlSerializer.
Compilar y ejecutar la aplicación.
Recursos relacionados
Introducción a la serialización XML
Serialización con XmlSerializer (C#)
Cómo: Serializar con XmlSerializer (Visual Basic)
¿Qué herramientas de diagnóstico están disponibles
en .NET Core?
16/09/2020 • 5 minutes to read • Edit Online
El comportamiento del software no siempre es el esperado, pero .NET Core tiene herramientas y API que lo
ayudarán a diagnosticar estos problemas de manera rápida y eficaz.
Este artículo lo ayuda a encontrar las distintas herramientas que necesita.
Depuradores administrados
Los depuradores administrados le permiten interactuar con el programa. Pausar, ejecutar de manera incremental,
examinar y reanudar le proporcionan información sobre el comportamiento del código. Un depurador es la
primera opción para diagnosticar problemas funcionales que se pueden reproducir de manera fácil.
Registro y seguimiento
El registro y seguimiento son técnicas relacionadas. Hacen referencia a la instrumentación del código para crear
archivos de registro. Los archivos registran los detalles de lo que hace un programa. Estos detalles se pueden usar
para diagnosticar los problemas más complejos. Cuando se combinan con marcas de tiempo, estas técnicas
también son valiosas para investigaciones de rendimiento.
Pruebas unitarias
Las pruebas unitarias son un componente clave de la integración continua y la implementación de software de alta
calidad. Las pruebas unitarias están diseñadas para brindarle una advertencia temprana cuando se daña algo.
Los depuradores permiten a los programas pausarse o ejecutarse paso a paso. Cuando se pausa, se puede ver el
estado actual del proceso. Al recorrer las secciones clave, se entiende mejor el código y el motivo por el que genera
determinado resultado.
Microsoft proporciona depuradores para código administrado en Visual Studio y Visual Studio Code .
Out-of-proc
dotnet-counters
EventSource
EventPipe
dotnet-trace
EventCounter
EventCounter In-proc
EventCounter EventListener
Contadores disponibles
En los distintos paquetes de .NET, las métricas básicas sobre la recolección de elementos no utilizados (GC), Just-in-
Time (JIT), los ensamblados, las excepciones, los subprocesos, las redes y las solicitudes web se publican mediante
EventCounters.
Contadores "System.Runtime"
Los siguientes contadores se publican como parte del entorno de ejecución de .NET y se mantienen en
RuntimeEventSource.cs .
C O N TA DO R DESC RIP C IÓ N
GC Heap Size ( gc-heap-size ) Número de bytes que se considera que están asignados según
GC.GetTotalMemory(Boolean)
Gen 0 GC Count ( gen-0-gc-count ) Número de veces que se ha producido una GC para Gen 0
Gen 1 GC Count ( gen-1-gc-count ) Número de veces que se ha producido una GC para Gen 1
Gen 2 GC Count ( gen-2-gc-count ) Número de veces que se ha producido una GC para Gen 2
Monitor Lock Contention Count ( Número de veces que ha habido contención al intentar tomar
monitor-lock-contention-count ) el bloqueo del monitor, según Monitor.LockContentionCount
Number of Active Timers ( active-timer-count ) Número de instancias de Timer que están activas actualmente,
según Timer.ActiveCount
ThreadPool Completed Work Item Count ( Número de elementos de trabajo que se han procesado hasta
threadpool-completed-items-count ) ahora en ThreadPool
ThreadPool Thread Count ( threadpool-thread-count ) Número de subprocesos del grupo de subprocesos que
existen actualmente en ThreadPool, según
ThreadPool.ThreadCount
Working Set ( working-set ) Cantidad de memoria física asignada al contexto del proceso
en un momento dado según Environment.WorkingSet
Contadores "Microsoft.AspNetCore.Hosting"
Los siguientes contadores se publican como parte de ASP.NET Core y se mantienen en HostingEventSource.cs .
C O N TA DO R DESC RIP C IÓ N
Current Requests ( current-requests ) Número total de solicitudes que se han iniciado, pero que aún
no se han detenido
C O N TA DO R DESC RIP C IÓ N
Failed Requests ( failed-requests ) Número total de solicitudes erróneas que se han producido
durante la vida de la aplicación
Request Rate ( requests-per-second ) Número de solicitudes que se han producido por segundo
Total Requests ( total-requests ) Número total de solicitudes que se han producido durante la
vida de la aplicación
Contadores "Microsoft.AspNetCore.Http.Connections"
Los siguientes contadores se publican como parte de ASP.NET Core SignalR y se mantienen en
HttpConnectionsEventSource.cs .
C O N TA DO R DESC RIP C IÓ N
Current Connections ( current-connections ) Número de conexiones activas que se han iniciado, pero que
aún no se han detenido
Total Connections Started ( connections-started ) Número total de conexiones que se han iniciado
Total Connections Stopped ( connections-stopped ) Número total de conexiones que se han detenido
Total Connections Timed Out ( connections-timed-out ) Número total de conexiones cuyo tiempo de espera se ha
agotado
C O N TA DO R DESC RIP C IÓ N
Failed TLS Handshakes ( failed-tls-handshakes ) Número total de protocolos de enlace TLS erróneos
TLS Handshake Rate ( tls-handshakes-per-second ) Número de protocolos de enlace TLS por segundo
C O N TA DO R DESC RIP C IÓ N
Total TLS Handshakes ( total-tls-handshakes ) Número total de protocolos de enlace TLS con el servidor web
Implementación de un EventSource
En el código siguiente se implementa un ejemplo EventSource expuesto como proveedor
"Sample.EventCounter.Minimal" con nombre. Este origen contiene un EventCounter que representa el tiempo de
procesamiento de la solicitud. Un contador de este tipo tiene un nombre (es decir, su identificador único en el
origen) y un nombre para mostrar, ambos usados por herramientas de escucha como dotnet-counter.
using System.Diagnostics.Tracing;
[EventSource(Name = "Sample.EventCounter.Minimal")]
public sealed class MinimalEventCounterSource : EventSource
{
public static readonly MinimalEventCounterSource Log = new MinimalEventCounterSource();
base.Dispose(disposing);
}
}
Use dotnet-counters ps para mostrar una lista de los procesos de .NET que se pueden supervisar:
dotnet-counters ps
1398652 dotnet C:\Program Files\dotnet\dotnet.exe
1399072 dotnet C:\Program Files\dotnet\dotnet.exe
1399112 dotnet C:\Program Files\dotnet\dotnet.exe
1401880 dotnet C:\Program Files\dotnet\dotnet.exe
1400180 sample-counters C:\sample-counters\bin\Debug\netcoreapp3.1\sample-counters.exe
Pase el nombre del EventSource al modificador counter_list para iniciar la supervisión del contador:
[Samples-EventCounterDemos-Minimal]
Request Processing Time (ms) 0.445
using System.Diagnostics.Tracing;
[EventSource(Name = "Sample.EventCounter.Conditional")]
public sealed class ConditionalEventCounterSource : EventSource
{
public static readonly ConditionalEventCounterSource Log = new ConditionalEventCounterSource();
private ConditionalEventCounterSource() { }
base.Dispose(disposing);
}
}
TIP
Los contadores condicionales son contadores de los que se crean instancias de forma condicional, una microoptimización. El
entorno de ejecución adopta este patrón para los escenarios en los que normalmente no se usan contadores, para ahorrar
una fracción de un milisegundo.
Contadores de ejemplo del entorno de ejecución de .NET Core
Hay muchas implementaciones de ejemplo excelentes del entorno de ejecución de .NET Core. Esta es la
implementación del entorno de ejecución del contador que realiza el seguimiento del tamaño del espacio de
trabajo de la aplicación.
PollingCounter comunica la cantidad actual de memoria física asignada al proceso (espacio de trabajo) de la
aplicación, ya que captura una métrica en un momento dado. La devolución de llamada del sondeo de un valor es
la expresión lambda proporcionada, que es simplemente una llamada a la API System.Environment.WorkingSet.
DisplayName y DisplayUnits son propiedades opcionales que se pueden establecer para ayudar al lado del
consumidor del contador a mostrar el valor con más claridad. Por ejemplo, dotnet-counters usa estas propiedades
para mostrar la versión más descriptiva de los nombres de contador.
IMPORTANT
Las propiedades DisplayName no están traducidas.
En el caso de PollingCounter e IncrementingPollingCounter, no es necesario hacer nada más. Ambos sondean los
valores por sí mismos en un intervalo solicitado por el consumidor.
Este es un ejemplo de un contador de entorno de ejecución implementado mediante IncrementingPollingCounter.
NOTE
dotnet-counters no usa DisplayRateTimeScale, y no es necesario que las escuchas de eventos lo usen.
Hay más implementaciones de contador que se pueden usar como referencia en el repositorio del entorno de
ejecución de .NET.
Simultaneidad
TIP
La API de EventCounters no garantiza la seguridad para subprocesos. Cuando varios subprocesos llaman a los delegados
pasados a instancias PollingCounter o IncrementingPollingCounter, es responsabilidad suya garantizar la seguridad para
subprocesos de los delegados.
Por ejemplo, considere el siguiente EventSource para realizar un seguimiento de las solicitudes.
using System;
using System.Diagnostics.Tracing;
base.Dispose(disposing);
}
}
Uso de EventCounters
Hay dos formas principales de usar EventCounters, en proceso o fuera de proceso. El uso de EventCounters se
puede clasificar en tres capas de distintas tecnologías de uso.
Transporte de eventos en una secuencia sin formato a través de ETW o EventPipe:
Las API de ETW están incluidas en el sistema operativo Windows y se puede acceder a EventPipe como
una API de .NET o el protocolo IPC de diagnóstico.
Descodificación del flujo de eventos binario en eventos:
La biblioteca TraceEvent controla los formatos de flujo de ETW y EventPipe.
Herramientas de línea de comandos y GUI:
Herramientas como PerfView (ETW o EventPipe), dotnet-counters (solo EventPipe) y dotnet-monitor (solo
EventPipe).
Uso fuera de proceso
El uso de EventCounters fuera de proceso es un enfoque muy común. Puede usar dotnet-counters para
consumirlos a modo multiplataforma a través de un EventPipe. La herramienta dotnet-counters es una
herramienta global de CLI de dotnet multiplataforma que se puede usar para supervisar los valores de los
contadores. Para obtener información sobre cómo usar dotnet-counters para supervisar los contadores, vea
dotnet-counters o trabaje con el tutorial Medición del rendimiento mediante EventCounters.
dotnet-trace
La herramienta dotnet-trace se puede usar para consumir los datos del contador a través de EventPipe. Este es un
ejemplo de uso de dotnet-trace para recopilar datos del contador.
Para obtener más información sobre cómo recopilar valores de un contador a lo largo del tiempo, vea la
documentación de dotnet-trace.
Azure Application Insights
Azure Monitor puede usar EventCounters, en concreto Azure Application Insights. Los contadores se pueden
agregar y quitar; además, el usuario puede especificar contadores personalizados o contadores conocidos. Para
obtener más información, vea Personalización de los contadores que se van a recopilar.
dotnet-monitor
dotnet-monitor es una herramienta experimental que facilita el acceso a la información de diagnóstico de un
proceso de .NET. Esta herramienta sirve como superconjunto de todas las herramientas de diagnóstico. Además de
ofrecer seguimientos, permite supervisar métricas, así como recopilar volcados de memoria y de memoria GC. Se
distribuye tanto como herramienta de la CLI como imagen de Docker. Expone una API de REST, y la recopilación de
artefactos de diagnóstico se produce a través de llamadas REST.
Para obtener más información, vea Presentación de dotnet-monitor, una herramienta experimental.
Uso en proceso
Puede usar los valores de un contador por medio de la API EventListener. EventListener es una forma en proceso
de usar los eventos escritos por todas las instancias de un EventSource en la aplicación. Para obtener más
información sobre cómo usar la API EventListener , vea EventListener.
En primer lugar, es necesario habilitar el EventSource que genera el valor del contador. Invalide el método
EventListener.OnEventSourceCreated para obtener una notificación cuando se cree un EventSource y, si es el
EventSource correcto con los EventCounters, puede llamar a EventListener.EnableEvents en él. Esta es una
invalidación de ejemplo:
Código de ejemplo
Esta es una clase EventListener de ejemplo que imprime todos los nombres y valores de contador del EventSource
del entorno de ejecución de .NET, para publicar sus contadores internos ( System.Runtime ) en algún intervalo.
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
Consulte también
dotnet-counters
dotnet-trace
EventCounter
EventListener
EventSource
Registro y seguimiento de .NET Core
18/03/2020 • 7 minutes to read • Edit Online
El registro y el seguimiento son en realidad dos nombres para la misma técnica. Esta sencilla técnica se ha usado
desde el inicio de la era de la informática. Simplemente implica instrumentar una aplicación para escribir la salida
que se va a consumir más adelante.
Instalación de dotnet-counters
Para instalar la versión de lanzamiento más reciente del paquete NuGet de dotnet-counters , use el
comando dotnet tool install:
Sinopsis
dotnet-counters [-h|--help] [--version] <command>
Descripción
dotnet-counters es una herramienta de supervisión de rendimiento diseñada para la investigación del
rendimiento y la supervisión del estado de primer nivel ad hoc. Puede observar los valores del contador
de rendimiento que se publican a través de la API EventCounter. Por ejemplo, se pueden supervisar
rápidamente cosas como el uso de la CPU o la velocidad de las excepciones que se producen en la
aplicación .NET Core para ver si hay algo sospechoso antes de profundizar en una investigación de
rendimiento más seria mediante PerfView o dotnet-trace .
Opciones
--version
Comandos
C O M A N DO
dotnet-counters collect
dotnet-counters list
dotnet-counters monitor
dotnet-counters ps
dotnet-counters collect
Recopila periódicamente los valores de contador seleccionados y los exporta a un formato de archivo
especificado para su posterior procesamiento.
Sinopsis
Opciones
-p|--process-id <PID>
Una lista de contadores separados por espacios. Los contadores pueden ser
provider_name[:counter_name] especificados. Si provider_name se usa sin un elemento
counter_name calificado, se mostrarán todos los contadores. Para descubrir los nombres del
proveedor y del contador, use el comando dotnet-counters list.
--format <csv|json>
dotnet-counters list
Muestra una lista de nombres y descripciones de contador, agrupada por proveedor.
Sinopsis
Ejemplo
> dotnet-counters list
Showing well-known counters only. Specific processes may support additional counters.
System.Runtime
cpu-usage Amount of time the process has utilized the CPU
(ms)
working-set Amount of working set used by the process (MB)
gc-heap-size Total heap size reported by the GC (MB)
gen-0-gc-count Number of Gen 0 GCs / min
gen-1-gc-count Number of Gen 1 GCs / min
gen-2-gc-count Number of Gen 2 GCs / min
time-in-gc % time in GC since the last GC
gen-0-size Gen 0 Heap Size
gen-1-size Gen 1 Heap Size
gen-2-size Gen 2 Heap Size
loh-size LOH Heap Size
alloc-rate Allocation Rate
assembly-count Number of Assemblies Loaded
exception-count Number of Exceptions / sec
threadpool-thread-count Number of ThreadPool Threads
monitor-lock-contention-count Monitor Lock Contention Count
threadpool-queue-length ThreadPool Work Items Queue Length
threadpool-completed-items-count ThreadPool Completed Work Items Count
active-timer-count Active Timers Count
Microsoft.AspNetCore.Hosting
requests-per-second Request rate
total-requests Total number of requests
current-requests Current number of requests
failed-requests Failed number of requests
NOTE
Los contadores de Microsoft.AspNetCore.Hosting se muestran cuando hay procesos identificados que
admiten estos contadores, por ejemplo, cuando una aplicación ASP.NET Core se está ejecutando en el equipo host.
dotnet-counters monitor
Muestra la actualización periódica de los valores de los contadores seleccionados.
Sinopsis
Opciones
-p|--process-id <PID>
Una lista de contadores separados por espacios. Los contadores pueden ser
provider_name[:counter_name] especificados. Si provider_name se usa sin un elemento
counter_name calificado, se mostrarán todos los contadores. Para descubrir los nombres del
proveedor y del contador, use el comando dotnet-counters list.
Ejemplos
Supervisión de todos los contadores de System.Runtime con un intervalo de actualización de 3
segundos:
Supervisión de los valores EventCounter del elemento EventSource definido por el usuario. Para
obtener más información, consulte Tutorial: Medición del rendimiento mediante EventCounters en
.NET Core.
dotnet-counters ps
Muestra una lista de los procesos de dotnet que se pueden supervisar.
Sinopsis
dotnet-counters ps [-h|--help]
Ejemplo
> dotnet-counters ps
NOTE
dotnet-dump no se admite en macOS.
Instalación de dotnet-dump
Para instalar la versión de lanzamiento más reciente del paquete NuGet de dotnet-dump , use el comando
dotnet tool install:
Sinopsis
dotnet-dump [-h|--help] [--version] <command>
Descripción
La herramienta global dotnet-dump es una forma de recopilar y analizar los volcados de Windows y Linux sin
necesidad de un depurador nativo implicado, como lldb en Linux. Esta herramienta es importante en
plataformas como Alpine Linux, donde no está disponible una versión de lldb totalmente operativa. La
herramienta dotnet-dump permite ejecutar comandos SOS para analizar bloqueos y el recolector de elementos
no utilizados (GC), pero no es un depurador nativo, por lo que no se admiten elementos como la visualización
de marcos de pila nativos.
Opciones
--version
Comandos
C O M A N DO
dotnet-dump collect
C O M A N DO
dotnet-dump analyze
dotnet-dump collect
Captura un volcado de un proceso.
Sinopsis
Opciones
-h|--help
Especifica el tipo de volcado, que determina los tipos de información que se recopilan del proceso. Hay
dos tipos:
Heap : un volcado grande y relativamente completo que contiene listas de módulos, listas de
subprocesos, todas las pilas, información de excepción, información de control y toda la memoria
excepto las imágenes asignadas.
Mini : un volcado pequeño que contiene listas de módulos, listas de subprocesos, información de
excepción y todas las pilas.
Si no se especifica, el valor predeterminado es Heap .
-o|--output <output_dump_path>
La ruta de acceso completa y el nombre de archivo donde se debe escribir el volcado recopilado.
Si no se especifica:
El valor predeterminado es .\dump_AAAAMMDD_HHMMSS.dmp en Windows.
El valor predeterminado es ./core_AAAAMMDD_HHMMSS en Linux.
AAAAMMDD es año/mes/día y HHMMSS es hora/minuto/segundo.
--diag
dotnet-dump analyze
Inicia un shell interactivo para explorar un volcado. El shell acepta varios comandos SOS.
Sinopsis
Argumentos
<dump_path>
histobjfind <arguments> Muestra todas las entradas de registro que hacen referencia
a un objeto en la dirección especificada.
Uso de dotnet-dump
El primer paso es recopilar un volcado. Este paso se puede omitir si ya se ha generado un volcado principal. El
sistema operativo o la característica de generación de volcado integrada del runtime de .NET Core pueden crear
volcados principales.
Esta acción abre una sesión interactiva que acepta comandos como los siguientes:
> clrstack
OS Thread Id: 0x573d (0)
Child SP IP Call Site
00007FFD28B42C58 00007fb22c1a8ed9 [HelperMethodFrame_PROTECTOBJ: 00007ffd28b42c58]
System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean, Boolean)
00007FFD28B42DD0 00007FB1B1334F67 System.Reflection.RuntimeMethodInfo.Invoke(System.Object,
System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[],
System.Globalization.CultureInfo) [/root/coreclr/src/mscorlib/src/System/Reflection/RuntimeMethodInfo.cs @
472]
00007FFD28B42E20 00007FB1B18D33ED SymbolTestApp.Program.Foo4(System.String)
[/home/mikem/builds/SymbolTestApp/SymbolTestApp/SymbolTestApp.cs @ 54]
00007FFD28B42ED0 00007FB1B18D2FC4 SymbolTestApp.Program.Foo2(Int32, System.String)
[/home/mikem/builds/SymbolTestApp/SymbolTestApp/SymbolTestApp.cs @ 29]
00007FFD28B42F00 00007FB1B18D2F5A SymbolTestApp.Program.Foo1(Int32, System.String)
[/home/mikem/builds/SymbolTestApp/SymbolTestApp/SymbolTestApp.cs @ 24]
00007FFD28B42F30 00007FB1B18D168E SymbolTestApp.Program.Main(System.String[])
[/home/mikem/builds/SymbolTestApp/SymbolTestApp/SymbolTestApp.cs @ 19]
00007FFD28B43210 00007fb22aa9cedf [GCFrame: 00007ffd28b43210]
00007FFD28B43610 00007fb22aa9cedf [GCFrame: 00007ffd28b43610]
StackTraceString: <none>
HResult: 80131604
En Microsoft SDK de .NET Core imágenes de Docker de SDK de Linux, algunos comandos dotnet-dump pueden
producir la siguiente excepción:
Vea también
Entrada de blog sobre la recopilación y el análisis de volcados de memoria
Herramienta de análisis del montón (dotnet-gcdump)
Herramienta de análisis del montón (dotnet-gcdump)
16/09/2020 • 5 minutes to read • Edit Online
Instalación de dotnet-gcdump
Para instalar la versión de lanzamiento más reciente del paquete NuGet de dotnet-gcdump , use el comando dotnet
tool install:
Sinopsis
dotnet-gcdump [-h|--help] [--version] <command>
Descripción
La herramienta global dotnet-gcdump permite recopilar volcados de memoria de GC (recolector de elementos no
utilizados) de procesos de .NET dinámicos. Usa la tecnología EventPipe, que es una alternativa multiplataforma a
ETW en Windows. Los volcados de memoria de GC se crean desencadenando un GC en el proceso de destino,
activando eventos especiales y regenerando el gráfico de raíces de objeto a partir del flujo de eventos. Este
proceso permite recopilar volcados de memoria de GC mientras el proceso se está ejecutando y con una
sobrecarga mínima. Estos volcados de memoria son útiles para varios escenarios:
Comparar el número de objetos del montón en varios puntos en el tiempo.
Analizar raíces de objetos (responder a preguntas como "¿qué sigue teniendo una referencia a este tipo?").
Recopilar estadísticas generales sobre los recuentos de objetos en el montón.
Ver el volcado de memoria de GC capturado por dotnet-gcdump
En Windows, los archivos .gcdump se pueden ver en PerfView o en Visual Studio para analizarlos. Actualmente, no
es posible abrir un archivo .gcdump en plataformas que no sean de Windows.
Puede recopilar varios archivos .gcdump y abrirlos simultáneamente en Visual Studio para obtener una
comparativa.
Opciones
--version
dotnet-gcdump collect
Recopila un volcado de memoria de GC de un proceso que se está ejecutando actualmente.
Sinopsis
Opciones
-h|--help
Ruta de acceso donde se deben escribir los volcados de memoria de GC recopilados. El valor
predeterminado es .\AAAAMMDD_HHMMSS_<pid>.gcdump.
-v|--verbose
Dejar de recopilar el volcado de memoria de GC si tarda más de la cantidad de segundos indicada. El valor
predeterminado es 30.
-n|--name <name>
dotnet-gcdump ps
Enumera los procesos de dotnet de los que se pueden recopilar volcados de memoria de GC.
Sinopsis
dotnet-gcdump ps
Opciones
-h|--help
Solución de problemas
No hay información de tipo en gcdump.
Antes de .NET Core 3.1, se producía un problema por el que una memoria caché de tipos no se borraba
entre varios gcdump cuando se invocaba con EventPipe. El resultado fue que los eventos necesarios para
determinar la información de tipo no se enviaban al segundo gcdump y a los siguientes. Esto se corrigió en
la versión preliminar 2 de .NET Core 3.1.
Los tipos COM y estáticos no se encuentran en el volcado de memoria de GC.
Antes de la versión preliminar 2 de .NET Core 3.1, se producía un problema por el que los tipos estáticos y
COM no se enviaban cuando se invocaba el volcado de memoria de GC a través de EventPipe. Esto se ha
corregido en la versión preliminar 2 de .NET Core 3.1.
Utilidad de análisis de rendimiento dotnet-trace
16/09/2020 • 8 minutes to read • Edit Online
Instalación de dotnet-trace
Instale el paquete NuGet dotnet-trace con el comando dotnet tool install:
Sinopsis
dotnet-trace [-h, --help] [--version] <command>
Descripción
La herramienta dotnet-trace :
Es una herramienta de .NET Core para varias plataformas.
Habilita la recolección de seguimientos de .NET Core de un proceso en ejecución sin un generador de
perfiles nativo.
Se basa en la tecnología EventPipe multiplataforma del entorno de ejecución de .NET Core.
Ofrece la misma experiencia en Windows, Linux o macOS.
Opciones
-h|--help
Comandos
C O M A N DO
dotnet-trace collect
dotnet-trace convert
dotnet-trace ps
dotnet-trace list-profiles
dotnet-trace collect
Recopila un seguimiento de diagnóstico de un proceso en ejecución.
Sinopsis
Opciones
--buffersize <size>
Establece el tamaño del búfer circular en memoria, en megabytes. Valor predeterminado de 256 MB.
--clreventlevel <clreventlevel>
Ruta de acceso de salida para los datos de seguimiento recopilados. Si no se especifica, el valor
predeterminado es trace.nettrace .
-p|--process-id <PID>
Lista separada por comas de proveedores de EventPipe que se van a habilitar. Estos proveedores
complementan a los proveedores implícitos en --profile <profile-name> . Si hay alguna incoherencia
para un proveedor determinado, esta configuración tiene prioridad sobre la configuración implícita del
perfil.
Esta lista de proveedores tiene el siguiente formato:
Provider[,Provider]
Provider tiene el formato: KnownProviderName[:Flags[:Level][:KeyValueArgs]] .
KeyValueArgs tiene el formato: [key1=value1][;key2=value2] .
dotnet-trace convert
dotnet-trace convert
Convierte los seguimientos de nettrace en formatos alternativos para usarlos con herramientas de análisis de
seguimiento alternativas.
Sinopsis
Argumentos
<input-filename>
dotnet-trace ps
Enumera los procesos de dotnet de los que se pueden recopilar seguimientos.
Sinopsis
dotnet-trace ps [-h|--help]
dotnet-trace list-profiles
Muestra los perfiles de seguimiento pregenerados con una descripción de los proveedores y filtros que hay en
cada perfil.
Sinopsis
NOTE
El tiempo de ejecución de .NET Core genera seguimientos en el formato nettrace . Los seguimientos se convierten a
formato speedscope (si se especifica) una vez completado el seguimiento. Dado que algunas conversiones pueden
provocar la pérdida de datos, el archivo nettrace original se conserva junto al archivo convertido.
El comando anterior indica a los contadores en tiempo de ejecución que se deben notificar una vez por
segundo para la supervisión ligera del estado. Reemplazar EventCounterIntervalSec=1 por un valor mayor (por
ejemplo, 60) permite recopilar un seguimiento más pequeño con menos granularidad en los datos de
contador.
El comando siguiente reduce la sobrecarga y el tamaño de seguimiento más que el anterior:
Proveedores .NET
El runtime de .NET Core admite los siguientes proveedores .NET. .NET Core usa las mismas palabras clave para
habilitar los seguimientos de Event Tracing for Windows (ETW) y EventPipe .
N O M B RE DEL P RO VEEDO R IN F O RM A C IÓ N
Requisitos previos
En el tutorial se usa:
SDK de .NET Core 3.1 o una versión posterior
dotnet-counters para supervisar contadores de eventos.
Una aplicación de destino de depuración de ejemplo que se va a diagnosticar.
Implementación de un EventSource
En el caso de los eventos que se producen cada pocos milisegundos, se recomienda que la sobrecarga por evento
sea baja (menos de un milisegundo). De lo contrario, el impacto sobre el rendimiento es considerable. El registro
de un evento significa que se va a escribir algo en el disco. Si el disco no es lo suficientemente rápido, se pierden
eventos. Necesita una solución que no sea el registro del propio evento.
Al trabajar con un gran número de eventos, conocer la medida por evento tampoco es útil. La mayor parte del
tiempo, todo lo que se necesita son algunas estadísticas. Por lo tanto, podría obtener las estadísticas dentro del
propio proceso y luego escribir un evento de vez en cuando para comunicar las estadísticas, que es lo que hace
EventCounter.
A continuación se muestra un ejemplo de cómo implementar un System.Diagnostics.Tracing.EventSource. Cree un
nuevo archivo denominado MinimalEventCounterSource.cs y use el fragmento de código como su origen:
using System.Diagnostics.Tracing;
[EventSource(Name = "Sample.EventCounter.Minimal")]
public sealed class MinimalEventCounterSource : EventSource
{
public static readonly MinimalEventCounterSource Log = new MinimalEventCounterSource();
base.Dispose(disposing);
}
}
using System.Diagnostics;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc.Filters;
namespace DiagnosticScenarios
{
public class LogRequestTimeFilterAttribute : ActionFilterAttribute
{
readonly Stopwatch _stopwatch = new Stopwatch();
MinimalEventCounterSource.Log.Request(
context.HttpContext.Request.GetDisplayUrl(), _stopwatch.ElapsedMilliseconds);
}
}
}
El filtro de acción inicia Stopwatch cuando comienza la solicitud y detiene cuando termina, capturando el tiempo
transcurrido. Los milisegundos totales se registran en la instancia singleton MinimalEventCounterSource . Para
aplicar este filtro, debe agregarlo a la colección de filtros. En el archivo Startup.cs, actualice el método
ConfigureServices al incluir este filtro.
dotnet-counters ps
Con el identificador de proceso de la salida del comando dotnet-counters ps , puede empezar a supervisar el
contador de eventos con el siguiente comando dotnet-counters monitor :
Mientras se ejecuta el comando dotnet-counters monitor , mantenga presionada la tecla F5 en el explorador para
iniciar la emisión de solicitudes continuas al punto de conexión https://localhost:5001/api/values . Tras unos
segundos, detenga al presionar q.
[Microsoft.AspNetCore.Hosting]
Request Rate / 1 sec 9
Total Requests 134
[System.Runtime]
CPU Usage (%) 13
[Sample.EventCounter.Minimal]
Request Processing Time (ms) 34.5
El comando dotnet-counters monitor es excelente para la supervisión activa. Pero se recomienda recopilar estas
métricas de diagnóstico para el procesamiento y el análisis posteriores. Para ello,use el comando
dotnet-counters collect . El comando modificador collect es similar al comando monitor , pero acepta algunos
parámetros adicionales. Puede especificar el nombre de archivo de salida y el formato que quiera. En el caso de un
archivo JSON denominado diagnostics.json, use el siguiente comando:
Una vez más, mientras se ejecuta el comando, mantenga presionada la tecla F5 en el explorador para iniciar la
emisión de solicitudes continuas al punto de conexión https://localhost:5001/api/values . Tras unos segundos,
detenga al presionar q. Se escribe el archivo diagnostics.json. Pero no se aplica sangría al archivo JSON escrito;
para mejorar la legibilidad, la sangría se aplica aquí.
{
"TargetProcess": "DiagnosticScenarios",
"StartTime": "8/5/2020 3:02:45 PM",
"Events": [
{
"timestamp": "2020-08-05 15:02:47Z",
"provider": "System.Runtime",
"name": "CPU Usage (%)",
"counterType": "Metric",
"value": 0
},
{
"timestamp": "2020-08-05 15:02:47Z",
"provider": "Microsoft.AspNetCore.Hosting",
"name": "Request Rate / 1 sec",
"counterType": "Rate",
"value": 0
},
{
"timestamp": "2020-08-05 15:02:47Z",
"provider": "Microsoft.AspNetCore.Hosting",
"name": "Total Requests",
"counterType": "Metric",
"value": 134
},
{
"timestamp": "2020-08-05 15:02:47Z",
"provider": "Sample.EventCounter.Minimal",
"name": "Request Processing Time (ms)",
"counterType": "Metric",
"value": 0
},
{
"timestamp": "2020-08-05 15:02:47Z",
"provider": "System.Runtime",
"name": "CPU Usage (%)",
"counterType": "Metric",
"value": 0
},
{
"timestamp": "2020-08-05 15:02:48Z",
"provider": "Microsoft.AspNetCore.Hosting",
"name": "Request Rate / 1 sec",
"counterType": "Rate",
"value": 0
},
{
"timestamp": "2020-08-05 15:02:48Z",
"provider": "Microsoft.AspNetCore.Hosting",
"name": "Total Requests",
"counterType": "Metric",
"value": 134
},
{
"timestamp": "2020-08-05 15:02:48Z",
"provider": "Sample.EventCounter.Minimal",
"name": "Request Processing Time (ms)",
"counterType": "Metric",
"value": 0
},
{
"timestamp": "2020-08-05 15:02:48Z",
"provider": "System.Runtime",
"name": "CPU Usage (%)",
"counterType": "Metric",
"value": 0
"value": 0
},
{
"timestamp": "2020-08-05 15:02:50Z",
"provider": "Microsoft.AspNetCore.Hosting",
"name": "Request Rate / 1 sec",
"counterType": "Rate",
"value": 0
},
{
"timestamp": "2020-08-05 15:02:50Z",
"provider": "Microsoft.AspNetCore.Hosting",
"name": "Total Requests",
"counterType": "Metric",
"value": 134
},
{
"timestamp": "2020-08-05 15:02:50Z",
"provider": "Sample.EventCounter.Minimal",
"name": "Request Processing Time (ms)",
"counterType": "Metric",
"value": 0
}
]
}
Pasos siguientes
EventCounters
Depuración de una fuga de memoria en .NET Core
16/09/2020 • 9 minutes to read • Edit Online
Requisitos previos
En el tutorial se usa:
SDK de .NET Core 3.1 o una versión posterior
dotnet-trace para mostrar procesos.
dotnet-counters para comprobar el uso de memoria administrada.
dotnet-dump para recopilar y analizar un archivo de volcado de memoria.
Una aplicación de destino de depuración de ejemplo que se va a diagnosticar.
En el tutorial se da por supuesto que el ejemplo y las herramientas están instalados y listos para usarse.
dotnet run
En una consola independiente, busque el identificador del proceso mediante la herramienta dotnet-trace:
dotnet-trace ps
4807 DiagnosticScena
/home/user/git/samples/core/diagnostics/DiagnosticScenarios/bin/Debug/netcoreapp3.0/DiagnosticScenarios
[System.Runtime]
# of Assemblies Loaded 118
% Time in GC (since last GC) 0
Allocation Rate (Bytes / sec) 37,896
CPU Usage (%) 0
Exceptions / sec 0
GC Heap Size (MB) 4
Gen 0 GC / sec 0
Gen 0 Size (B) 0
Gen 1 GC / sec 0
Gen 1 Size (B) 0
Gen 2 GC / sec 0
Gen 2 Size (B) 0
LOH Size (B) 0
Monitor Lock Contention Count / sec 0
Number of Active Timers 1
ThreadPool Completed Work Items / sec 10
ThreadPool Queue Length 0
ThreadPool Threads Count 1
Working Set (MB) 83
Puede ver que la memoria de montón administrado es de 4 MB justo después del inicio.
Ahora, visite la dirección URL https://localhost:5001/api/diagscenario/memleak/20000 .
Observe que el uso de memoria ha aumentado a 30 MB.
Al observar el uso de memoria, puede indicar con seguridad el aumento o la fuga de memoria. El siguiente paso
consiste en recopilar los datos adecuados para el análisis de memoria.
Generación de un volcado de memoria
Al analizar posibles fugas de memoria, debe tener acceso al montón de memoria de la aplicación. A continuación,
puede analizar el contenido de la memoria. Al observarse las relaciones entre los objetos, se crean teorías de por
qué no se libera la memoria. Un origen de datos de diagnóstico habitual es un volcado de memoria en Windows o
el volcado de memoria principal equivalente en Linux. Para generar un volcado de memoria de una aplicación .NET
Core, puede usar la herramienta dotnet-dump).
Con el destino de depuración de ejemplo iniciado anteriormente, ejecute el siguiente comando para generar un
volcado de memoria principal de Linux:
Donde core_20190430_185145 es el nombre del volcado de memoria principal que desea analizar.
NOTE
Si ve un error que indica que no se encuentra libdl.so, es posible que tenga que instalar el paquete libc6-dev. Para más
información, consulte Requisitos previos para .NET Core en Linux.
Se le mostrará un mensaje en el que puede escribir comandos SOS. Normalmente, lo primero que desea ver es el
estado general del montón administrado:
Statistics:
MT Count TotalSize Class Name
...
00007f6c1eeefba8 576 59904 System.Reflection.RuntimeMethodInfo
00007f6c1dc021c8 1749 95696 System.SByte[]
00000000008c9db0 3847 116080 Free
00007f6c1e784a18 175 128640 System.Char[]
00007f6c1dbf5510 217 133504 System.Object[]
00007f6c1dc014c0 467 416464 System.Byte[]
00007f6c21625038 6 4063376 testwebapi.Controllers.Customer[]
00007f6c20a67498 200000 4800000 testwebapi.Controllers.Customer
00007f6c1dc00f90 206770 19494060 System.String
Total 428516 objects
Aquí puede ver que la mayoría de los objetos son objetos String o Customer .
Puede volver a usar el comando dumpheap con la tabla del método (MT) para obtener una lista de todas las
instancias de String :
> dumpheap -mt 00007faddaa50f90
Address MT Size
...
00007f6ad09421f8 00007faddaa50f90 94
...
00007f6ad0965b20 00007f6c1dc00f90 80
00007f6ad0965c10 00007f6c1dc00f90 80
00007f6ad0965d00 00007f6c1dc00f90 80
00007f6ad0965df0 00007f6c1dc00f90 80
00007f6ad0965ee0 00007f6c1dc00f90 80
Statistics:
MT Count TotalSize Class Name
00007f6c1dc00f90 206770 19494060 System.String
Total 206770 objects
Ahora puede usar el comando gcroot en una instancia de System.String para ver cómo y por qué se considera
raíz el objeto. Tenga paciencia, ya que este comando tarda varios minutos con un montón de 30 MB:
Thread 3f68:
00007F6795BB58A0 00007F6C1D7D0745 System.Diagnostics.Tracing.CounterGroup.PollForValues()
[/_/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/CounterGroup.cs @ 260]
rbx: (interior)
-> 00007F6BDFFFF038 System.Object[]
-> 00007F69D0033570 testwebapi.Controllers.Processor
-> 00007F69D0033588 testwebapi.Controllers.CustomerCache
-> 00007F69D00335A0 System.Collections.Generic.List`1[[testwebapi.Controllers.Customer,
DiagnosticScenarios]]
-> 00007F6C000148A0 testwebapi.Controllers.Customer[]
-> 00007F6AD0942258 testwebapi.Controllers.Customer
-> 00007F6AD09421F8 System.String
HandleTable:
00007F6C98BB15F8 (pinned handle)
-> 00007F6BDFFFF038 System.Object[]
-> 00007F69D0033570 testwebapi.Controllers.Processor
-> 00007F69D0033588 testwebapi.Controllers.CustomerCache
-> 00007F69D00335A0 System.Collections.Generic.List`1[[testwebapi.Controllers.Customer,
DiagnosticScenarios]]
-> 00007F6C000148A0 testwebapi.Controllers.Customer[]
-> 00007F6AD0942258 testwebapi.Controllers.Customer
-> 00007F6AD09421F8 System.String
Found 2 roots.
Puede ver que el objeto Customer mantiene directamente String y un objeto CustomerCache lo hace
indirectamente.
Puede seguir volcando objetos para ver que la mayoría de los objetos String siguen un patrón similar. En este
punto, la investigación proporcionó suficiente información para identificar la causa principal en su código.
Este procedimiento general le permite identificar el origen de las principales fugas de memoria.
Pasos siguientes
Depuración del uso elevado de CPU en .NET Core
Depuración del uso elevado de CPU en .NET Core
16/09/2020 • 7 minutes to read • Edit Online
Requisitos previos
En el tutorial se usa:
SDK de .NET Core 3.1 o una versión posterior
Destino de depuración de ejemplo para desencadenar el escenario
dotnet-trace para enumerar los procesos y generar un perfil
dotnet-counters para supervisar el uso de la CPU
Contadores de CPU
Antes de intentar recopilar datos de diagnóstico, debe observar una condición de CPU alta. Ejecute la aplicación de
ejemplo mediante el siguiente comando desde el directorio raíz del proyecto.
dotnet run
dotnet-trace ps
Anote el identificador de proceso de la salida del comando. El identificador de proceso era 22884 , pero el suyo
será diferente. Para comprobar el uso de CPU actual, use el comando de la herramienta dotnet-counters:
refresh-interval es el número de segundos entre los valores de CPU de sondeo de contador. La salida debe ser
similar a la siguiente:
Press p to pause, r to resume, q to quit.
Status: Running
[System.Runtime]
% Time in GC since last GC (%) 0
Allocation Rate / 1 sec (B) 0
CPU Usage (%) 0
Exception Count / 1 sec 0
GC Heap Size (MB) 4
Gen 0 GC Count / 60 sec 0
Gen 0 Size (B) 0
Gen 1 GC Count / 60 sec 0
Gen 1 Size (B) 0
Gen 2 GC Count / 60 sec 0
Gen 2 Size (B) 0
LOH Size (B) 0
Monitor Lock Contention Count / 1 sec 0
Number of Active Timers 1
Number of Assemblies Loaded 140
ThreadPool Completed Work Item Count / 1 sec 3
ThreadPool Queue Length 0
ThreadPool Thread Count 7
Working Set (MB) 63
Con la aplicación web en ejecución, inmediatamente después del inicio, no se consume CPU en absoluto y se
muestra 0% . Vaya a la ruta api/diagscenario/highcpu con 60000 como parámetro de ruta:
https://localhost:5001/api/diagscenario/highcpu/60000
Ahora, vuelva a ejecutar el comando dotnet-counters. Para supervisar solo cpu-usage , especifique
System.Runtime[cpu-usage] como parte del comando.
[System.Runtime]
CPU Usage (%) 25
Durante toda la solicitud, el uso de CPU rondará en torno al 25 %. En función del equipo host, se espera un uso de
CPU variable.
TIP
Para visualizar un uso de CPU incluso mayor, puede ejecutar este punto de conexión simultáneamente en varias pestañas del
explorador.
En este momento, puede afirmar con seguridad que el uso de CPU es mayor del esperado.
Generación de seguimiento
Al analizar una solicitud lenta, necesita una herramienta de diagnóstico que pueda proporcionar información
sobre lo que hace el código. Lo habitual es optar por un generador de perfiles. Existen diferentes opciones de
generador de perfiles entre las que elegir.
Linux
Windows
La herramienta perf se puede usar para generar perfiles de aplicaciones .NET Core. Salga de la instancia anterior
del destino de depuración de ejemplo.
Establezca la variable de entorno COMPlus_PerfMapEnabled para que la aplicación .NET Core cree un archivo map en
el directorio /tmp . perf usa este archivo map para asignar la dirección de CPU a las funciones generadas por JIT
por nombre. Para obtener más información, consulte Escritura del mapa de rendimiento.
Ejecute el destino de depuración de ejemplo en la misma sesión de terminal.
export COMPlus_PerfMapEnabled=1
dotnet run
El comando perf inicia el proceso de recopilación de rendimiento. Deje que se ejecute durante unos 20 o
30 segundos y, luego, presione Ctrl+C para salir del proceso de recopilación. Puede usar el mismo comando
perf para ver la salida del seguimiento.
Este comando genera un archivo flamegraph.svg que puede ver en el explorador para investigar el problema de
rendimiento:
Consulte también
dotnet-trace para mostrar procesos
dotnet-counters para comprobar el uso de memoria administrada
dotnet-dump para recopilar y analizar un archivo de volcado de memoria
dotnet/diagnostics
Pasos siguientes
Depuración de un interbloqueo en .NET Core
Depuración de un interbloqueo en .NET Core
16/09/2020 • 11 minutes to read • Edit Online
Requisitos previos
En el tutorial se usa:
SDK de .NET Core 3.1 o una versión posterior
Destino de depuración de ejemplo: aplicación web para desencadenar el escenario
dotnet-trace para mostrar procesos
dotnet-dump para recopilar y analizar un archivo de volcado de memoria
dotnet run
dotnet-trace ps
Anote el identificador de proceso de la salida del comando. El identificador de proceso era 4807 , pero el suyo será
diferente. Vaya a la siguiente dirección URL, que es un punto de conexión de API en el sitio de ejemplo:
https://localhost:5001/api/diagscenario/deadlock
La solicitud de API al sitio se bloquea y no responde. Deje que la solicitud se ejecute durante unos 10 o
15 segundos. Luego, cree el volcado de núcleo mediante el siguiente comando:
Linux
Windows
sudo dotnet-dump collect -p 4807
Puesto que está examinando un posible bloqueo, quiere obtener una idea general de la actividad de los
subprocesos del proceso. Puede usar el comando threads como se muestra a continuación:
> threads
*0 0x1DBFF (121855)
1 0x1DC01 (121857)
2 0x1DC02 (121858)
3 0x1DC03 (121859)
4 0x1DC04 (121860)
5 0x1DC05 (121861)
6 0x1DC06 (121862)
7 0x1DC07 (121863)
8 0x1DC08 (121864)
9 0x1DC09 (121865)
10 0x1DC0A (121866)
11 0x1DC0D (121869)
12 0x1DC0E (121870)
13 0x1DC10 (121872)
14 0x1DC11 (121873)
15 0x1DC12 (121874)
16 0x1DC13 (121875)
17 0x1DC14 (121876)
18 0x1DC15 (121877)
19 0x1DC1C (121884)
20 0x1DC1D (121885)
21 0x1DC1E (121886)
22 0x1DC21 (121889)
23 0x1DC22 (121890)
24 0x1DC23 (121891)
25 0x1DC24 (121892)
26 0x1DC25 (121893)
...
...
317 0x1DD48 (122184)
318 0x1DD49 (122185)
319 0x1DD4A (122186)
320 0x1DD4B (122187)
321 0x1DD4C (122188)
La salida muestra todos los subprocesos que se están ejecutando en el proceso con su identificador de subproceso
de depurador asociado y su identificador de subproceso de sistema operativo. Hay más de 300 subprocesos en
función de la salida.
El siguiente paso es entender mejor lo que los subprocesos están haciendo mediante la obtención de la pila de
llamadas de cada subproceso. El comando clrstack se puede usar para generar pilas de llamadas. Puede generar
una sola pila de llamadas o todas. Use el siguiente comando para generar todas las pilas de llamadas de todos los
subprocesos del proceso:
clrstack -all
...
...
...
Child SP IP Call Site
00007F2AE37B5680 00007f305abc6360 [GCFrame: 00007f2ae37b5680]
00007F2AE37B5770 00007f305abc6360 [GCFrame: 00007f2ae37b5770]
00007F2AE37B57D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2ae37b57d0]
System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2AE37B5920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1()
[/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2AE37B5950 00007F2FE392B46D
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,
System.Threading.ContextCallback, System.Object)
[/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2AE37B5CA0 00007f30593044af [GCFrame: 00007f2ae37b5ca0]
00007F2AE37B5D70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2ae37b5d70]
OS Thread Id: 0x1dc82
Child SP IP Call Site
00007F2AE2FB4680 00007f305abc6360 [GCFrame: 00007f2ae2fb4680]
00007F2AE2FB4770 00007f305abc6360 [GCFrame: 00007f2ae2fb4770]
00007F2AE2FB47D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2ae2fb47d0]
System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2AE2FB4920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1()
[/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2AE2FB4950 00007F2FE392B46D
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,
System.Threading.ContextCallback, System.Object)
[/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2AE2FB4CA0 00007f30593044af [GCFrame: 00007f2ae2fb4ca0]
00007F2AE2FB4D70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2ae2fb4d70]
OS Thread Id: 0x1dc83
Child SP IP Call Site
00007F2AE27B3680 00007f305abc6360 [GCFrame: 00007f2ae27b3680]
00007F2AE27B3770 00007f305abc6360 [GCFrame: 00007f2ae27b3770]
00007F2AE27B37D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2ae27b37d0]
System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2AE27B3920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1()
[/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2AE27B3950 00007F2FE392B46D
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,
System.Threading.ContextCallback, System.Object)
[/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2AE27B3CA0 00007f30593044af [GCFrame: 00007f2ae27b3ca0]
00007F2AE27B3D70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2ae27b3d70]
OS Thread Id: 0x1dc84
Child SP IP Call Site
00007F2AE1FB2680 00007f305abc6360 [GCFrame: 00007f2ae1fb2680]
00007F2AE1FB2770 00007f305abc6360 [GCFrame: 00007f2ae1fb2770]
00007F2AE1FB27D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2ae1fb27d0]
System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2AE1FB2920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1()
[/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2AE1FB2950 00007F2FE392B46D
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,
System.Threading.ContextCallback, System.Object)
[/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2AE1FB2CA0 00007f30593044af [GCFrame: 00007f2ae1fb2ca0]
00007F2AE1FB2D70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2ae1fb2d70]
OS Thread Id: 0x1dc85
Child SP IP Call Site
00007F2AE17B1680 00007f305abc6360 [GCFrame: 00007f2ae17b1680]
00007F2AE17B1770 00007f305abc6360 [GCFrame: 00007f2ae17b1770]
00007F2AE17B17D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2ae17b17d0]
System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2AE17B1920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1()
[/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2AE17B1950 00007F2FE392B46D
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,
System.Threading.ContextCallback, System.Object)
[/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2AE17B1CA0 00007f30593044af [GCFrame: 00007f2ae17b1ca0]
00007F2AE17B1D70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2ae17b1d70]
OS Thread Id: 0x1dc86
Child SP IP Call Site
00007F2AE0FB0680 00007f305abc6360 [GCFrame: 00007f2ae0fb0680]
00007F2AE0FB0770 00007f305abc6360 [GCFrame: 00007f2ae0fb0770]
00007F2AE0FB07D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2ae0fb07d0]
System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2AE0FB0920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1()
[/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2AE0FB0950 00007F2FE392B46D
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,
System.Threading.ContextCallback, System.Object)
[/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2AE0FB0CA0 00007f30593044af [GCFrame: 00007f2ae0fb0ca0]
00007F2AE0FB0D70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2ae0fb0d70]
OS Thread Id: 0x1dc87
Child SP IP Call Site
00007F2AE07AF680 00007f305abc6360 [GCFrame: 00007f2ae07af680]
00007F2AE07AF770 00007f305abc6360 [GCFrame: 00007f2ae07af770]
00007F2AE07AF7D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2ae07af7d0]
System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2AE07AF920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1()
[/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2AE07AF950 00007F2FE392B46D
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,
System.Threading.ContextCallback, System.Object)
[/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2AE07AFCA0 00007f30593044af [GCFrame: 00007f2ae07afca0]
00007F2AE07AFD70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2ae07afd70]
OS Thread Id: 0x1dc88
Child SP IP Call Site
00007F2ADFFAE680 00007f305abc6360 [GCFrame: 00007f2adffae680]
00007F2ADFFAE770 00007f305abc6360 [GCFrame: 00007f2adffae770]
00007F2ADFFAE7D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2adffae7d0]
System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2ADFFAE920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1()
[/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2ADFFAE950 00007F2FE392B46D
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,
System.Threading.ContextCallback, System.Object)
[/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2ADFFAECA0 00007f30593044af [GCFrame: 00007f2adffaeca0]
00007F2ADFFAED70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2adffaed70]
...
...
Si se observan las pilas de llamadas de los más de 300 subprocesos, se muestra un patrón en el que una mayoría
de los subprocesos comparten una pila de llamadas común:
OS Thread Id: 0x1dc88
Child SP IP Call Site
00007F2ADFFAE680 00007f305abc6360 [GCFrame: 00007f2adffae680]
00007F2ADFFAE770 00007f305abc6360 [GCFrame: 00007f2adffae770]
00007F2ADFFAE7D0 00007f305abc6360 [HelperMethodFrame_1OBJ: 00007f2adffae7d0]
System.Threading.Monitor.ReliableEnter(System.Object, Boolean ByRef)
00007F2ADFFAE920 00007F2FE392B31F testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_1()
[/home/marioh/webapi/Controllers/diagscenario.cs @ 36]
00007F2ADFFAE950 00007F2FE392B46D
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,
System.Threading.ContextCallback, System.Object)
[/__w/3/s/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
00007F2ADFFAECA0 00007f30593044af [GCFrame: 00007f2adffaeca0]
00007F2ADFFAED70 00007f30593044af [DebuggerU2MCatchHandlerFrame: 00007f2adffaed70]
La pila de llamadas parece mostrar que la solicitud ha llegado en el método de interbloqueo que, a su vez, realiza
una llamada a Monitor.ReliableEnter . Este método indica que los subprocesos están intentando especificar un
bloqueo de monitor. Están esperando a que el bloqueo esté disponible. Probablemente otro subproceso lo haya
bloqueado.
El siguiente paso es averiguar qué subproceso está manteniendo realmente el bloqueo de monitor. Como los
monitores normalmente almacenan información de bloqueo en la tabla de bloques de sincronización, se puede
usar el comando syncblk para obtener más información:
> syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
43 00000246E51268B8 603 1 0000024B713F4E30 5634 28 00000249654b14c0 System.Object
44 00000246E5126908 3 1 0000024B713F47E0 51d4 29 00000249654b14d8 System.Object
-----------------------------
Total 344
CCW 1
RCW 2
ComClassFactory 0
Free 0
Las dos columnas que interesan son MonitorHeld y Owning Thread Info . La columna MonitorHeld muestra si
un subproceso ha adquirido un bloqueo de monitor y el número de subprocesos en espera. La columna Owning
Thread Info muestra el subproceso que posee actualmente el bloqueo de monitor. La información del subproceso
tiene tres subcolumnas diferentes. La segunda subcolumna muestra el identificador de subproceso de sistema
operativo.
En este punto, se sabe que dos subprocesos diferentes (0x5634 y 0x51d4) mantienen un bloqueo de monitor. El
siguiente paso consiste en echar un vistazo a lo que están haciendo esos subprocesos. Hay que comprobar si
mantienen el bloqueo indefinidamente. Vamos a usar los comandos setthread y clrstack para alternar entre
cada uno de los subprocesos y mostrar las pilas de llamadas.
Para ver el primer subproceso, ejecute el comando setthread y busque el índice del subproceso 0x5634 (el índice
era 28). La función de interbloqueo está esperando a adquirir un bloqueo, pero ya posee dicho bloqueo. Está en
interbloqueo, a la espera del bloqueo que ya mantiene.
> setthread 28
> clrstack
OS Thread Id: 0x5634 (28)
Child SP IP Call Site
0000004E46AFEAA8 00007fff43a5cbc4 [GCFrame: 0000004e46afeaa8]
0000004E46AFEC18 00007fff43a5cbc4 [GCFrame: 0000004e46afec18]
0000004E46AFEC68 00007fff43a5cbc4 [HelperMethodFrame_1OBJ: 0000004e46afec68]
System.Threading.Monitor.Enter(System.Object)
0000004E46AFEDC0 00007FFE5EAF9C80 testwebapi.Controllers.DiagScenarioController.DeadlockFunc()
[C:\Users\dapine\Downloads\Diagnostic_scenarios_sample_debug_target\Controllers\DiagnosticScenarios.cs @ 58]
0000004E46AFEE30 00007FFE5EAF9B8C testwebapi.Controllers.DiagScenarioController.<deadlock>b__3_0()
[C:\Users\dapine\Downloads\Diagnostic_scenarios_sample_debug_target\Controllers\DiagnosticScenarios.cs @ 26]
0000004E46AFEE80 00007FFEBB251A5B System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
[/_/src/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @ 44]
0000004E46AFEEB0 00007FFE5EAEEEEC
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,
System.Threading.ContextCallback, System.Object)
[/_/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs @ 201]
0000004E46AFEF20 00007FFEBB234EAB System.Threading.ThreadHelper.ThreadStart()
[/_/src/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @ 93]
0000004E46AFF138 00007ffebdcc6b63 [GCFrame: 0000004e46aff138]
0000004E46AFF3A0 00007ffebdcc6b63 [DebuggerU2MCatchHandlerFrame: 0000004e46aff3a0]
El segundo subproceso es similar. También está intentando adquirir un bloqueo que ya posee. Lo más probable es
que los más de 300 subprocesos restantes en espera estén esperando también a uno de los bloqueos que han
causado el interbloqueo.
Consulte también
dotnet-trace para mostrar procesos
dotnet-counters para comprobar el uso de memoria administrada
dotnet-dump para recopilar y analizar un archivo de volcado de memoria
dotnet/diagnostics
Pasos siguientes
¿Qué herramientas de diagnóstico están disponibles en .NET Core?
Analizador de API en .NET
18/03/2020 • 10 minutes to read • Edit Online
El analizador de API de .NET es un analizador Roslyn que detecta posibles riesgos de compatibilidad de API de C#
en distintas plataformas y detecta llamadas a API en desuso. Puede ser útil para todos los programadores de C#
en cualquier fase de desarrollo.
El analizador de API está disponible como un paquete NuGet Microsoft.DotNet.Analyzers.Compatibility. Tras hacer
referencia a él en un proyecto, supervisa automáticamente el código e indica el uso de API problemático. También
puede obtener sugerencias sobre posibles soluciones; para ello, haga clic en la bombilla. El menú desplegable
incluye una opción para suprimir las advertencias.
NOTE
El analizador de API de .NET aún está en versión preliminar.
Requisitos previos
Visual Studio 2017 o Visual Studio para Mac (todas las versiones).
La ventana Lista de errores contiene advertencias con un identificador exclusivo por cada API en desuso, como
se muestra en el ejemplo siguiente ( DE004 ):
Al hacer clic en el identificador, se le remite a una página web que contiene información detallada sobre la API en
desuso y sugerencias sobre las API alternativas que pueden usarse.
Las advertencias pueden suprimirse si se hace clic con el botón derecho del ratón en el miembro resaltado y se
selecciona Suprimir <Id. de diagnóstico> . Hay dos maneras de suprimir las advertencias:
localmente (en el origen)
globalmente (en un archivo de supresión); se trata de la opción recomendada
Supresión de advertencias localmente
Para suprimir advertencias localmente, haga clic con el botón derecho en el miembro del que desea suprimir las
advertencias y luego seleccione Acciones rápidas y refactorizaciones > Suprimir Id. de diagnóstico <Id.
de diagnóstico> > en origen . La directiva del preprocesador de advertencias #pragma se agrega al código
fuente en el ámbito definido:
.
Supresión de advertencias globalmente
Para suprimir advertencias globalmente, haga clic con el botón derecho en el miembro del que desea suprimir las
advertencias y luego seleccione Acciones rápidas y refactorizaciones > Suprimir Id. de diagnóstico <Id.
de diagnóstico> > en archivo de supresión .
Se agrega un archivo GlobalSuppressions.cs al proyecto después de su primera supresión. Las nuevas supresiones
globales se anexan a este archivo.
La supresión global es el método recomendado para garantizar la coherencia del uso de API en los proyectos.
<PropertyGroup>
<PlatformCompatIgnore>Linux;macOS</PlatformCompatIgnore>
</PropertyGroup>
Si el código tiene como destino varias plataformas y desea beneficiarse de una API que no es compatible en
algunas de ellas, puede proteger esa parte del código con una instrucción if :
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var w = Console.WindowWidth;
// More code
}
También puede compilar de forma condicional pro sistema operativo/marco de destino, pero actualmente necesita
hacerlo manualmente.
Diagnóstico admitido
Actualmente, el analizador controla los casos siguientes:
Uso de una API de .NET Standard que genera PlatformNotSupportedException (PC001).
Uso de una API de .NET Standard que no está disponible en .NET Framework 4.6.1 (PC002).
Uso de una API nativa que no existe en UWP (PC003).
Uso de las API Delegate.BeginInvoke y EndInvoke (PC004).
Uso de una API que está marcada como en desuso (DEXXXX).
Configuración
El usuario decide cómo se deben tratar los diagnósticos: como advertencias, errores, sugerencias o estar
desactivados. Por ejemplo, como un arquitecto, puede decidir que los problemas de compatibilidad deben tratarse
como errores y que las llamadas a algunas API en desuso generen advertencias, mientras que otras solo generan
sugerencias. Puede configurar esto por separado por identificador de diagnóstico y por proyecto. Para ello, en el
Explorador de soluciones , vaya al nodo Dependencias del proyecto. Expanda los nodos Dependencias >
Analizadores > Microsoft.DotNet.Analyzers.Compatibility . Haga clic con el botón derecho en el
identificador de diagnóstico, seleccione Configurar gravedad del conjunto de reglas y elija la opción
deseada.
Vea también
Entrada de blog de introducción al analizador de API.
Vídeo de demostración del analizador de API de YouTube.
Analizador de portabilidad de .NET
16/09/2020 • 11 minutes to read • Edit Online
¿Quiere que sus bibliotecas sean multiplataforma? ¿Quiere ver cuánto trabajo se requiere para que su aplicación
de .NET Framework se ejecute en .NET Core? El Analizador de portabilidad de .NET es una herramienta que
analiza ensamblados y proporciona un informe detallado sobre las API de .NET que faltan para que las
aplicaciones o bibliotecas sean portátiles en las plataformas .NET de destino. El analizador de portabilidad se
ofrece como una extensión de Visual Studio, que analiza un ensamblado por proyecto y, como una aplicación de
consola ApiPort, que analiza los ensamblados por archivos especificados o directorio.
Cuando haya convertido el proyecto para que tenga como destino la nueva plataforma, como .NET Core, puede
usar la herramienta del analizador de API basada en Roslyn para identificar las API que producen excepciones
PlatformNotSupportedException y otras incidencias de compatibilidad.
Destinos comunes
.NET Core: tiene un diseño modular, admite la instalación en paralelo y está dirigido a escenarios
multiplataforma. La instalación en paralelo permite adoptar nuevas versiones de .NET Core sin que ello afecte
a otras aplicaciones. Si su objetivo es portar su aplicación a .NET Core para que sea compatible con varias
plataformas, este es el destino recomendado.
.NET Standard: Incluye las API de .NET Standard disponibles en todas las implementaciones de .NET. Si su
objetivo es que la biblioteca se ejecute en todas las plataformas compatibles con .NET, este es el destino
recomendado.
ASP.NET Core: Un marco web moderno basado en .NET Core. Si su objetivo es portar su aplicación web a .NET
Core para que sea compatible con varias plataformas, éste es el destino recomendado.
.NET Core + extensiones de plataforma: Incluye las API de .NET Core además del paquete de compatibilidad
de Windows, que proporciona muchas de las tecnologías disponibles de .NET Framework. Se trata de un
destino recomendado para la portabilidad de la aplicación de .NET Framework a .NET Core en Windows.
.NET standard + extensiones de la plataforma: Incluye las API de .NET Standard además del paquete de
compatibilidad de Windows, que proporciona muchas de las tecnologías disponibles de .NET Framework. Se
trata de un destino recomendado para la portabilidad de la biblioteca de .NET Framework a .NET Core en
Windows.
Se recomienda que incluya todos los archivos exe y dll relacionados que posea y desea portar, y que excluya los
archivos de los que depende la aplicación, pero que no posee y no puede portar. Esto le proporcionará un
informe de portabilidad más relevante.
Vista e interpretación de los resultados de portabilidad
Solo las API que no son compatibles con una plataforma de destino aparecen en el informe. Después de ejecutar
el análisis en Visual Studio, verá que aparecerá el vínculo del archivo de informe de portabilidad de .NET. Si ha
usado la aplicación de consola ApiPort, el informe de portabilidad de .NET se guardará como archivo en el
formato especificado. El valor predeterminado es en un archivo de Excel ( .xlsx) ubicado en el directorio actual.
Resumen de portabilidad
En la sección Resumen de portabilidad del informe se muestra el porcentaje de portabilidad para cada
ensamblado incluido en la ejecución. En el ejemplo anterior, el 71,24 % de las API de .NET Framework utilizadas
en la aplicación svcutil están disponibles en .NET Core + extensiones de la plataforma. Si ejecuta la
herramienta Analizador de portabilidad de .NET en varios ensamblados, cada ensamblado debe tener una fila en
el informe de Resumen de portabilidad.
Detalles
La sección Detalles del informe enumera las API que faltan desde cualquiera de las plataformas de destino
seleccionadas.
Tipo de destino: al tipo le falta la API desde una plataforma de destino
Miembro de destino: el método no está presente en una plataforma de destino
Nombre del ensamblado: el ensamblado de .NET Framework en el que se encuentra la API que falta.
Cada una de las plataformas de destino seleccionada es una columna, como ".NET Core": El valor de "No
compatible" significa que la API no se admite en esta plataforma de destino.
Cambios recomendados: la API o tecnología recomendada a la que realizar el cambio. Actualmente, este
campo está vacío o no está actualizado para muchas de las API. Debido al gran número de API, nos
enfrentamos a un gran desafío para mantenerlas actualizadas. Estamos examinando soluciones alternativas
para proporcionar información útil a los clientes.
Ensamblados que faltan
Puede encontrar la sección Ensamblados que faltan en el informe. Esta sección contiene una lista de
ensamblados a los que hacen referencia los ensamblados analizados y que no se han analizado. Si se trata un
ensamblado que posee, inclúyalo en la ejecución del analizador de portabilidad de API para que pueda obtener
un informe detallado de portabilidad a nivel de API. Si se trata de una biblioteca de terceros, compruebe si hay
una versión más reciente que admita la plataforma de destino y considere la posibilidad de usar dicha versión
más reciente. Finalmente, la lista debe incluir todos los ensamblados de terceros de los que depende la
aplicación que tengan una versión compatible con la plataforma de destino.
Para obtener más información sobre el Analizador de portabilidad de .NET, visite la documentación de GitHub y
el vídeo de Channel 9 A Brief Look at the .NET Portability Analyzer (Información breve sobre el Analizador de
portabilidad de .NET).
Introducción a Common Language Runtime
16/09/2020 • 9 minutes to read • Edit Online
.NET Framework proporciona un entorno en tiempo de ejecución denominado Common Language Runtime, que
ejecuta el código y proporciona servicios que facilitan el proceso de desarrollo.
Los compiladores y las herramientas exponen la funcionalidad de Common Language Runtime y permiten escribir
código con las ventajas que proporciona este entorno de ejecución administrado. El código desarrollado con un
compilador de lenguaje orientado al tiempo de ejecución se denomina código administrado. Este código se
beneficia de características como: la integración entre lenguajes, el control de excepciones entre lenguajes, la
seguridad mejorada, la compatibilidad con la implementación y las versiones, un modelo simplificado de
interacción y servicios de generación de perfiles y depuración.
NOTE
Los compiladores y las herramientas pueden generar resultados que Common Language Runtime puede consumir porque el
sistema de tipos, el formato de metadatos y el entorno en tiempo de ejecución (el sistema de ejecución virtual) están todos
definidos según un estándar público, la especificación Common Language Infrastructure de ECMA. Para obtener más
información, consulte ECMA C# and Common Language Infrastructure Specifications (Especificaciones de ECMA C# y
Common Language Infrastructure).
Para permitir al motor en tiempo de ejecución proporcionar servicios al código administrado, los compiladores de
lenguajes deben emitir metadatos que describen los tipos, los miembros y las referencias del código. Los
metadatos se almacenan con el código; cada archivo ejecutable portable (PE) de Common Language Runtime
cargable contiene metadatos. El motor en tiempo de ejecución utiliza los metadatos para localizar y cargar clases,
colocar instancias en memoria, resolver invocaciones a métodos, generar código nativo, exigir mecanismos de
seguridad y establecer los límites del contexto en tiempo de ejecución.
El tiempo de ejecución controla automáticamente la disposición de los objetos y administra las referencias a éstos,
liberándolos cuando ya no se utilizan. Los objetos cuya duración se administra de esta forma se denominan datos
administrados. La recolección de elementos no utilizados elimina pérdidas de memoria así como otros errores
habituales de programación. Con un código administrado se pueden utilizar datos administrados, datos no
administrados o estos dos tipos de datos en una aplicación .NET Framework. Como los compiladores de lenguajes
proporcionan sus propios tipos, como tipos primitivos, no siempre se sabe (o no es necesario saber) si los datos se
están administrando.
Common Language Runtime facilita el diseño de los componentes y de las aplicaciones cuyos objetos interactúan
entre lenguajes distintos. Los objetos escritos en lenguajes diferentes pueden comunicarse entre sí, lo que permite
integrar sus comportamientos de forma precisa. Por ejemplo, puede definir una clase y, a continuación, utilizar un
lenguaje diferente para derivar una clase de la clase original o llamar a un método de la clase original. También se
puede pasar al método de una clase una instancia de una clase escrita en un lenguaje diferente. Esta integración
entre lenguajes diferentes es posible porque los compiladores y las herramientas de lenguajes orientados al motor
en tiempo de ejecución utilizan un sistema de tipos común definido por el motor en tiempo de ejecución, y los
lenguajes siguen las reglas en tiempo de ejecución para definir nuevos tipos, así como para crear, utilizar,
almacenar y enlazar tipos.
Como parte de los metadatos, todos los componentes administrados contienen información sobre los
componentes y los recursos utilizados en su compilación. El motor en tiempo de ejecución utiliza esta información
para garantizar que el componente o la aplicación contiene las versiones especificadas de todo lo necesario, por lo
que hay menos posibilidades de que la ejecución del código se interrumpa debido a una dependencia inadecuada.
La información de registro y los datos de estado ya no se almacenan en el Registro, donde puede ser difícil
establecer y mantener datos. En su lugar, la información sobre tipos definidos por el usuario (y sus dependencias)
se almacena con el código como metadatos y, de este modo, las tareas de replicación y eliminación de
componentes es mucho menos complicada.
Las herramientas y los compiladores de lenguajes exponen la funcionalidad del motor en tiempo de ejecución de
forma que resulte útil e intuitiva para los programadores. Esto significa que algunas características en tiempo de
ejecución pueden ser más evidentes en un entorno que en otro. El funcionamiento del motor en tiempo de
ejecución depende de las herramientas y los compiladores utilizados. Por ejemplo, un programador de Visual Basic
observará que con Common Language Runtime, el lenguaje Visual Basic contiene más características orientadas a
objetos que antes. El motor en tiempo de ejecución ofrece las siguientes ventajas:
Mejoras en el rendimiento.
Capacidad para utilizar fácilmente componentes desarrollados en otros lenguajes.
Tipos extensibles que proporciona una biblioteca de clases
Características del lenguaje como herencia, interfaces y sobrecarga para la programación orientada a
objetos.
Compatibilidad con subprocesamiento libre explícito que permite la creación de aplicaciones multiproceso
escalables.
Compatibilidad con el control de excepciones estructurado.
Compatibilidad con atributos personalizados.
Recolección de elementos no utilizados.
Emplee delegados en lugar de punteros a funciones para mayor seguridad y protección de tipos. Para más
información acerca de los delegados, consulte Common Type System.
Versiones de CLR
El número de versión de .NET Framework no se corresponde necesariamente con el número de versión del CLR
que incluye. Para obtener una lista de las versiones de .NET Framework y sus versiones de CLR correspondientes,
consulte Versiones y dependencias de .NET Framework. Las versiones de .NET Core tienen una versión de producto
única, es decir, no hay ninguna versión independiente de CLR. Para obtener una lista de las versiones de .NET Core,
consulte Descarga de .NET Core.
Temas relacionados
T IT L E DESC RIP C IÓ N
Proceso de ejecución administrada Describe los pasos requeridos para aprovechar al máximo las
ventajas de Common Language Runtime.
Información general acerca de .NET Framework Describe conceptos clave de .NET Framework como Common
Type System (CTS), interoperabilidad entre lenguajes, ejecución
administrada, dominios de aplicación y ensamblados.
T IT L E DESC RIP C IÓ N
Sistema de tipos comunes Describe cómo declarar, usar y administrar tipos en el motor
en tiempo de ejecución para permitir la integración entre
lenguajes.
proceso de ejecución administrada
16/09/2020 • 16 minutes to read • Edit Online
El proceso de ejecución administrada incluye los pasos siguientes, que se describen en detalle más adelante en
este tema:
1. Elegir un compilador.
Para obtener los beneficios que proporciona Common Language Runtime, se deben utilizar uno o más
compiladores de lenguaje orientados al tiempo de ejecución.
2. Compilar código a MSIL.
La compilación convierte el código fuente en lenguaje intermedio de Microsoft (MSIL) y genera los
metadatos necesarios.
3. Compilar MSIL a código nativo.
En tiempo de ejecución, un compilador Just-In-Time (JIT) convierte MSIL en código nativo. Durante esta
compilación, el código debe pasar un proceso de comprobación que examina el MSIL y los metadatos para
averiguar si el código garantiza la seguridad de tipos.
4. Ejecutar código.
Common Language Runtime proporciona la infraestructura que permite que la ejecución tenga lugar y los
servicios que se pueden usar durante la ejecución.
Elegir un compilador
Para aprovechar las ventajas que ofrece Common Language Runtime (CLR), deben emplearse uno o varios
compiladores de lenguaje destinados al motor en tiempo de ejecución, como Visual Basic, C#, Visual C++, F# o
uno de los muchos compiladores de otros fabricantes, como un compilador Eiffel, Perl o COBOL.
Como se ejecuta en un entorno multilenguaje, el motor en tiempo de ejecución es compatible con una gran
variedad de tipos de datos y características de lenguajes. El compilador de lenguaje utilizado determina las
características en tiempo de ejecución que están disponibles, y el código se diseña con esas características. El
compilador, y no el motor en tiempo de ejecución, es el que establece la sintaxis que se debe utilizar en el código.
Si los componentes escritos en otros lenguajes deben poder usar plenamente el componente, los tipos exportados
de ese componente solo deben exponer las características del lenguaje incluidas en Independencia del lenguaje y
componentes independientes del lenguaje (CLS). Puede utilizar el atributo CLSCompliantAttribute para garantizar
que su código es conforme a CLS. Para más información, consulte Independencia del lenguaje y componentes
independientes del lenguaje.
Volver al principio
Ejecutar código
Common Language Runtime proporciona la infraestructura que permite que la ejecución administrada tenga
lugar y los servicios que se pueden usar durante la ejecución. Para poder ejecutar un método, primero se debe
compilar a código específico del procesador. Cada método para el que se ha generado código de MSIL se compila
mediante un compilador JIT la primera vez que se le llama y, después, se ejecuta. La próxima vez que se ejecuta el
método, se ejecuta el código nativo existente resultante de la compilación JIT. El proceso de compilación JIT y la
posterior ejecución de código se repite hasta completar la ejecución.
Durante la ejecución, el código administrado recibe servicios como la recolección de elementos no utilizados,
seguridad, interoperabilidad con código no administrado, compatibilidad de depuración entre lenguajes diferentes
y compatibilidad mejorada con el control de versiones y la implementación.
En Microsoft Windows Vista, el cargador del sistema operativo comprueba los módulos administrados mediante el
examen de un bit del encabezado de COFF. El bit que se establece indica un módulo administrado. Si el cargador
detecta módulos administrados, carga mscoree.dll, y _CorValidateImage y _CorImageUnloading notifican al
cargador cuándo se cargan y descargan imágenes del módulo administrado. _CorValidateImage lleva a cabo las
acciones siguientes:
1. Garantiza que el código es código administrado válido.
2. Cambia el punto de entrada en la imagen a un punto de entrada en el motor en tiempo de ejecución.
En las versiones de 64 bits de Windows, _CorValidateImage modifica la imagen que está en la memoria
transformando el formato PE32 en PE32+.
Volver al principio
Vea también
Información general
Independencia del lenguaje y componentes independientes del lenguaje
Metadatos y componentes autodescriptivos
Ilasm.exe (Ensamblador de IL)
Seguridad
Interoperating with Unmanaged Code (Interoperar con código no administrado)
Implementación
Ensamblados de .NET
Dominios de aplicación
Ensamblados de .NET
16/09/2020 • 14 minutes to read • Edit Online
Los ensamblados componen las unidades fundamentales de implementación, control de versiones, reutilización,
ámbito de activación y permisos de seguridad para las aplicaciones basadas en .NET. Un ensamblado es una
colección de tipos y recursos compilados para funcionar en conjunto y formar una unidad lógica de funcionalidad.
Los ensamblados adoptan la forma de un archivo ejecutable ( .exe) o de biblioteca de vínculos dinámicos ( .dll), y
son los bloques de creación de las aplicaciones .NET. Proporcionan a Common Language Runtime la información
necesaria para conocer las implementaciones de tipos.
En .NET Core y .NET Framework, puede crear un ensamblado a partir de uno o varios archivos de código fuente. En
.NET Framework, los ensamblados pueden contener uno o varios módulos. Esto permite que los proyectos más
grandes se planeen para que varios desarrolladores individuales puedan trabajar en archivos de código fuente o
módulos independientes, que se combinan para crear un ensamblado único. Para más información sobre los
módulos, vea el tema Procedimiento para compilar un ensamblado de varios archivos.
Los ensamblados tienen las propiedades siguientes:
Los ensamblados se implementan como archivos .exe o .dll.
Para las bibliotecas destinadas a .NET Framework, puede compartir los ensamblados entre aplicaciones si
los coloca en la caché global de ensamblados (GAC). Debe asignar nombres seguros a los ensamblados
antes de poder incluirlos en la GAC. Para obtener más información, vea Ensamblados con nombre seguro.
Los ensamblados solo se cargan en memoria si son necesarios. Si no se usan, no se cargan. Esto significa
que los ensamblados pueden ser una manera eficaz de administrar recursos en proyectos más grandes.
Mediante programación, puede obtener información sobre un ensamblado mediante reflexión. Para más
información, vea Reflexión (C#) o Reflexión (Visual Basic).
Puede cargar un ensamblado solo para inspeccionarlo mediante la clase MetadataLoadContext en .NET
Core y los métodos Assembly.ReflectionOnlyLoad o Assembly.ReflectionOnlyLoadFrom en .NET Core y .NET
Framework.
Creación de un ensamblado
Los ensamblados pueden ser estáticos o dinámicos. Los ensamblados estáticos se almacenan en el disco, en
archivos ejecutables portables PE. Los ensamblados estáticos pueden incluir interfaces, clases y recursos como
mapas de bits, archivos JPEG y otros archivos de recursos. También puede crear ensamblados dinámicos, que se
ejecutan directamente desde la memoria y no se guardan en el disco antes de su ejecución. Los ensamblados
dinámicos se pueden guardar en el disco una vez que se hayan ejecutado.
Existen varias formas de crear ensamblados. Puede usar herramientas de desarrollo, como Visual Studio, que
permite crear archivos .dll o .exe. Puede usar las herramientas de Windows SDK para crear ensamblados con
módulos de otros entornos de programación. También puede utilizar las API de Common Language Runtime,
como System.Reflection.Emit, para crear ensamblados dinámicos.
Para compilar ensamblados puede hacerlo en Visual Studio, con las herramientas de la interfaz de la línea de
comandos de .NET Core o, para los ensamblados de .NET Framework, con un compilador de línea de comandos.
Para más información sobre cómo compilar los ensamblados con la CLI de .NET Core, consulte Información
general sobre la CLI de .NET Core. Para compilar ensamblados con los compiladores de línea de comandos, vea
Compilar la línea de comandos con csc.exe para C#, o bien Compilar desde la línea de comandos (Visual Basic)
para Visual Basic.
NOTE
Para compilar un ensamblado en Visual Studio, seleccione Compilar en el menú Compilar .
NOTE
La referencia a la mayoría de los ensamblados de la biblioteca de clases de .NET se hace automáticamente. Si no se hace
referencia a un ensamblado del sistema de forma automática, para .NET Core puede agregar una referencia al paquete
NuGet que contiene el ensamblado. Use el administrador de paquetes NuGet de Visual Studio, o bien agregue un elemento
<PackageReference> para el ensamblado al proyecto .csproj o .vbproj. En .NET Framework, puede agregar una referencia al
ensamblado mediante el cuadro de diálogo Agregar referencia de Visual Studio o mediante la opción de la línea de
comandos -reference para los compiladores de C# o Visual Basic.
En C#, puede usar dos versiones del mismo ensamblado en una misma aplicación. Para obtener más información,
vea alias externo.
Contenido relacionado
T IT L E DESC RIP C IÓ N
Manifiesto del ensamblado Datos del manifiesto del ensamblado y cómo se almacenan en
los ensamblados.
Ensamblados con nombre seguro Características de los ensamblados con nombre seguro.
Control de versiones de los ensamblados Información general sobre la directiva de control de versiones
de .NET Framework.
Cómo el motor en tiempo de ejecución ubica ensamblados Cómo resuelve .NET Framework las referencias de ensamblado
en tiempo de ejecución.
Referencia
System.Reflection.Assembly
Vea también
Formato de archivo de ensamblado .NET
Ensamblados de confianza
Ensamblados de referencia
Cómo: para cargar y descargar ensamblados
Cómo: para usar y depurar la descargabilidad de ensamblados en .NET Core
Cómo: para determinar si un archivo es un ensamblado
Cómo: Inspect assembly contents using MetadataLoadContext (Procedimiento para inspeccionar el contenido
de un ensamblado con MetadataLoadContext)
Metadatos y componentes autodescriptivos
16/09/2020 • 16 minutes to read • Edit Online
Hasta ahora, un componente de software (.exe o .dll) escrito en un lenguaje no podía usar fácilmente un
componente de software escrito en otro lenguaje. COM supuso un paso adelante en la resolución de este
problema. .NET Framework hace la interoperación entre componentes todavía más fácil, permitiendo que los
compiladores emitan información de declaración adicional en todos los módulos y ensamblados. Esta información,
denominada metadatos, contribuye a que los componentes interactúen sin problemas.
Los metadatos son información binaria que describe un programa, almacenada en un archivo ejecutable portable
(PE) de Common Language Runtime o en memoria. Cuando se compila el código en un archivo PE, los metadatos
se insertan en una parte del archivo, y el código se convierte al lenguaje intermedio de Microsoft (MSIL) y se
inserta en otra parte del archivo. Cada tipo y miembro que se define y al que se hace referencia en un módulo o
ensamblado se describe en los metadatos. Cuando se ejecuta código, el motor en tiempo de ejecución carga los
metadatos en la memoria y hace referencia a ellos para detectar información acerca de las clases, miembros,
herencia, etc., del código.
Los metadatos describen todos los tipos y miembros definidos en el código mediante un lenguaje neutro. Los
metadatos almacenan la siguiente información:
Descripción del ensamblado
Identidad (nombre, versión, referencia cultural, clave pública).
Los tipos que se exportan.
Otros ensamblados de los que depende éste.
Permisos de seguridad que hay que ejecutar.
Descripción de los tipos.
Nombre, visibilidad, clase base e interfaces implementadas.
Miembros (métodos, campos, propiedades, eventos, tipos anidados).
Atributos.
Elementos descriptivos adicionales que modifiquen los tipos y los miembros.
El byte superior ( 0x06 ) indica que este es un token de MethodDef . Los tres bytes inferiores ( 000004 ) indican a
Common Language Runtime que busque en la cuarta fila de la tabla MethodDef la información que describe la
definición de este método.
Metadatos en un archivo PE
Cuando se compila un programa para Common Language Runtime, se convierte en un archivo PE formado por
tres partes. La tabla siguiente describe el contenido de cada una de estas partes.
using System;
public class MyApp
{
public static int Main()
{
int ValueOne = 10;
int ValueTwo = 20;
Console.WriteLine("The Value is: {0}", Add(ValueOne, ValueTwo));
return 0;
}
public static int Add(int One, int Two)
{
return (One + Two);
}
}
Cuando se ejecuta el código, el motor en tiempo de ejecución carga el módulo en la memoria y consulta los
metadatos de esta clase. Una vez cargado, el motor en tiempo de ejecución realiza una análisis exhaustivo de la
secuencia de lenguaje intermedio de Microsoft (MSIL) del método para convertirla en rápidas instrucciones
máquina nativas. El motor en tiempo de ejecución usa un compilador Just-In-Time (JIT) para convertir las
instrucciones MSIL en código máquina nativo, método a método, según sea necesario.
En el siguiente ejemplo de código se muestra parte del MSIL producido a partir de la función Main del código
anterior. El MSIL y los metadatos se pueden ver desde cualquier aplicación de .NET Framework usando el
Desensamblador de MSIL (Ildasm.exe).
.entrypoint
.maxstack 3
.locals ([0] int32 ValueOne,
[1] int32 ValueTwo,
[2] int32 V_2,
[3] int32 V_3)
IL_0000: ldc.i4.s 10
IL_0002: stloc.0
IL_0003: ldc.i4.s 20
IL_0005: stloc.1
IL_0006: ldstr "The Value is: {0}"
IL_000b: ldloc.0
IL_000c: ldloc.1
IL_000d: call int32 ConsoleApplication.MyApp::Add(int32,int32) /* 06000003 */
El compilador JIT lee el MSIL de todo el método, lo analiza exhaustivamente y genera instrucciones nativas
efectivas para ese método. En IL_000d se encuentra un token de metadatos del método Add ( /* 06000003 */ ), y
el motor en tiempo de ejecución usa ese token para consultar la tercera fila de la tabla MethodDef .
En la siguiente tabla, se muestra parte de la tabla MethodDef a la que hace referencia el token de los metadatos
que describe el método Add . Aunque existen otras tablas de metadatos en el ensamblado y tienen sus propios
valores únicos, sólo se trata esta tabla.
N O M B RE
F IRM A ( SEÑ A L A
DIREC C IÓ N ( SEÑ A L A EL EL M O N TÓ N DE
REL AT IVA M O N TÓ N DE O B JETO S
F IL A VIRT UA L ( RVA ) IM P L F L A GS M A RC A S C A DEN A S) . B IN A RIO S)
Administrado ReuseSlot
SpecialName
RTSpecialName
.ctor
Administrado Estático
ReuseSlot
Administrado Estático
ReuseSlot
Cada columna de la tabla contiene información importante sobre el código. La columna RVA permite que el motor
en tiempo de ejecución calcule la dirección de memoria de inicio del MSIL que define este método. Las columnas
ImplFlags y Flags contienen máscaras de bits que describen el método (por ejemplo, si el método es público o
privado). La columna Nombre indexa el nombre del método del montón de cadenas. La columna Firma indexa la
definición de la firma del método del montón de blobs.
El motor en tiempo de ejecución calcula la dirección de desplazamiento deseada desde la columna RVA de la
tercera fila y la devuelve al compilador JIT, que, después, se dirige a la nueva dirección. El compilador JIT continúa
procesando el MSIL en la nueva dirección hasta que encuentra otro token de metadatos y se repite el proceso.
Usando metadatos, el motor en tiempo de ejecución tiene acceso a toda la información que necesita para cargar el
código y procesarlo en instrucciones máquina nativas. De este modo, los metadatos hacen posible los archivos
autodescriptivos y, junto con el sistema de tipos comunes, la herencia de un lenguaje a otro.
Temas relacionados
T IT L E DESC RIP C IÓ N
Todas las aplicaciones .NET Core tienen dependencias. Incluso la aplicación sencilla hello world tiene
dependencias en partes de las bibliotecas de clases de .NET Core.
La descripción de la lógica de carga de ensamblados predeterminados de .NET Core puede ayudar a comprender y
depurar incidencias de implementación habituales.
En algunas aplicaciones, las dependencias se determinan dinámicamente en tiempo de ejecución. En estas
situaciones, es fundamental entender cómo se cargan los ensamblados administrados y las dependencias no
administradas.
Descripción de AssemblyLoadContext
La API AssemblyLoadContext es fundamental para el diseño de carga de .NET Core. En el artículo Descripción de
AssemblyLoadContext se proporciona información general conceptual para el diseño.
Carga de detalles
Los detalles del algoritmo de carga se describen brevemente en varios artículos:
Algoritmo de carga de ensamblado administrado
Algoritmo de carga de ensamblado satélite
Algoritmo de carga de biblioteca no administrada (nativa)
Sondeo predeterminado
La clase AssemblyLoadContext es única en .NET Core. En este artículo se intenta complementar la documentación
de la API AssemblyLoadContext con información conceptual.
Este artículo es relevante para los desarrolladores que implementan carga dinámica, en particular los
desarrolladores de marco de carga dinámica.
¿Qué es AssemblyLoadContext?
Cada aplicación de .NET Core usa implícitamente AssemblyLoadContext. Es el proveedor del runtime para buscar y
cargar las dependencias. Cada vez que se carga una dependencia, se invoca una instancia de AssemblyLoadContext
para buscarla.
Proporciona un servicio de búsqueda, carga y almacenamiento en caché de ensamblados administrados y
otras dependencias.
Para admitir la carga y descarga de código dinámico, crea un contexto aislado con el fin de cargar el código
y sus dependencias en su propia instancia de AssemblyLoadContext.
Complicaciones
Incidencias de conversión de tipos
Cuando dos instancias de AssemblyLoadContext contienen definiciones de tipo con el mismo name , no son del
mismo tipo. Son del mismo tipo si y solo si proceden de la misma instancia de Assembly.
Para complicar las cosas, los mensajes de excepción sobre estos tipos no coincidentes pueden ser confusos. Se
hace referencia a los tipos en los mensajes de excepción por sus nombres de tipo simple. En este caso, el mensaje
de excepción común tendría el formato siguiente:
Los ensamblados administrados se ubican y se cargan con un algoritmo que implica varias fases.
Todos los ensamblados administrados, excepto los ensamblados satélite y los de WinRT , usan el mismo algoritmo.
Algoritmo
El algoritmo siguiente describe cómo el runtime carga un ensamblado administrado.
1. Determine el elemento AssemblyLoadContext active .
En el caso de una referencia estática de ensamblado, el elemento AssemblyLoadContext active es la
instancia que ha cargado el ensamblado de referencia.
Las API preferidas hacen que el elemento AssemblyLoadContext active sea explícito.
Otras API infieren el elemento AssemblyLoadContext active . Para estas API, se usa la propiedad
AssemblyLoadContext.CurrentContextualReflectionContext. Si su valor es null , se usa la instancia de
AssemblyLoadContext inferida.
Consulte la tabla anterior.
2. En el caso de los métodos Load-by-name , el elemento AssemblyLoadContext activo carga el ensamblado. En
orden de prioridad por:
Comprobar su cache-by-name .
Llamar a la función de AssemblyLoadContext.Load.
Comprobar la memoria caché de las instancias de AssemblyLoadContext.Default y ejecutar la lógica
sondeo predeterminado de ensamblado administrado.
Generar el evento AssemblyLoadContext.Resolving para el AssemblyLoadContext activo.
Generar el evento AppDomain.AssemblyResolve.
3. En los demás tipos de cargas, el elemento AssemblyLoadContext active carga el ensamblado. En orden de
prioridad por:
Comprobar su cache-by-name .
Cargar desde la ruta de acceso especificada o el objeto de ensamblado sin formato.
4. En cualquier caso, si se ha cargado un ensamblado recientemente, entonces ocurre lo siguiente:
Se genera el evento AppDomain.AssemblyLoad.
Se agrega una referencia al elemento cache-by-name de la instancia de AssemblyLoadContext del
ensamblado.
5. Si se encuentra el ensamblado, se agrega una referencia según sea necesario al elemento cache-by-name
de la instancia de AssemblyLoadContext active .
Algoritmo de carga de ensamblado satélite
11/05/2020 • 5 minutes to read • Edit Online
Los ensamblados satélite se utilizan con el fin de almacenar los recursos localizados personalizados para el idioma
y la referencia cultural.
Los ensamblados satélite usan un algoritmo de carga distinto al de los ensamblados administrados generales.
Algoritmo
El proceso de reserva de recursos de .NET Core conlleva los pasos siguientes:
1. Determine la instancia de AssemblyLoadContext active . En todos los casos, la instancia de active es el
elemento AssemblyLoadContext del ensamblado que se ejecuta.
2. La instancia de active intenta cargar un ensamblado satélite para la referencia cultural solicitada por
orden de prioridad al realizar lo siguiente:
Comprobar la memoria caché.
Comprobar en el directorio del ensamblado actualmente en ejecución un subdirectorio que coincida
con el elemento CultureInfo.Name solicitado (por ejemplo, es-MX ).
NOTE
Esta característica no se ha implementado en .NET Core antes de la versión 3.0.
NOTE
En Linux y macOS, el subdirectorio distingue entre mayúsculas y minúsculas y debe cumplir lo siguiente:
Coincidir exactamente con mayúsculas y minúsculas.
Estar en minúsculas.
NOTE
Para buscar un recurso dentro del ensamblado satélite, el entorno de ejecución busca el archivo de recursos
solicitado por ResourceManager para el CultureInfo.Name actual. En el archivo de recursos, busca el nombre del
recurso solicitado. Si no se encuentra ninguno, se considera que no se encontró el recurso.
4. Luego, el runtime busca los ensamblados de referencias culturales primarias a través de muchos niveles
potenciales, y cada vez repite los pasos 2 y 3.
Cada referencia cultural tiene solo un elemento primario, que está definido mediante la propiedad
CultureInfo.Parent.
La búsqueda de las referencias culturales primarias se detiene cuando la propiedad Parent de una
referencia cultural es CultureInfo.InvariantCulture.
En el caso de InvariantCulture, no se vuelve a los pasos 2 y 3, sino que continúa con el paso 5.
5. Si todavía no se encuentra el recurso, se usa el recurso para la referencia cultural predeterminada (de
reserva).
Normalmente, los recursos de la referencia cultural predeterminada se incluyen en el ensamblado de la
aplicación principal. Sin embargo, se puede especificar UltimateResourceFallbackLocation.Satellite para la
propiedad NeutralResourcesLanguageAttribute.Location. Este valor indica que la ubicación de reserva final
para los recursos es un ensamblado satélite, en lugar del ensamblado principal.
NOTE
La referencia cultural predeterminada es la reserva final. Por lo tanto, se recomienda incluir siempre un conjunto de
recursos exhaustivo en el archivo de recursos predeterminado. Esto ayuda a evitar que se produzcan excepciones. Al
tener un conjunto exhaustivo, se proporciona una reserva para todos los recursos y se garantiza que al menos un
recurso esté siempre presente para el usuario, incluso si no es específico de la referencia cultural.
6. Por último,
si el tiempo de ejecución no encuentra un archivo de recursos para una referencia cultural
predeterminada (de reserva), se produce una excepción MissingManifestResourceException o
MissingSatelliteAssemblyException.
Si se encuentra el archivo de recursos pero el recurso solicitado no se encuentra, la solicitud devuelve
null .
Algoritmo de carga de biblioteca no administrada
(nativa)
19/03/2020 • 2 minutes to read • Edit Online
Las bibliotecas no administradas se ubican y se cargan con un algoritmo que implica varias fases.
El algoritmo siguiente describe cómo se cargan las bibliotecas nativas a través de PInvoke .
2. En el caso del elemento AssemblyLoadContext active , intente buscar el ensamblado en orden de prioridad
por:
Comprobar la memoria caché.
Llamar al delegado System.Runtime.InteropServices.DllImportResolver actual que establece la
función NativeLibrary.SetDllImportResolver(Assembly, DllImportResolver).
Llamar a la función AssemblyLoadContext.LoadUnmanagedDll en el elemento AssemblyLoadContext
de active .
Comprobar la memoria caché de la instancia de AppDomain y ejecutar la lógica de sondeo de
biblioteca no administrada (nativa).
Generar el evento AssemblyLoadContext.ResolvingUnmanagedDll para el elemento
AssemblyLoadContext de active .
Creación de una aplicación de .NET Core con
complementos
20/05/2020 • 16 minutes to read • Edit Online
Este tutorial muestra cómo crear un contexto AssemblyLoadContext personalizado para cargar complementos. Se
usa un elemento AssemblyDependencyResolver para resolver las dependencias del complemento. El tutorial aísla
correctamente las dependencias del complemento de la aplicación host. Aprenderá a:
Estructurar un proyecto para admitir los complementos.
Crear un elemento AssemblyLoadContext personalizado para cargar cada complemento.
Usar el tipo System.Runtime.Loader.AssemblyDependencyResolver para permitir que los complementos tengan
dependencias.
Crear complementos que se puedan implementar fácilmente con solo copiar los artefactos de compilación.
Requisitos previos
Instale el SDK de .NET Core 3.0 o una versión más reciente.
Crear la aplicación
El primer paso es crear la aplicación:
1. Cree una carpeta nueva y, en ella, ejecute el siguiente comando:
2. Para facilitar la compilación del proyecto, cree un archivo de solución de Visual Studio en la misma carpeta.
Ejecute el siguiente comando:
namespace AppWithPlugin
{
class Program
{
static void Main(string[] args)
{
try
{
if (args.Length == 1 && args[0] == "/d")
{
Console.WriteLine("Waiting for any key...");
Console.ReadLine();
}
if (args.Length == 0)
{
Console.WriteLine("Commands: ");
// Output the loaded commands.
}
else
{
foreach (string commandName in args)
{
Console.WriteLine($"-- {commandName} --");
Console.WriteLine();
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
}
int Execute();
}
}
Reemplace el comentario // Load commands from plugins con el fragmento de código siguiente para habilitarlo
para cargar complementos desde rutas de acceso de archivo determinadas:
Después, reemplace el comentario // Output the loaded commands por el fragmento de código siguiente:
Reemplace el comentario // Execute the command with the name passed as an argument por el fragmento de código
siguiente:
command.Execute();
Y, por último, agregue los métodos estáticos denominados LoadPlugin y CreateCommands a la clase Program ,
como se muestra aquí:
static Assembly LoadPlugin(string relativePath)
{
throw new NotImplementedException();
}
if (count == 0)
{
string availableTypes = string.Join(",", assembly.GetTypes().Select(t => t.FullName));
throw new ApplicationException(
$"Can't find any type which implements ICommand in {assembly} from {assembly.Location}.\n" +
$"Available types: {availableTypes}");
}
}
Carga de complementos
Ahora la aplicación puede cargar correctamente y crear instancias de los comandos a partir de los ensamblados de
complemento cargados, pero todavía no puede cargar los ensamblados de complemento. Cree un archivo
denominado PluginLoadContext.cs en la carpeta AppWithPlugin con el contenido siguiente:
using System;
using System.Reflection;
using System.Runtime.Loader;
namespace AppWithPlugin
{
class PluginLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
return null;
}
return IntPtr.Zero;
}
}
}
Al usar una instancia de PluginLoadContext diferente para cada complemento, los complementos puede tener
dependencias diferentes o incluso en conflicto sin ningún problema.
using PluginBase;
using System;
namespace HelloPlugin
{
public class HelloCommand : ICommand
{
public string Name { get => "hello"; }
public string Description { get => "Displays hello message."; }
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
</Project>
<ItemGroup>
<ProjectReference Include="..\PluginBase\PluginBase.csproj">
<Private>false</Private>
<ExcludeAssets>runtime</ExcludeAssets>
</ProjectReference>
</ItemGroup>
Esto evita que los ensamblados A.PluginBase se copien en el directorio de salida del complemento y asegura que
el complemento use la versión de A.PluginBase la aplicación A.
A partir de .NET Core 3.0, se admite la capacidad de cargar y posteriormente descargar un conjunto de
ensamblados. Para este fin, en .NET Framework se usaban dominios de aplicación personalizados, pero .NET Core
solo admite un dominio de aplicación predeterminado único.
.NET Core 3.0 y versiones posteriores admiten la descargabilidad a través de AssemblyLoadContext. Puede cargar
un conjunto de ensamblados en un AssemblyLoadContext recopilable, ejecutar métodos en ellos o simplemente
inspeccionarlos mediante reflexión y, por último, descargar el AssemblyLoadContext . Esa acción descarga los
ensamblados cargados en el AssemblyLoadContext .
Cabe destacar una diferencia entre la descarga mediante AssemblyLoadContext y mediante AppDomains. Con
AppDomains, se fuerza la descarga. En el momento de la descarga, se anulan todos los subprocesos que se
ejecutan en la instancia de AppDomain de destino, se destruyen los objetos COM administrados creados en la
AppDomain de destino, etc. Con AssemblyLoadContext , la descarga es "cooperativa". La llamada al método
AssemblyLoadContext.Unload simplemente inicia la descarga. La descarga finaliza una vez que:
Ningún subproceso tiene métodos de los ensamblados cargados en el AssemblyLoadContext en sus pilas de
llamadas.
Ninguno de los elementos siguientes hace referencia a los tipos de los ensamblados cargados en el
AssemblyLoadContext , a las instancias de esos tipos ni a los ensamblados mismos:
Referencias fuera del AssemblyLoadContext , excepto las referencias parciales (WeakReference o
WeakReference<T>).
Un recolector de elementos no utilizados (GC) potente controla (GCHandleType.Normal o
GCHandleType.Pinned) desde dentro y fuera de AssemblyLoadContext .
Como puede ver, el método Load devuelve null . Esto significa que todos los ensamblados de dependencias se
cargan en el contexto predeterminado y el contexto nuevo solo incluye los ensamblados que se cargaron
explícitamente en él.
Si quiere cargar algunas o todas las dependencias también en el AssemblyLoadContext , puede usar
AssemblyDependencyResolver en el método Load . AssemblyDependencyResolver resuelve los nombres de
ensamblado en rutas de acceso absoluto a archivos de ensamblado. El solucionador utiliza el archivo .deps.json y
los archivos de ensamblado en el directorio del ensamblado principal que se cargó en el contexto.
using System.Reflection;
using System.Runtime.Loader;
namespace complex
{
class TestAssemblyLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
return null;
}
}
}
Para cada uno de los ensamblados a los que hace referencia el ensamblado cargado, se llama al método
TestAssemblyLoadContext.Load para que el TestAssemblyLoadContext pueda decidir desde dónde obtener el
ensamblado. En nuestro caso, devuelve null para indicar que se debe cargar en el contexto predeterminado
desde las ubicaciones que el runtime usa para cargar los ensamblados de manera predeterminada.
Ahora que se cargó un ensamblado, puede ejecutar un método desde él. Ejecute el método Main :
Una vez que el método Main devuelve algún resultado, puede iniciar la descarga ya sea llamando al método
Unload en el AssemblyLoadContext personalizado o deshaciéndose de la referencia que hace al
AssemblyLoadContext :
alc.Unload();
Esto es suficiente para descargar el ensamblado de prueba. Para que las referencias de ranura de pila (ya sean
variables locales reales o introducidas por JIT) no puedan mantener activos los objetos TestAssemblyLoadContext ,
Assembly y MethodInfo (el Assembly.EntryPoint ), pongamos todos los elementos en un método no insertable
independiente. Eso podría mantener activo al TestAssemblyLoadContext e impedir la descarga.
Además, devuelva una referencia parcial al AssemblyLoadContext para que pueda usarlo más adelante para
detectar la finalización de la descarga.
[MethodImpl(MethodImplOptions.NoInlining)]
static int ExecuteAndUnload(string assemblyPath, out WeakReference alcWeakRef)
{
var alc = new TestAssemblyLoadContext();
Assembly a = alc.LoadFromAssemblyPath(assemblyPath);
alc.Unload();
return result;
}
Ahora puede ejecutar esta función para cargar, ejecutar y descargar el ensamblado.
WeakReference testAlcWeakRef;
int result = ExecuteAndUnload("absolute/path/to/your/assembly", out testAlcWeakRef);
Sin embargo, la descarga no se completa de inmediato. Como se mencionó anteriormente, se basa en el recolector
de elementos no utilizados para recopilar todos los objetos desde el ensamblado de prueba. En muchos casos, no
es necesario esperar a que se complete la descarga. Sin embargo, hay casos en los que resulta útil saber que la
descarga se completó. Por ejemplo, es posible que quiera eliminar el archivo de ensamblado que se cargó en el
AssemblyLoadContext personalizado del disco. En tal caso, se puede usar el fragmento de código siguiente.
Desencadena una recolección de elementos no utilizados y espera a los finalizadores pendientes en un bucle hasta
que la referencia parcial al AssemblyLoadContext personalizado se establece en null , lo que indica que se recopiló
el objeto de destino. En la mayoría de los casos, solo se requiere un paso a través del bucle. Sin embargo, en casos
más complejos en los que los objetos creados por el código que se ejecutan en el AssemblyLoadContext tienen
finalizadores, es posible que se necesiten más pasos.
for (int i = 0; testAlcWeakRef.IsAlive && (i < 10); i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
El evento de descarga
En algunos casos, puede ser necesario que el código cargado en un AssemblyLoadContext personalizado realice
una limpieza cuando se inicie la descarga. Por ejemplo, es posible que tenga que detener subprocesos o limpiar los
identificadores de los recolectores de elementos no utilizados seguros, etc. En esos casos, se puede usar el evento
Unloading . Es posible enlazar a este evento un controlador que realice la limpieza necesaria.
TIP
Se pueden producir referencias a objetos que se almacenan en ranuras de pilas o en los registros de procesador y que
podrían impedir la descarga de un AssemblyLoadContext en las siguientes situaciones:
Cuando los resultados de una llamada de función se pasan directamente a otra función incluso si no hay ninguna variable
local creada por el usuario.
Cuando el compilador JIT mantiene una referencia a un objeto que estaba disponible en algún momento de un método.
En LLDB:
Vamos a depurar un programa de ejemplo que tiene problemas con la descarga. El código de origen se incluye a
continuación. Cuando lo ejecuta en WinDbg, el programa irrumpe en el depurador justo después de intentar
comprobar que la descarga se realizó correctamente. Luego, puede empezar a buscar a los culpables.
TIP
Si realiza la depuración con LLDB en Unix, los comandos de SOS de los ejemplos siguientes no tienen ! delante de ellos.
Este comando vuelca todos los objetos con un nombre de tipo que contiene LoaderAllocator que se encuentran
en el montón de GC. A continuación se muestra un ejemplo:
Address MT Size
000002b78000ce40 00007ffadc93a288 48
000002b78000ceb0 00007ffadc93a218 24
Statistics:
MT Count TotalSize Class Name
00007ffadc93a218 1 24 System.Reflection.LoaderAllocatorScout
00007ffadc93a288 1 48 System.Reflection.LoaderAllocator
Total 2 objects
En la parte "Statistics:" (Estadísticas) que aparece abajo, compruebe el MT ( MethodTable ) que pertenece al
System.Reflection.LoaderAllocator , que es el objeto que nos interesa. Luego, en la lista que aparece al principio,
busque la entrada con el MT que coincide con ese y obtenga la dirección del objeto mismo. En nuestro caso, se
trata de "000002b78000ce40".
Ahora que sabemos la dirección del objeto LoaderAllocator , podemos usar otro comando para encontrar sus
raíces de GC:
Este comando vuelca la cadena de referencias de objeto que llevan a la instancia LoaderAllocator . La lista empieza
por la raíz, que es la entidad que mantiene activo el LoaderAllocator y, por lo tanto, es la parte fundamental del
problema. La raíz puede ser una ranura de pila, un registro de procesador, un identificador de GC o una variable
estática.
Este es un ejemplo de la salida del comando gcroot :
Thread 4ac:
000000cf9499dd20 00007ffa7d0236bc example.Program.Main(System.String[])
[E:\unloadability\example\Program.cs @ 70]
rbp-20: 000000cf9499dd90
-> 000002b78000d328 System.Reflection.RuntimeMethodInfo
-> 000002b78000d1f8 System.RuntimeType+RuntimeTypeCache
-> 000002b78000d1d0 System.RuntimeType
-> 000002b78000ce40 System.Reflection.LoaderAllocator
HandleTable:
000002b7f8a81198 (strong handle)
-> 000002b78000d948 test.Test
-> 000002b78000ce40 System.Reflection.LoaderAllocator
Found 3 roots.
El paso siguiente es averiguar dónde se encuentra la raíz para poder corregirla. El caso más sencillo es cuando la
raíz es una ranura de pila o un registro de procesador. En ese caso, gcroot muestra el nombre de la función cuyo
marco contiene la raíz y el subproceso que ejecuta esa función. El caso difícil es cuando la raíz es una variable
estática o un identificador de GC.
En el ejemplo anterior, la primera raíz es una variable local de tipo System.Reflection.RuntimeMethodInfo
almacenada en el marco de la función example.Program.Main(System.String[]) en la dirección rbp-20 ( rbp es el
registro de procesador rbp y -20 es un desplazamiento hexadecimal a partir de ese registro).
La segunda raíz es un GCHandle normal (seguro) que contiene una referencia a una instancia de la clase
test.Test .
La tercera raíz es un GCHandle anclado. Esta es en realidad una variable estática, pero lamentablemente, no es
posible saberlo a ciencia cierta. Las variables estáticas para tipos de referencia se almacenan en una matriz de
objetos administrada en estructuras internas de runtime.
Otro caso que puede impedir la descarga de un AssemblyLoadContext es cuando un subproceso tiene un marco de
un método proveniente de un ensamblado cargado en el AssemblyLoadContext en su pila. Puede comprobarlo si
vuelca las pilas de llamadas administradas de todos los subprocesos:
~*e !clrstack
El comando siguiente "aplicar a todos los subprocesos el comando !clrstack ". Luego se muestra la salida de ese
comando para el ejemplo. Lamentablemente, LLDB en Unix no tiene forma de aplicar un comando a todos los
subprocesos, por lo que tendrá que cambiar de manera manual los subprocesos y repetir el comando clrstack .
Omita todos los subprocesos en los que el depurador indique "Unable to walk the managed stack" (No se puede
recorrer la pila administrada).
OS Thread Id: 0x6ba8 (0)
Child SP IP Call Site
0000001fc697d5c8 00007ffb50d9de12 [HelperMethodFrame: 0000001fc697d5c8]
System.Diagnostics.Debugger.BreakInternal()
0000001fc697d6d0 00007ffa864765fa System.Diagnostics.Debugger.Break()
0000001fc697d700 00007ffa864736bc example.Program.Main(System.String[]) [E:\unloadability\example\Program.cs @
70]
0000001fc697d998 00007ffae5fdc1e3 [GCFrame: 0000001fc697d998]
0000001fc697df28 00007ffae5fdc1e3 [GCFrame: 0000001fc697df28]
OS Thread Id: 0x2ae4 (1)
Unable to walk the managed stack. The current thread is likely not a
managed thread. You can run !threads to get a list of managed threads in
the process
Failed to start stack walk: 80070057
OS Thread Id: 0x61a4 (2)
Unable to walk the managed stack. The current thread is likely not a
managed thread. You can run !threads to get a list of managed threads in
the process
Failed to start stack walk: 80070057
OS Thread Id: 0x7fdc (3)
Unable to walk the managed stack. The current thread is likely not a
managed thread. You can run !threads to get a list of managed threads in
the process
Failed to start stack walk: 80070057
OS Thread Id: 0x5390 (4)
Unable to walk the managed stack. The current thread is likely not a
managed thread. You can run !threads to get a list of managed threads in
the process
Failed to start stack walk: 80070057
OS Thread Id: 0x5ec8 (5)
Child SP IP Call Site
0000001fc70ff6e0 00007ffb5437f6e4 [DebuggerU2MCatchHandlerFrame: 0000001fc70ff6e0]
OS Thread Id: 0x4624 (6)
Child SP IP Call Site
GetFrameContext failed: 1
0000000000000000 0000000000000000
OS Thread Id: 0x60bc (7)
Child SP IP Call Site
0000001fc727f158 00007ffb5437fce4 [HelperMethodFrame: 0000001fc727f158]
System.Threading.Thread.SleepInternal(Int32)
0000001fc727f260 00007ffb37ea7c2b System.Threading.Thread.Sleep(Int32)
0000001fc727f290 00007ffa865005b3 test.Program.ThreadProc() [E:\unloadability\test\Program.cs @ 17]
0000001fc727f2c0 00007ffb37ea6a5b System.Threading.Thread.ThreadMain_ThreadStart()
0000001fc727f2f0 00007ffadbc4cbe3
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext,
System.Threading.ContextCallback, System.Object)
0000001fc727f568 00007ffae5fdc1e3 [GCFrame: 0000001fc727f568]
0000001fc727f7f0 00007ffae5fdc1e3 [DebuggerU2MCatchHandlerFrame: 0000001fc727f7f0]
Como puede ver, el último subproceso tiene test.Program.ThreadProc() . Esta es una función del ensamblado
cargado en el AssemblyLoadContext , por lo que mantiene activo al AssemblyLoadContext .
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;
namespace example
namespace example
{
class TestAssemblyLoadContext : AssemblyLoadContext
{
public TestAssemblyLoadContext() : base(true)
{
}
protected override Assembly Load(AssemblyName name)
{
return null;
}
}
class TestInfo
{
public TestInfo(MethodInfo mi)
{
entryPoint = mi;
}
MethodInfo entryPoint;
}
class Program
{
static TestInfo entryPoint;
[MethodImpl(MethodImplOptions.NoInlining)]
static int ExecuteAndUnload(string assemblyPath, out WeakReference testAlcWeakRef, out MethodInfo
testEntryPoint)
{
var alc = new TestAssemblyLoadContext();
testAlcWeakRef = new WeakReference(alc);
Assembly a = alc.LoadFromAssemblyPath(assemblyPath);
if (a == null)
{
testEntryPoint = null;
Console.WriteLine("Loading the test assembly failed");
return -1;
}
// Issue preventing unloading #1 - we keep MethodInfo of a method for an assembly loaded into the
TestAssemblyLoadContext in a static variable
entryPoint = new TestInfo(a.EntryPoint);
testEntryPoint = a.EntryPoint;
return result;
}
System.Diagnostics.Debugger.Break();
System.Diagnostics.Debugger.Break();
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace test
{
class Test
{
string message = "Hello";
}
class Program
{
public static void ThreadProc()
{
// Issue preventing unloading #4 - a thread running method inside of the TestAssemblyLoadContext
at the unload time
Thread.Sleep(Timeout.Infinite);
}
return 1;
}
}
}
Introducción a la creación de versiones de .NET Core
16/09/2020 • 10 minutes to read • Edit Online
.NET Core se refiere al runtime y el SDK de .NET Core, que contiene las herramientas que necesita para desarrollar
aplicaciones. Los SDK de .NET Core están diseñados para funcionar con cualquier versión anteriores del runtime
de .NET Core. En este artículo se explican el runtime y la estrategia de la versión del SDK. En el artículo .NET
Standard se ofrece una explicación de los números de versión de .NET Standard.
El runtime y el SDK de .NET Core agregan características a un ritmo diferente; en general, el SDK de .NET Core
incluye herramientas actualizadas antes de que el runtime de .NET Core cambie el runtime que se usa en
producción.
C A M B IO RUN T IM E DE . N ET C O RE SDK DE . N ET C O RE ( *)
(*) En este gráfico se usa el entorno de ejecución 2.2 de .NET Core como ejemplo, ya que un artefacto histórico
indica que el primer SDK de .NET Core 2.1 es el de la versión 2.1.300. Para obtener más información, consulte
.Selección de la versión de .NET Core.
NOTAS:
Si el SDK tiene 10 actualizaciones de características anteriores a la actualización de una de las características
del runtime, los números de versión progresarán en la serie de 1000 con números como 2.2.1000, tal como la
publicación de características posterior a la versión 2.2.900. No se espera que esta situación llegue a
producirse.
Nunca se publicarán 99 versiones sin publicar ninguna característica. Si una versión se acercase a este
número, se forzaría una publicación de características.
Puede ver más detalles en la propuesta inicial, que forma parte del repositorio dotnet/designs.
MAJOR.MINOR.PATCH[-PRERELEASE-BUILDNUMBER]
Los elementos opcionales PRERELEASE y BUILDNUMBER nunca forman parte de las versiones compatibles y solo
están presentes en las compilaciones nocturnas, en las compilaciones locales a partir de destinos de origen y en
las versiones preliminares no compatibles.
Comprender los cambios en el número de versión del runtime
MAJOR se incrementa cuando:
Se producen cambios importantes en el producto, o bien este toma una nueva dirección.
Se han realizado cambios importantes. Hay un nivel de aceptación de cambios importantes elevado.
Ya no se admite una versión antigua.
Se adopta una versión MAJOR más reciente de una dependencia existente.
. N ET C O RE . N ET STA N DA RD
Para obtener una tabla interactiva de las versiones de .NET Standard y ver cómo se corresponden con las
implementaciones de .NET, vea Versiones de .NET Standard.
Vea también
Marcos de trabajo de destino
Empaquetado de distribución de .NET Core
Hoja de información sobre el ciclo de vida de compatibilidad de .NET Core
.NET Core 2+ Version Binding (Enlace de versión de .NET Core 2+)
Imágenes de Docker para .NET Core
Selección de la versión de .NET Core que se va a
usar
16/09/2020 • 12 minutes to read • Edit Online
En este artículo se explican las directivas que usan las herramientas de .NET Core, el SDK y el runtime para la
selección de versiones. Estas directivas proporcionan un equilibrio entre la ejecución de aplicaciones con las
versiones especificadas y facilitan la actualización de los equipos del desarrollador y el usuario final. Estas
directivas realizan las siguientes acciones:
La implementación sencilla y eficaz de .NET Core, incluidas las actualizaciones de seguridad y confiabilidad.
Usar las herramientas y los comandos más recientes independientemente del runtime de destino.
La selección de versiones se produce:
Al ejecutar un comando del SDK, el SDK usa la versión instalada más reciente.
Al compilar un ensamblado, los monikers de la plataforma de destino definen las API de tiempo de
compilación.
Al ejecutar una aplicación .NET Core, las aplicaciones dependientes de la plataforma de destino se ponen al
día.
Al publicar una aplicación autocontenida, las implementaciones autocontenidas incluyen el runtime
seleccionado.
En el resto de este documento se examinan los cuatro escenarios.
<TargetFramework>netcoreapp3.0</TargetFramework>
Puede compilar el proyecto con varios TFM. Configurar varias plataformas de destino es más común para las
bibliotecas, pero también se puede hacer con las aplicaciones. Especifique una propiedad TargetFrameworks (el
plural de TargetFramework ). Las plataformas de destino se delimitan por punto y coma, como se muestra en el
ejemplo siguiente:
<TargetFrameworks>netcoreapp3.0;net47</TargetFrameworks>
Un SDK determinado admite un conjunto fijo de plataformas, limitado a la plataforma de destino del runtime
con el que se suministra. Por ejemplo, el SDK de .NET Core 3.0 incluye el runtime .NET Core 3.0, que es una
implementación de la plataforma de destino netcoreapp3.0 . El SDK de .NET Core 3.0 admite netcoreapp2.1 ,
netcoreapp2.2 y netcoreapp3.0 , pero no netcoreapp3.1 (o una versión posterior). El SDK de .NET Core 3.1 se
instala para compilar para netcoreapp3.1 .
Las plataformas de destino de .NET Standard también se limitan a la plataforma de destino del runtime con el
que se incluye el SDK. El SDK de .NET Core 3.1 está limitado a netstandard2.1 . Para más información, consulte
.NET Standard.
<RuntimeFrameworkVersion>3.0.3</RuntimeFrameworkVersion>
Vea también
Descarga e instalación de .NET Core
Eliminación de los componentes .NET Core Runtime y SDK
Opciones de configuración en tiempo de ejecución
de .NET Core
16/09/2020 • 6 minutes to read • Edit Online
.NET Core admite el uso de archivos de configuración y variables de entorno para configurar el comportamiento
de las aplicaciones .NET Core en tiempo de ejecución. La configuración en tiempo de ejecución es una opción
atractiva si:
No se posee ni controla el código fuente de una aplicación y, por tanto, no puede configurarlo mediante
programación.
Varias instancias de la aplicación se ejecutan al mismo tiempo en un solo sistema y se quiere configurar
cada una para un rendimiento óptimo.
NOTE
Esta documentación está en desarrollo. Si observa que la información que se presenta aquí está incompleta o es inexacta,
abra una incidencia para informarnos sobre ella o envíe una solicitud de incorporación de cambios para solucionarla. Para
obtener información sobre el envío de solicitudes de incorporación de cambios para el repositorio dotnet/docs, consulte la
guía del colaborador.
.NET Core ofrece los siguientes mecanismos para configurar el comportamiento de las aplicaciones en tiempo de
ejecución:
El archivo runtimeconfig.json
Propiedades de MSBuild
Variables de entorno
TIP
El hecho de configurar una opción relativa al tiempo de ejecución mediante el uso de una variable de entorno aplica la
configuración a todas las aplicaciones de .NET Core. Sin embargo, si se configura una opción relativa al tiempo de ejecución
en runtimeconfig.json o en el archivo del proyecto, la configuración solo afectará a la aplicación en cuestión.
runtimeconfig.json
Cuando un proyecto se compila, se genera un archivo [nombre_aplicación].runtimeconfig.json en el directorio de
salida. Si un archivo runtimeconfig.template.json existe en la misma carpeta que el archivo de proyecto, las
opciones de configuración que contiene se combinan en el archivo [nombre_aplicación].runtimeconfig.json. Si va a
compilar la aplicación, coloque las opciones de configuración en el archivo runtimeconfig.template.json. Si solo va
a ejecutar la aplicación, insértelas directamente en el archivo [nombre_aplicación].runtimeconfig.json file.
NOTE
El archivo [nombre_aplicación].runtimeconfig.json se sobrescribirá en las compilaciones posteriores.
Especifique las opciones de configuración de runtime en la sección configProper ties de los archivos
runtimeconfig.json. Esta sección tiene el formato siguiente:
"configProperties": {
"config-property-name1": "config-value1",
"config-property-name2": "config-value2"
}
{
"runtimeOptions": {
"tfm": "netcoreapp3.1",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "3.1.0"
},
"configProperties": {
"System.GC.Concurrent": false,
"System.Threading.ThreadPool.MinThreads": 4,
"System.Threading.ThreadPool.MaxThreads": 25
}
}
}
{
"configProperties": {
"System.GC.Concurrent": false,
"System.Threading.ThreadPool.MinThreads": "4",
"System.Threading.ThreadPool.MaxThreads": "25"
}
}
propiedades de MSBuild
Algunas opciones de configuración de runtime se pueden establecer mediante propiedades de MSBuild en el
archivo .csproj o .vbproj de proyectos de .NET Core de estilo SDK. Las propiedades de MSBuild tienen prioridad
sobre las opciones establecidas en el archivo runtimeconfig.template.json. También sobrescriben las opciones
establecidas en el archivo [nombre_aplicación].runtimeconfig.json en tiempo de compilación.
Este es un ejemplo de archivo de proyecto de estilo SDK con propiedades de MSBuild para configurar el
comportamiento de runtime:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<ConcurrentGarbageCollection>false</ConcurrentGarbageCollection>
<ThreadPoolMinThreads>4</ThreadPoolMinThreads>
<ThreadPoolMaxThreads>25</ThreadPoolMaxThreads>
</PropertyGroup>
</Project>
Las propiedades de MSBuild para configurar el comportamiento de runtime se indican en los artículos
individuales de cada área, por ejemplo, la recolección de elementos no utilizados. También se enumeran en la
sección sobre la configuración del tiempo de ejecución de la referencia de las propiedades de MSBuild para los
proyectos de estilo de SDK.
Variables de entorno
Las variables de entorno se pueden usar para proporcionar información de configuración del entorno de
ejecución. El hecho de configurar una opción relativa al tiempo de ejecución mediante el uso de una variable de
entorno aplica la configuración a todas las aplicaciones de .NET Core. Los botones de configuración especificados
como variables de entorno generalmente tienen el prefijo COMPlus_ .
Puede definir variables de entorno desde el Panel de control de Windows, en la línea de comandos o mediante
programación llamando al método Environment.SetEnvironmentVariable(String, String) en sistemas basados en
Windows y Unix.
En los siguientes ejemplos se muestra cómo establecer una variable de entorno en la línea de comandos:
# Windows
set COMPlus_GCRetainVM=1
# Powershell
$env:COMPlus_GCRetainVM="1"
# Unix
export COMPlus_GCRetainVM=1
Opciones de configuración del entorno de ejecución
para compilación
16/09/2020 • 4 minutes to read • Edit Online
Compilación en niveles
Configura si el compilador Just-In-Time (JIT) usa la compilación en niveles. La compilación en niveles realiza la
transición de métodos mediante dos niveles:
El primer nivel genera código más rápidamente (JIT rápido) o carga código previamente compilado
(ReadyToRun).
El segundo nivel genera código optimizado en segundo plano ("JIT de optimización").
En la versión 3.0 de .NET Core y posteriores, la compilación en niveles está habilitada de forma predeterminada.
En las versiones 2.1 y 2.2 de .NET Core, la compilación en niveles está deshabilitada de forma predeterminada.
Para obtener más información, vea la Guía de compilación en niveles.
N O M B RE DE VA LO R VA LO RES
Ejemplos
Archivo runtimeconfig.json:
{
"runtimeOptions": {
"configProperties": {
"System.Runtime.TieredCompilation": false
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TieredCompilation>false</TieredCompilation>
</PropertyGroup>
</Project>
JIT rápido
Configura si el compilador JIT usa JIT rápido. En el caso de los métodos que no contienen bucles y para los que
no hay código compilado previamente, JIT rápido los compila más rápidamente, pero sin optimizaciones.
La habilitación de JIT rápido reduce el tiempo de inicio, pero puede generar código con características de
rendimiento degradadas. Por ejemplo, el código puede usar más espacio de pila, asignar más memoria y
ejecutarse más lentamente.
Si JIT rápido está deshabilitado pero la compilación en niveles está habilitada, solo el código compilado
previamente participa en la compilación en niveles. Si un método no está compilado previamente con
ReadyToRun, el comportamiento de JIT es el mismo que si la compilación en niveles estuviera deshabilitada.
En .NET Core 3.0 y versiones posteriores, JIT rápido está habilitado de forma predeterminada.
En .NET Core 2.1 y 2.2, JIT rápido está deshabilitado de forma predeterminada.
N O M B RE DE VA LO R VA LO RES
runtimeconfig.json System.Runtime.TieredCompilation.QuickJit
true : habilitado.
false : deshabilitado.
Ejemplos
Archivo runtimeconfig.json:
{
"runtimeOptions": {
"configProperties": {
"System.Runtime.TieredCompilation.QuickJit": false
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TieredCompilationQuickJit>false</TieredCompilationQuickJit>
</PropertyGroup>
</Project>
Ejemplos
Archivo runtimeconfig.json:
{
"runtimeOptions": {
"configProperties": {
"System.Runtime.TieredCompilation.QuickJitForLoops": false
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TieredCompilationQuickJitForLoops>true</TieredCompilationQuickJitForLoops>
</PropertyGroup>
</Project>
ReadyToRun
Configura si el entorno de ejecución de .NET Core usa código precompilado para las imágenes con datos de
ReadyToRun disponibles. Al deshabilitar esta opción, se fuerza al entorno de ejecución a compilar código de
marco mediante JIT.
Para obtener más información, vea ReadyToRun.
Si se omite esta configuración, .NET usa datos ReadyToRun cuando están disponibles. Esto es equivalente a
establecer el valor en 1 .
N O M B RE DE VA LO R VA LO RES
Habilitación de diagnósticos
Configura si el depurador, el generador de perfiles y los diagnósticos de EventPipe están habilitados o
deshabilitados.
Si se omite esta configuración, se habilitan los diagnósticos. Esto es equivalente a establecer el valor en 1 .
N O M B RE DE VA LO R VA LO RES
N O M B RE DE VA LO R VA LO RES
N O M B RE DE VA LO R VA LO RES
N O M B RE DE VA LO R VA LO RES
N O M B RE DE VA LO R VA LO RES
NOTE
Este valor se omite si se omite COMPlus_PerfMapEnabled o se establece en 0 (es decir, deshabilitado).
Opciones de configuración del entorno de ejecución
para la recolección de elementos no utilizados
16/09/2020 • 29 minutes to read • Edit Online
Esta página contiene información sobre la configuración del recolector de elementos no utilizados (GC) que se
puede cambiar en el entorno de ejecución. Si intenta lograr el máximo rendimiento de una aplicación en
ejecución, valore la posibilidad de usar esta configuración. Sin embargo, los valores predeterminados
proporcionan un rendimiento óptimo para la mayoría de aplicaciones en situaciones habituales.
En esta página, la configuración se organiza en grupos. La configuración de cada grupo se usa normalmente junto
con las otras para lograr un resultado concreto.
NOTE
La aplicación también puede cambiar dinámicamente esta configuración mientras se ejecuta, por lo que se puede
invalidar cualquier valor del entorno de ejecución que haya establecido.
Por lo general, algunos valores de configuración, como el nivel de latencia, se establecen únicamente a través de la API
en tiempo de diseño. Estos valores se omiten en esta página.
En el caso de los valores numéricos, use la notación decimal para la configuración del archivo runtimeconfig.json y la
notación hexadecimal para la configuración de las variables de entorno. Para los valores hexadecimales, puede
especificarlos con o sin el prefijo "0x".
Ejemplos
Archivo runtimeconfig.json:
{
"runtimeOptions": {
"configProperties": {
"System.GC.Server": true
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
</Project>
GC en segundo plano
Configura si está habilitada la recolección de elementos no utilizados en segundo plano (simultánea).
Predeterminado: uso de GC en segundo plano. Esto es equivalente a establecer el valor en true .
Para más información, consulte Recolección de elementos no utilizados en segundo plano.
Ejemplos
Archivo runtimeconfig.json:
{
"runtimeOptions": {
"configProperties": {
"System.GC.Concurrent": false
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ConcurrentGarbageCollection>false</ConcurrentGarbageCollection>
</PropertyGroup>
</Project>
Ejemplo:
{
"runtimeOptions": {
"configProperties": {
"System.GC.HeapCount": 16
}
}
}
TIP
Si configura la opción en runtimeconfig.json, especifique un valor decimal. Si configura la opción como una variable de
entorno, especifique un valor hexadecimal. Por ejemplo, para limitar el número de montones a 16, los valores serían 16 para
el archivo JSON y 0x10 o 10 para la variable de entorno.
Ejemplo:
{
"runtimeOptions": {
"configProperties": {
"System.GC.HeapAffinitizeMask": 1023
}
}
}
Affinitize ranges (Intervalos de afinidad)
Especifica la lista de procesadores que se van a usar para los subprocesos del recolector de elementos no
utilizados.
Este valor es similar a System.GC.HeapAffinitizeMask, salvo que permite especificar más de 64 procesadores.
En el caso de los sistemas operativos Windows, agregue el prefijo con el grupo de CPU correspondiente al
número o el rango del procesador, por ejemplo, "0:1-10,0:12,1:50-52,1:70".
Si la afinidad del procesador de GC está deshabilitada, esta configuración se ignora.
Solo se aplica a la recolección de elementos no utilizados del servidor.
Para obtener más información, vea el artículo del blog de Maoni Stephens sobre la mejora de la configuración
de la CPU para la GC en máquinas con > 64 CPU.
Ejemplo:
{
"runtimeOptions": {
"configProperties": {
"System.GC.GCHeapAffinitizeRanges": "0:1-10,0:12,1:50-52,1:70"
}
}
}
Grupos de CPU
Configura si el recolector de elementos no utilizados usa grupos de CPU o no.
Cuando un equipo Windows de 64 bits tiene varios grupos de CPU, es decir, hay más de 64 procesadores,
la habilitación de este elemento amplía la recolección de elementos no utilizados en todos los grupos de
CPU. El recolector de elementos no utilizados usa todos los núcleos para crear y equilibrar montones.
Solo se aplica a la recolección de elementos no utilizados del servidor en sistemas operativos Windows de
64 bits.
Predeterminado: GC no se extiende por los grupos de CPU. Esto es equivalente a establecer el valor en 0 .
Para obtener más información, vea el artículo del blog de Maoni Stephens sobre la mejora de la
configuración de la CPU para la GC en máquinas con > 64 CPU.
N O M B RE DE VA LO R VA LO RES VERSIÓ N IN T RO DUC IDA
NOTE
Para configurar Common Language Runtime (CLR) con el fin de distribuir también los subprocesos del grupo de
subprocesos entre todos los grupos de CPU, habilite la opción Elemento Thread_UseAllCpuGroups. En el caso de las
aplicaciones de .NET Core, se puede habilitar esta opción estableciendo el valor de la variable de entorno
COMPlus_Thread_UseAllCpuGroups en 1 .
Affinitize (Afinidad)
Especifica si establecer afinidad entre subprocesos de recolección de elementos no utilizados con
procesadores. El hecho de establecer afinidad entre un subproceso de GC significa que solo puede ejecutarse
en su CPU concreta. Se crea un montón para cada subproceso de GC.
Solo se aplica a la recolección de elementos no utilizados del servidor.
Predeterminado: establecer afinidad entre subprocesos de recolección de elementos no utilizados con
procesadores. Esto es equivalente a establecer el valor en false .
app.config para .NET GCNoAffinitize false : establecer afinidad. .NET Framework 4.6.2
Framework true : no establecer
afinidad.
Ejemplo:
{
"runtimeOptions": {
"configProperties": {
"System.GC.NoAffinitize": true
}
}
}
Ejemplo:
{
"runtimeOptions": {
"configProperties": {
"System.GC.HeapHardLimit": 209715200
}
}
}
TIP
Si configura la opción en runtimeconfig.json, especifique un valor decimal. Si configura la opción como una variable de
entorno, especifique un valor hexadecimal. Por ejemplo, para especificar un límite de montón de 200 mebibytes (MiB), los
valores serían 209715200 para el archivo JSON y 0xC800000 o C800000 para la variable de entorno.
Ejemplo:
{
"runtimeOptions": {
"configProperties": {
"System.GC.HeapHardLimitPercent": 30
}
}
}
TIP
Si configura la opción en runtimeconfig.json, especifique un valor decimal. Si configura la opción como una variable de
entorno, especifique un valor hexadecimal. Por ejemplo, para limitar el uso del montón al 30 %, los valores serían 30 para el
archivo JSON y 0x1E o 1E para la variable de entorno.
TIP
Si configura la opción en runtimeconfig.json, especifique un valor decimal. Si configura la opción como una variable de
entorno, especifique un valor hexadecimal. Por ejemplo, para especificar un límite de montón de 200 mebibytes (MiB), los
valores serían 209715200 para el archivo JSON y 0xC800000 o C800000 para la variable de entorno.
TIP
Si configura la opción en runtimeconfig.json, especifique un valor decimal. Si configura la opción como una variable de
entorno, especifique un valor hexadecimal. Por ejemplo, para limitar el uso del montón al 30 %, los valores serían 30 para el
archivo JSON y 0x1E o 1E para la variable de entorno.
NOTE
En el caso de los procesos que se ejecutan en un contenedor, la recolección de elementos no utilizados considera la
memoria física según el límite del contenedor.
TIP
Si configura la opción en runtimeconfig.json, especifique un valor decimal. Si configura la opción como una variable de
entorno, especifique un valor hexadecimal. Por ejemplo, para establecer el umbral de uso de memoria alto en el 75 %, los
valores serían 75 para el archivo JSON y 0x4B o 4B para la variable de entorno.
Retain VM (Conservar VM )
Configura si los segmentos que se deben eliminar se ponen en una lista en espera para usarlos en el futuro o
se devuelven al sistema operativo (SO).
Predeterminado: devolver los segmentos al sistema operativo. Esto es equivalente a establecer el valor en
false .
Ejemplos
Archivo runtimeconfig.json:
{
"runtimeOptions": {
"configProperties": {
"System.GC.RetainVM": true
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RetainVMGarbageCollection>true</RetainVMGarbageCollection>
</PropertyGroup>
</Project>
Páginas grandes
Especifica si se deben usar páginas grandes cuando se establece un límite máximo de montones.
Predeterminado: no utilizar páginas grandes cuando se establezca un límite rígido de montones. Esto es
equivalente a establecer el valor en 0 .
Se trata de un valor de configuración experimental.
Ejemplo:
{
"runtimeOptions": {
"configProperties": {
"System.GC.LOHThreshold": 120000
}
}
}
TIP
Si configura la opción en runtimeconfig.json, especifique un valor decimal. Si configura la opción como una variable de
entorno, especifique un valor hexadecimal. Por ejemplo, para establecer un tamaño de umbral de 120 000 bytes, los valores
serían 120000 para el archivo JSON y 0x1D4C0 o 1D4C0 para la variable de entorno.
GC independiente
Especifica una ruta de acceso a la biblioteca que contiene el recolector de elementos no utilizados que el
entorno de ejecución pretende cargar.
Para obtener más información, vea Diseño del cargador de GC independiente.
Modo invariable
Determina si una aplicación de .NET Core se ejecuta en modo invariable de globalización sin tener acceso a los
datos y el comportamiento concretos de la referencia cultural.
Si se omite esta configuración, la aplicación se ejecuta con acceso a los datos culturales. Esto es equivalente a
establecer el valor en false .
Para obtener más información, vea Modo invariable de globalización de .NET Core.
N O M B RE DE VA LO R VA LO RES
Ejemplos
Archivo runtimeconfig.json:
{
"runtimeOptions": {
"configProperties": {
"System.Globalization.Invariant": true
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
</Project>
N O M B RE DE VA LO R VA LO RES
N O M B RE DE VA LO R VA LO RES
N O M B RE DE VA LO R VA LO RES
NLS
Determina si .NET usa las API de globalización de compatibilidad con el idioma nacional (NLS) o componentes
internacionales para Unicode (ICU) para aplicaciones de Windows. .NET 5.0 y versiones posteriores usan las API
de globalización de ICU de forma predeterminada en la actualización de mayo de 2019 de Windows 10 o
versiones posteriores.
Si se omite esta configuración, .NET usa las API de globalización de ICU forma predeterminada. Esto es
equivalente a establecer el valor en false .
Para obtener más información, consulte Las API de globalización usan bibliotecas ICU en Windows.
N O M B RE DE VA LO R VA LO RES IN C L USIÓ N
Protocolo HTTP/2
Configura si está habilitada la compatibilidad con el protocolo HTTP/2.
Si se omite esta configuración, la compatibilidad con el protocolo HTTP/2 está deshabilitada. Esto es
equivalente a establecer el valor en false .
Introducido en .NET Core 3.0.
N O M B RE DE VA LO R VA LO RES
UseSocketsHttpHandler
Configura si System.Net.Http.HttpClientHandler usa System.Net.Http.SocketsHttpHandler o pilas de
protocolo HTTP anteriores (WinHttpHandler en Windows y CurlHandler , una clase interna implementada
sobre libcurl, en Linux).
NOTE
Es posible que esté usando las API para redes de alto nivel en lugar de crear directamente instancias de la
clase HttpClientHandler. Este valor también afecta a la pila del protocolo HTTP que usan las API para redes de alto
nivel, como HttpClient y HttpClientFactory.
Si se omite este valor, HttpClientHandler utiliza SocketsHttpHandler. Esto es equivalente a establecer el valor
en true .
Se puede configurar este valor mediante programación llamando al método AppContext.SetSwitch.
N O M B RE DE VA LO R VA LO RES
Grupos de CPU
Configura si los subprocesos se distribuyen automáticamente entre los grupos de CPU.
Si se omite esta configuración, los subprocesos no se distribuyen entre los grupos de CPU. Esto es equivalente a
establecer el valor en 0 .
N O M B RE DE VA LO R VA LO RES
Mínimo de subprocesos
Especifica el número mínimo de subprocesos para el grupo de subprocesos de trabajo.
Corresponde al método ThreadPool.SetMinThreads.
N O M B RE DE VA LO R VA LO RES
Ejemplos
Archivo runtimeconfig.json:
{
"runtimeOptions": {
"configProperties": {
"System.Threading.ThreadPool.MinThreads": 4
}
}
}
<PropertyGroup>
<ThreadPoolMinThreads>4</ThreadPoolMinThreads>
</PropertyGroup>
</Project>
Máximo de subprocesos
Especifica el número máximo de subprocesos para el grupo de subprocesos de trabajo.
Corresponde al método ThreadPool.SetMaxThreads.
N O M B RE DE VA LO R VA LO RES
Ejemplos
Archivo runtimeconfig.json:
{
"runtimeOptions": {
"configProperties": {
"System.Threading.ThreadPool.MaxThreads": 20
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ThreadPoolMaxThreads>20</ThreadPoolMaxThreads>
</PropertyGroup>
</Project>
Información general sobre la publicación de
aplicaciones de .NET Core
16/09/2020 • 16 minutes to read • Edit Online
Las aplicaciones que se crean con .NET Core se pueden publicar de dos modos diferentes, lo cual afecta a la
forma en la que un usuario ejecuta la aplicación.
La publicación de la aplicación como independiente genera una aplicación que incluye el entorno de
ejecución y las bibliotecas de .NET Core, así como la aplicación y sus dependencias. Los usuarios de la
aplicación pueden ejecutarla en un equipo que no tenga instalado el entorno de ejecución de .NET Core.
La publicación de la aplicación como dependiente de la plataforma genera una aplicación que incluye
únicamente la propia aplicación y sus dependencias. Los usuarios de la aplicación tienen que instalar el
entorno de ejecución de .NET Core por separado.
De forma predeterminada, ambos modos de publicación generan un archivo ejecutable específico de la
plataforma. Las aplicaciones dependientes de la plataforma se pueden crear sin un archivo ejecutable y son
multiplataforma.
Cuando se genera un archivo ejecutable, puede especificar la plataforma de destino con un identificador de
entorno de ejecución (RID). Para obtener más información sobre los RID, vea el Catálogo de identificadores
de entorno de ejecución (RID) de .NET Core.
En la tabla siguiente se describen los comandos que se usan para publicar una aplicación como
dependiente de la plataforma o independiente según la versión del SDK:
T IP O SDK 2. 1 SDK 3. X C O M A N DO
archivo ejecutable ️
✔ dotnet publish
dependiente de la
plataforma para la
plataforma actual.
archivo ejecutable ️
✔ dotnet publish -r
dependiente de la <RID> --self-
contained false
plataforma para una
plataforma específica.
archivo binario ️
✔ ️
✔ dotnet publish
multiplataforma
dependiente de la
plataforma.
archivo ejecutable ️
✔ ️
✔ dotnet publish -r
independiente <RID>
Para obtener más información, vea el artículo sobre el comando dotnet publish de .NET Core.
T IP O SDK 2. 1 SDK 3. X C O M A N DO
archivo ejecutable ️
✔ dotnet publish
dependiente de la
plataforma para la
plataforma actual.
archivo ejecutable ️
✔ dotnet publish -r
dependiente de la <RID> --self-
contained false
plataforma para una
plataforma específica.
archivo ejecutable ️
✔ ️
✔ dotnet publish -r
independiente <RID>
T IP O SDK 2. 1 SDK 3. X C O M A N DO
archivo binario ️
✔ ️
✔ dotnet publish
multiplataforma
dependiente de la
plataforma.
IMPORTANT
El SDK 2.1 de .NET Core no genera archivos ejecutables específicos de la plataforma al publicar una aplicación
dependiente de la plataforma.
dotnet publish
Sirve para publicar una aplicación multiplataforma dependiente de la plataforma. Se creará un ejecutable
de 64 bits de Linux junto con el archivo .dll. Este comando no funciona con .NET Core SDK 2.1.
TIP
Hay una característica de recorte en versión preliminar que puede ayudar a reducir todavía más el tamaño de
su desarrollo.
Sirve para publicar una aplicación independiente. Se creará un archivo ejecutable de Windows de 64 bits.
Vea también
Implementación de aplicaciones de .NET Core con la CLI de .NET Core
Implementación de aplicaciones de .NET Core con Visual Studio
Catálogo de identificadores de entorno de ejecución (RID) de .NET Core.
Selección de la versión de .NET Core que se va a usar
Implementación de aplicaciones de .NET Core con
Visual Studio
16/09/2020 • 29 minutes to read • Edit Online
Puede implementar una aplicación de .NET Core como una implementación dependiente de la plataforma, que
incluye los archivos binarios de la aplicación pero depende de la presencia de .NET Core en el sistema de destino,
o como una implementación independiente, que incluye la aplicación y los archivos binarios de .NET Core. Para
obtener información general sobre la implementación de aplicaciones de NET Core, vea Implementación de
aplicaciones .NET Core.
En las secciones siguientes se muestra cómo usar Microsoft Visual Studio para crear los siguientes tipos de
implementaciones:
Implementación dependiente de marco de trabajo
Implementación dependiente de marco de trabajo con dependencias de terceros
Implementación autocontenida
Implementación autocontenida con dependencias de terceros
Para obtener información sobre el uso de Visual Studio para desarrollar aplicaciones de .NET Core, vea
Dependencias y requisitos de .NET Core .
namespace Applications.ConsoleApps
{
public class ConsoleParser
{
public static void Main()
{
Console.WriteLine("Enter any text, followed by <Enter>:\n");
String s = Console.ReadLine();
ShowWords(s);
Console.Write("\nPress any key to continue... ");
Console.ReadKey();
}
Namespace Applications.ConsoleApps
Public Module ConsoleParser
Public Sub Main()
Console.WriteLine("Enter any text, followed by <Enter>:")
Console.WriteLine()
Dim s = Console.ReadLine()
ShowWords(s)
Console.Write($"{vbCrLf}Press any key to continue... ")
Console.ReadKey()
End Sub
using System;
using System.Text.RegularExpressions;
namespace Applications.ConsoleApps
{
public class ConsoleParser
{
public static void Main()
{
Console.WriteLine("Enter any text, followed by <Enter>:\n");
String s = Console.ReadLine();
ShowWords(s);
Console.Write("\nPress any key to continue... ");
Console.ReadKey();
}
Namespace Applications.ConsoleApps
Public Module ConsoleParser
Public Sub Main()
Console.WriteLine("Enter any text, followed by <Enter>:")
Console.WriteLine()
Dim s = Console.ReadLine()
ShowWords(s)
Console.Write($"{vbCrLf}Press any key to continue... ")
Console.ReadKey()
End Sub
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<RuntimeHostConfigurationOption Include="System.Globalization.Invariant" Value="true" />
</ItemGroup>
</Project>
<PropertyGroup>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64</RuntimeIdentifiers>
</PropertyGroup>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64</RuntimeIdentifiers>
</PropertyGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
</ItemGroup>
</Project>
Al implementar la aplicación, los archivos de aplicación también contienen las dependencias de terceros usadas en
la aplicación. No se requieren las bibliotecas de terceros en el sistema en el que se ejecuta la aplicación.
Solo puede implementar una implementación autocontenida con una biblioteca de terceros en plataformas
compatibles con esa biblioteca. Esto es similar a tener dependencias de terceros con dependencias nativas en la
implementación dependiente de la plataforma, donde las dependencias nativas no existirán en la plataforma de
destino a menos que se hayan instalado allí previamente.
Vea también
Implementación de aplicaciones .NET Core
Catálogo de identificadores de entorno de ejecución (RID) de .NET Core
Publicación de aplicaciones .NET Core con la CLI
de .NET Core
16/09/2020 • 16 minutes to read • Edit Online
En este artículo se muestra cómo se puede publicar la aplicación .NET Core desde la línea de comandos. .NET
core proporciona tres maneras de publicar las aplicaciones. La implementación dependiente del marco de
trabajo genera un archivo .dll multiplataforma que usa el runtime de .NET Core instalado localmente. La
implementación dependiente del marco de trabajo genera un archivo ejecutable específico de la plataforma que
usa el runtime de .NET Core instalado localmente. El archivo ejecutable independiente genera un archivo
ejecutable específico de la plataforma e incluye una copia local del runtime de .NET Core.
Para obtener información general sobre estos modos de publicación, vea Implementación de aplicaciones .NET
Core.
¿Busca ayuda rápida sobre el uso de la CLI? En la tabla siguiente se muestran algunos ejemplos de cómo
publicar la aplicación. La plataforma de destino se puede especificar con el parámetro -f <TFM> o si edita el
archivo de proyecto. Para más información, vea Conceptos básicos de publicación.
* Si se usa el archivo ejecutable dependiente del marco de la versión 3.0 del SDK, se trata del modo de
publicación predeterminado cuando se ejecuta el comando dotnet publish básico. Esto solo se aplica cuando
el proyecto tiene como destino .NET Core 2.1 o .NET Core 3.0 .
Aplicación de ejemplo
Puede usar la siguiente aplicación para explorar los comandos de publicación. La aplicación se crea mediante la
ejecución de los comandos siguientes en el terminal:
mkdir apptest1
cd apptest1
dotnet new console
dotnet add package Figgle
Es necesario cambiar el archivo Program.cs o Program.vb que genera la plantilla de consola por lo siguiente:
using System;
namespace apptest1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Figgle.FiggleFonts.Standard.Render("Hello, World!"));
}
}
}
Module Program
Sub Main(args As String())
Console.WriteLine(Figgle.FiggleFonts.Standard.Render("Hello, World!"))
End Sub
End Module
_ _ _ _ __ __ _ _ _
| | | | ___| | | ___ \ \ / /__ _ __| | __| | |
| |_| |/ _ \ | |/ _ \ \ \ /\ / / _ \| '__| |/ _` | |
| _ | __/ | | (_) | \ V V / (_) | | | | (_| |_|
|_| |_|\___|_|_|\___( ) \_/\_/ \___/|_| |_|\__,_(_)
|/
-r <RID> Este modificador usa un identificador (RID) para especificar la plataforma de destino. Para
obtener una lista de identificadores de tiempo de ejecución, vea Catálogo de identificadores de entorno
de ejecución (RID) de .NET Core.
--self-contained false Este modificador indica al SDK de .NET Core que cree un archivo ejecutable
como un FDE.
Siempre que se usa el modificador -r , la ruta de acceso de la carpeta de salida cambia a:
./bin/<BUILD-CONFIGURATION>/<TFM>/<RID>/publish/
Si usa la aplicación de ejemplo, ejecute dotnet publish -f netcoreapp2.2 -r win10-x64 --self-contained false .
Este comando crea el archivo ejecutable siguiente: ./bin/Debug/netcoreapp2.2/win10-x64/publish/apptest1.exe
NOTE
Puede reducir el tamaño total de la implementación si habilita el modo de globalización invariable . Este modo es útil
para las aplicaciones que no son globales y que pueden usar las convenciones de formato, las de mayúsculas y
minúsculas, y el criterio de ordenación y la comparación de cadenas de la referencia cultural invariable. Para más
información sobre el modo de globalización invariable y cómo habilitarlo, vea Modo de globalización invariable de
.NET Core.
Implementación autocontenida
Cuando se publica una implementación autocontenida (SCD), el SDK de .NET Core crea un archivo ejecutable
específico de la plataforma. La publicación de una SCD incluye todos los archivos de .NET Core necesarios para
ejecutar la aplicación, pero no incluye las dependencias nativas de .NET Core. Estas dependencias deben estar
presentes en el sistema antes de ejecutar la aplicación.
Al publicar una SCD, se crea una aplicación que no se pone al día a la revisión de seguridad de .NET Core más
reciente disponible. Para más información sobre el enlace de versión en tiempo de compilación, vea Selección
de la versión de .NET Core que se va a usar.
Debe usar los modificadores siguientes con el comando dotnet publish para publicar una SCD:
-r <RID> Este modificador usa un identificador (RID) para especificar la plataforma de destino. Para
obtener una lista de identificadores de tiempo de ejecución, vea Catálogo de identificadores de entorno
de ejecución (RID) de .NET Core.
--self-contained true Este modificador indica al SDK de .NET Core que cree un archivo ejecutable como
una SCD.
NOTE
Puede reducir el tamaño total de la implementación si habilita el modo de globalización invariable . Este modo es útil
para las aplicaciones que no son globales y que pueden usar las convenciones de formato, las de mayúsculas y
minúsculas, y el criterio de ordenación y la comparación de cadenas de la referencia cultural invariable. Para más
información sobre el modo de globalización invariable y cómo habilitarlo, vea Modo de globalización invariable de
.NET Core.
Vea también
Información general sobre la implementación de aplicaciones .NET Core
Catálogo de identificadores de entorno de ejecución (RID) de .NET Core
Creación de un paquete NuGet con la CLI de
.NET Core
18/03/2020 • 4 minutes to read • Edit Online
NOTE
A continuación se muestran ejemplos de línea de comandos mediante Unix. El comando dotnet pack , tal como se muestra
aquí, funciona del mismo modo en Windows.
Está previsto que las bibliotecas .NET Standard y .NET Core se distribuyan como paquetes NuGet. De hecho, es así
como se distribuyen y consumen todas las bibliotecas estándar .NET. Esto se hace más fácil con el comando
dotnet pack .
Imagine que acaba de escribir una nueva biblioteca sorprendente que quiere distribuir a través de NuGet. Puede
crear un paquete NuGet con herramientas multiplataforma para hacer exactamente eso. En el ejemplo siguiente,
hay una biblioteca denominada SuperAwesomeLibrar y con destino a netstandard1.0 .
Si tiene dependencias transitivas; es decir, un proyecto que depende de otro paquete, asegúrese de restaurar los
paquetes para toda la solución con el comando dotnet restore antes de crear un paquete NuGet. Si no lo hace, el
comando dotnet pack no funcionará correctamente.
No es necesario ejecutar dotnet restore porque lo ejecutan implícitamente todos los comandos que necesitan que
se produzca una restauración, como dotnet new , dotnet build , dotnet run , dotnet test , dotnet publish y
dotnet pack . Para deshabilitar la restauración implícita, use la opción --no-restore .
El comando dotnet restore sigue siendo válido en algunos escenarios donde tiene sentido realizar una
restauración explícita, como las compilaciones de integración continua en Azure DevOps Services o en los sistemas
de compilación que necesitan controlar explícitamente cuándo se produce la restauración.
Para obtener información sobre cómo administrar fuentes de NuGet, vea la documentación de dotnet restore .
Después de asegurarse de que se restauran los paquetes, puede ir hasta el directorio donde reside una biblioteca:
cd src/SuperAwesomeLibrary
dotnet pack
$ ls bin/Debug
netstandard1.0/
SuperAwesomeLibrary.1.0.0.nupkg
SuperAwesomeLibrary.1.0.0.symbols.nupkg
Esto genera un paquete que se puede depurar. Si quiere compilar un paquete NuGet con archivos binarios de
versión comercial, todo lo que tiene que hacer es agregar el modificador --configuration (o -c ) y usar release
como argumento.
dotnet pack --configuration release
La carpeta /bin tendrá ahora una carpeta versión que contiene el paquete NuGet con archivos binarios de versión:
$ ls bin/release
netstandard1.0/
SuperAwesomeLibrary.1.0.0.nupkg
SuperAwesomeLibrary.1.0.0.symbols.nupkg
Vea también
Inicio rápido: Creación y publicación de un paquete
Puesta al día del runtime de implementación
autocontenida
18/03/2020 • 5 minutes to read • Edit Online
Las implementaciones de aplicaciones autocontenidas de .NET Core incluyen las bibliotecas de .NET Core y el
runtime de .NET Core. A partir del SDK de .NET Core 2.1 (versión 2.1.300), una implementación de aplicación
autocontenida publica el runtime con la revisión superior en el equipo. De forma predeterminada, el comando
dotnet publish para una implementación autocontenida selecciona la versión más reciente instalada como parte
del SDK en el equipo de publicación. Esto permite que la aplicación implementada se ejecute con las revisiones de
seguridad (y otras correcciones) disponibles en el momento de usar el comando publish . La aplicación debe
volver a publicarse para obtener una nueva revisión. Para crear una aplicación autocontenida, se especifica
-r <RID> en el comando dotnet publish o se especifica el identificador de runtime (RID) en el archivo de
proyecto (csproj o vbproj) o en la línea de comandos.
NOTE
restore y build pueden ejecutarse implícitamente como parte de otro comando, como publish . Cuando se ejecutan
implícitamente como parte de otro comando, se les proporciona contexto adicional para que se produzcan los artefactos
correctos. Cuando se usa el comando publish con un runtime (por ejemplo, dotnet publish -r linux-x64 ), la parte
implícita restore restaura los paquetes para el runtime de linux-x64. Si se llama a restore explícitamente, no se
restauran los paquetes de runtime de forma predeterminada, dado que carece del contexto.
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
Por ejemplo, para colocar algunos archivos en el directorio de publicación pero no empaquetarlos en el archivo
único:
<ItemGroup>
<Content Update="Plugin.dll">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
</ItemGroup>
Publicación de una aplicación de archivo único: CLI
Publique una aplicación de archivo único mediante el comando dotnet publish. Al publicar la aplicación, establezca
las propiedades siguientes:
Publicación para un runtime concreto: -r win-x64
Publicación como archivo único: -p:PublishSingleFile=true
En el ejemplo siguiente se publica una aplicación para Windows como aplicación independiente de archivo único.
En el ejemplo siguiente se publica una aplicación para Linux como una aplicación de archivo único dependiente del
marco.
Para obtener más información, vea Publicación de aplicaciones .NET Core con la CLI de .NET Core.
Si todavía no tiene un perfil de publicación, siga las instrucciones para crear uno y elija el tipo de destino
Carpeta .
2. Elija Editar .
3. En el cuadro de diálogo Configuración de perfil , establezca las opciones siguientes:
Establezca Modo de implementación en Independiente .
Establezca Tiempo de ejecución de destino en la plataforma en la que quiera publicar.
Seleccione Producir un único archivo .
Elija Guardar para guardar la configuración y volver al cuadro de diálogo Publicar .
Vea también
Implementación de aplicaciones .NET Core.
Publicación de aplicaciones .NET Core con la CLI de .NET Core.
Publicación de aplicaciones .NET Core con Visual Studio.
Comando dotnet publish .
Recorte de implementaciones autocontenidas y
ejecutables
16/09/2020 • 8 minutes to read • Edit Online
El modelo de implementación dependiente del marco ha sido el modelo de implementación más eficaz desde el
inicio de .NET. En este escenario, el desarrollador de la aplicación agrupa solo la aplicación y los ensamblados de
terceros con la expectativa de que las bibliotecas del entorno de ejecución y el marco de .NET estén disponibles
en el equipo cliente. Este modelo de implementación sigue siendo el dominante en .NET Core, pero hay algunos
escenarios en los que el modelo dependiente del marco no es óptimo. La alternativa es publicar una aplicación
independiente, donde el entorno de ejecución y el marco de .NET Core están agrupados con la aplicación y
ensamblados de terceros.
El modelo de implementación trim independiente es una versión especializada del modelo de implementación
independiente que está optimizado para reducir el tamaño de la implementación. Minimizar el tamaño de la
implementación es un requisito fundamental para algunos escenarios del lado cliente, como las aplicaciones
Blazor. En función de la complejidad de la aplicación, solo se hace referencia a un subconjunto de ensamblados
de marco, y se requiere un subconjunto del código en cada ensamblado para ejecutar la aplicación. Las partes sin
usar de las bibliotecas no son necesarias y se pueden recortar de la aplicación empaquetada.
No obstante, existe el riesgo de que el análisis del tiempo de compilación de la aplicación pueda causar errores
en tiempo de ejecución, debido a que no puede analizar de forma confiable diversos patrones de código
problemáticos (centrados en gran medida en el uso de la reflexión). Dado que no se puede garantizar la
confiabilidad, este modelo de implementación se ofrece como una característica en versión preliminar.
El motor de análisis en tiempo de compilación proporciona advertencias al desarrollador de los patrones de
código problemáticos para detectar qué otro código es necesario. El código se puede anotar con atributos para
indicar al recortador qué más se debe incluir. Muchos patrones de reflexión se pueden reemplazar por la
generación de código en tiempo de compilación mediante generadores de código fuente.
El modo de recorte de las aplicaciones se configura con TrimMode . El valor predeterminado es copyused y
agrupa los ensamblados a los que se hace referencia con la aplicación. El valor link se usa con las aplicaciones
WebAssembly de Blazor y recorta el código no usado dentro de los ensamblados. Las advertencias de análisis de
recorte proporcionan información sobre patrones de código en los que no es posible realizar análisis de
dependencias completas. Estas advertencias se suprimen de forma predeterminada y se pueden activar
estableciendo la marca SuppressTrimAnalysisWarnings en false . Para obtener más información acerca de las
opciones de recorte disponibles, vea Opciones de recorte.
NOTE
El recorte es una característica experimental en .NET Core 3.1, 5.0 y solo está disponible para las aplicaciones que se
publican independientes.
<ItemGroup>
<TrimmerRootAssembly Include="System.Security" />
</ItemGroup>
<PropertyGroup>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
En este ejemplo se publica una aplicación en el modo de recorte agresivo en el que el código no usado dentro de
los ensamblados se desactivará y se habilitarán las advertencias de recorte.
<PropertyGroup>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
</PropertyGroup>
Para obtener más información, vea Publicación de aplicaciones .NET Core con la CLI de .NET Core.
Vea también
Implementación de aplicaciones .NET Core.
Publicación de aplicaciones .NET Core con la CLI de .NET Core.
Publicación de aplicaciones .NET Core con Visual Studio.
Comando dotnet publish.
Almacenamiento de paquetes en tiempo de
ejecución
16/09/2020 • 11 minutes to read • Edit Online
A partir de .NET Core 2.0, es posible empaquetar e implementar aplicaciones en un conjunto conocido de
paquetes que existen en el entorno de destino. Las ventajas son implementaciones más rápidas, menor uso de
espacio en disco y un rendimiento de inicio mejorado en algunos casos.
Esta característica se implementa como un almacenamiento de paquetes en tiempo de ejecución, que es un
directorio en disco donde se almacenan los paquetes (normalmente en /usr/local/share/dotnet/store en
macOS/Linux y C:/Program Files/dotnet/store en Windows). En este directorio, existen subdirectorios para las
arquitecturas y las plataformas de destino. El diseño del archivo es similar a la manera en que los activos de
NuGet se colocan en el disco:
\dotnet
\store
\x64
\netcoreapp2.0
\microsoft.applicationinsights
\microsoft.aspnetcore
...
\x86
\netcoreapp2.0
\microsoft.applicationinsights
\microsoft.aspnetcore
...
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="NUGET_PACKAGE" Version="VERSION" />
<!-- Include additional packages here -->
</ItemGroup>
</Project>
Ejemplo
El siguiente manifiesto de almacenamiento de paquetes de ejemplo (packages.csproj) se usa para agregar
Newtonsoft.Json y Moq a un almacenamiento de paquetes en tiempo de ejecución:
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="Moq" Version="4.7.63" />
</ItemGroup>
</Project>
Aprovisione el almacenamiento de paquetes en tiempo de ejecución ejecutando dotnet store con el manifiesto
de almacenamiento de paquetes, el runtime y el marco:
Ejemplo
Puede pasar varias rutas de manifiesto de almacenamiento de paquetes de destino a un único comando
dotnet store repitiendo la opción y la ruta en el comando.
<StoreArtifacts>
<Package Id="Newtonsoft.Json" Version="10.0.3" />
<Package Id="Castle.Core" Version="4.1.0" />
<Package Id="Moq" Version="4.7.63" />
</StoreArtifacts>
Ejemplo
Implemente la aplicación publicada resultante en un entorno que tenga los paquetes descritos en el manifiesto
de destino. Realizar esto incorrectamente produce errores en el inicio de la aplicación.
Especifique varios manifiestos de destino al publicar una aplicación repitiendo la opción y la ruta (por ejemplo,
--manifest manifest1.xml --manifest manifest2.xml ). Cuando realice esto, la aplicación se reduce para la unión
de los paquetes especificados en los archivos de manifiesto de destino que se proporcionan para el comando.
Si implementa una aplicación con una dependencia de manifiesto que está presente en la implementación (el
ensamblado está presente en la carpeta bin), el almacenamiento de paquetes en tiempo de ejecución no se usa
en el host de ese ensamblado. El ensamblado de la carpeta bin se usa independientemente de su presencia en el
almacenamiento de paquetes en tiempo de ejecución en el host.
La versión de la dependencia indicada en el manifiesto debe coincidir con la versión de la dependencia en el
almacenamiento de paquetes en tiempo de ejecución. Si no coinciden las versiones entre la dependencia del
manifiesto de destino y la versión que existe en el almacenamiento de paquetes en tiempo de ejecución, y la
aplicación no incluye la versión necesaria del paquete en su implementación, se produce un error en el inicio de
la aplicación. La excepción incluye el nombre del manifiesto de destino que se ha llamado para el ensamblado
del almacenamiento de paquetes en tiempo de ejecución, que le ayuda a solucionar los errores de coincidencia.
Cuando la implementación se reduce en su publicación, solo las versiones específicas de los paquetes de
manifiesto que indique se retienen del resultado publicado. Los paquetes de las versiones indicadas deben estar
presentes en el host para que se inicie la aplicación.
<PropertyGroup>
<TargetManifestFiles>manifest1.xml;manifest2.xml</TargetManifestFiles>
</PropertyGroup>
Especifique los manifiestos de destino en el archivo de proyecto solo cuando el entorno de destino de la
aplicación sea muy conocido, como para los proyectos de .NET Core. Este no es el caso de los proyectos de
código abierto. Los usuarios de un proyecto de código abierto normalmente lo implementan en diferentes
entornos de producción. Estos entornos de producción generalmente tienen diferentes conjuntos de paquetes
preinstalados. No puede realizar presuposiciones sobre el manifiesto de destino en dichos entornos, por lo que
debe usar la opción --manifest de dotnet publish .
<PropertyGroup>
<PublishWithAspNetCoreTargetManifest>false</PublishWithAspNetCoreTargetManifest>
</PropertyGroup>
NOTE
En el caso de las aplicaciones de implementación independiente, se presupone que el sistema de destino no contiene
necesariamente los paquetes de manifiesto necesarios. Por lo tanto, no se puede establecer
<PublishWithAspNetCoreTargetManifest> en true para una aplicación independiente.
Vea también
dotnet-publish
dotnet-store
Catálogo de identificadores de entorno de
ejecución (RID) de .NET Core
16/09/2020 • 9 minutes to read • Edit Online
RID es la abreviatura de identificador de entorno de ejecución. Los valores de RID se usan para
identificar las plataformas de destino donde se ejecuta la aplicación. Los paquetes de .NET los usan
para presentar los recursos específicos de la plataforma en los paquetes de NuGet. Los valores
siguientes son ejemplos de RID: linux-x64 , ubuntu.14.04-x64 , win7-x64 o osx.10.12-x64 . En el caso
de los paquetes con dependencias nativas, el RID designa las plataformas en las que se puede
restauran el paquete.
Un único RID se puede establecer en el elemento <RuntimeIdentifier> del archivo del proyecto. Se
pueden definir varios RID como una lista delimitada por punto y coma en el elemento
<RuntimeIdentifiers> del archivo del proyecto. También se usan a través de la opción --runtime con
los comandos de la CLI de .NET Core siguientes:
dotnet build
dotnet clean
dotnet pack
dotnet publish
dotnet restore
dotnet run
dotnet store
Los RID que representan sistemas operativos concretos normalmente siguen este patrón:
[os].[version]-[architecture]-[additional qualifiers] , donde:
[additional qualifiers] diferencia aún más las distintas plataformas. Por ejemplo: aot .
Grafo de RID
El grado de RID o el grafo de reserva de entorno de ejecución es una lista de RID compatibles entre sí.
Los RID se definen en el paquete Microsoft.NETCore.Platforms. Puede ver la lista de RID compatibles y
el grafo de RID en el archivo runtime.json, que se encuentra en el repositorio dotnet/runtime . En este
archivo puede ver que todos los RID, excepto el RID de base, contienen una instrucción "#import" .
Estas instrucciones indican los RID compatibles.
Cuando NuGet restaura los paquetes, intenta encontrar una coincidencia exacta para el entorno de
ejecución especificado. Si no se encuentra una coincidencia exacta, NuGet vuelve al grafo hasta
encontrar el sistema compatible más cercano según el grafo de RID.
El ejemplo siguiente es la entrada real del RID osx.10.12-x64 :
"osx.10.12-x64": {
"#import": [ "osx.10.12", "osx.10.11-x64" ]
}
El RID anterior especifica que osx.10.12-x64 importa osx.10.11-x64 . Por tanto, cuando NuGet
restaura los paquetes, intenta encontrar una coincidencia exacta para osx.10.12-x64 en el paquete. Si
NuGet no puede encontrar el entorno de ejecución específico, puede restaurar, por ejemplo, los
paquetes que especifican entornos de ejecución osx.10.11-x64 .
En el ejemplo siguiente se muestra un grafo de RID ligeramente más grande que también se define en
el archivo runtime.json:
win7-x64 win7-x86
| \ / |
| win7 |
| | |
win-x64 | win-x86
\ | /
win
|
any
RID de Windows
Solo se muestran los valores comunes. Para obtener la versión más reciente y completa, vea el archivo
runtime.json en el repositorio dotnet/runtime .
Portátil (.NET Core 2.0 o versiones posteriores)
win-x64
win-x86
win-arm
win-arm64
Windows 7/Windows Server 2008 R2
win7-x64
win7-x86
Windows 8.1/Windows Server 2012 R2
win81-x64
win81-x86
win81-arm
Windows 10/Windows Server 2016
win10-x64
win10-x86
win10-arm
win10-arm64
RID de Linux
Solo se muestran los valores comunes. Para obtener la versión más reciente y completa, vea el archivo
runtime.json en el repositorio dotnet/runtime . Los dispositivos que ejecutan una distribución que no
se muestran en la lista podrían funcionar con uno de los RID portátiles. Por ejemplo, el destino de los
dispositivos Raspberry Pi que ejecutan una distribución de Linux se puede establecer con linux-arm .
Portátil (.NET Core 2.0 o versiones posteriores)
linux-x64 (La mayoría de las distribuciones de escritorio como CentOS, Debian, Fedora,
Ubuntu y derivados)
linux-musl-x64 (Distribuciones ligeras con musl como Alpine Linux)
linux-arm (Distribuciones de Linux que se ejecutan en ARM, como Raspbian en Raspberry
Pi, modelo 2+)
linux-arm64 (Distribuciones de Linux que se ejecutan en ARM de 64 bits, como Ubuntu
Server de 64 bits en Raspberry Pi, modelo 3+)
Red Hat Enterprise Linux
rhel-x64 (Se reemplaza por linux-x64 para RHEL por encima de la versión 6)
rhel.6-x64 (.NET Core 2.0 o versiones posteriores)
Tizen (.NET Core 2.0 o versiones posteriores)
tizen
tizen.4.0.0
tizen.5.0.0
RID de macOS
Los RID de macOS usan la personalización de marca antigua "OSX". Solo se muestran los valores
comunes. Para obtener la versión más reciente y completa, vea el archivo runtime.json en el
repositorio dotnet/runtime .
Portátil (.NET Core 2.0 o versiones posteriores)
(La versión mínima del sistema operativo es macOS 10.12 Sierra)
osx-x64
macOS 10.10 Yosemite
osx.10.10-x64
macOS 10.11 El Capitan
osx.10.11-x64
macOS 10.12 Sierra (.NET Core 1.1 o versiones posteriores)
osx.10.12-x64
macOS 10.13 High Sierra (.NET Core 1.1 o versiones posteriores)
osx.10.13-x64
macOS 10.14 Mojave (.NET Core 1.1 o versiones posteriores)
osx.10.14-x64
Vea también
Identificadores de entorno de ejecución
Introducción a .NET y Docker
18/03/2020 • 7 minutes to read • Edit Online
.NET Core puede ejecutarse con facilidad en un contenedor de Docker. Los contenedores proporcionan una
manera ligera de aislar la aplicación del resto del sistema host, con tan solo compartir el kernel y usar los
recursos proporcionados a la aplicación. Si no está familiarizado con Docker, se recomienda encarecidamente que
lea la documentación de introducción a Docker.
Para más información sobre cómo instalar Docker, vea la página de descarga de Docker Desktop: Community
Edition.
Servicios de Azure
Varios servicios de Azure admiten contenedores. Cree una imagen de Docker para la aplicación e impleméntela
en alguno de los siguientes servicios:
Azure Kubernetes Service (AKS)
Escale y organice contenedores de Linux mediante Kubernetes.
Azure App Service
Implemente aplicaciones web o API con contenedores de Linux en un entorno PaaS.
Azure Container Instances
Hospede el contenedor en la nube sin ningún servicio de administración de nivel superior.
Azure Batch
Ejecute trabajos de proceso repetitivos mediante contenedores.
Azure Service Fabric
Migre las aplicaciones de .NET mediante lift-and-shift y modernícelas a microservicios mediante
contenedores de Windows Server.
Azure Container Registry
Almacene y administre imágenes de contenedor en todos los tipos de implementaciones de Azure.
Pasos siguientes
Obtenga información sobre cómo incluir una aplicación de .NET Core en contenedores.
Obtenga información sobre cómo incluir una aplicación ASP.NET Core en contenedores.
Consulte el tutorial de ASP.NET Core para los microservicios.
Obtenga información sobre las herramientas de contenedor en Visual Studio
Tutorial: Incluir una aplicación de .NET Core en un
contenedor
16/09/2020 • 21 minutes to read • Edit Online
En este tutorial obtendrá información sobre cómo incluir una aplicación de .NET Core en un contenedor con
Docker. Los contenedores tienen muchas características y ventajas, como ser una infraestructura inmutable,
proporcionar una arquitectura portátil y permitir la escalabilidad. La imagen puede usarse para crear
contenedores para un entorno de desarrollo local, una nube privada o una nube pública.
En este tutorial ha:
Crear y publicar una aplicación .NET Core sencilla
Crear y configurar un archivo Dockerfile para .NET Core
Creación de una imagen de Docker
Crear y ejecutar un contenedor de Docker
Aprenderá sobre las tareas de compilación e implementación de un contenedor de Docker para una aplicación
.NET Core. La plataforma Docker usa el motor de Docker para compilar y empaquetar rápidamente aplicaciones
como imágenes de Docker. Estas imágenes se escriben en el formato Dockerfile para implementarse y ejecutarse
en un contenedor superpuesto.
NOTE
Este tutorial no es para aplicaciones ASP.NET Core. Si usa ASP.NET Core, lea el tutorial sobre cómo incluir una aplicación de
ASP.NET Core en un contenedor.
Requisitos previos
Instale estos requisitos previos:
SDK de .NET Core 3.1
Si tiene instalado .NET Core, use el comando dotnet --info para determinar el SDK que está usando.
Docker Community Edition
Una carpeta de trabajo temporal para Dockerfile y una aplicación .NET Core de ejemplo. En este tutorial, el
nombre docker-working se usa como la carpeta de trabajo.
Con el comando dotnet new se crea una carpeta denominada App y se genera una aplicación de consola "Hola
mundo". Cambie los directorios y vaya a la carpeta App, desde la sesión de Terminal. Use el comando dotnet run
para iniciar la aplicación. La aplicación se ejecutará e imprimirá Hello World! debajo del comando:
dotnet run
Hello World!
La plantilla predeterminada crea una aplicación que imprime en el terminal y termina de inmediato. En este
tutorial, se usará una aplicación que se repite en bucle de manera indefinida. Abra el archivo Program.cs en un
editor de texto.
TIP
Si está utilizando Visual Studio Code, en la sesión de Terminal anterior, escriba el siguiente comando:
code .
using System;
namespace NetCore.Docker
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Reemplace el archivo por el código siguiente que cuenta números cada segundo:
using System;
using System.Threading.Tasks;
namespace NetCore.Docker
{
class Program
{
static async Task Main(string[] args)
{
var counter = 0;
var max = args.Length != 0 ? Convert.ToInt32(args[0]) : -1;
while (max == -1 || counter < max)
{
Console.WriteLine($"Counter: {++counter}");
await Task.Delay(1000);
}
}
}
}
Guarde el archivo y vuelva a probar el programa con dotnet run . Recuerde que esta aplicación se ejecuta de
manera indefinida. Use el comando Cancelar Ctrl+C para detenerla. A continuación se muestra un resultado de
ejemplo:
dotnet run
Counter: 1
Counter: 2
Counter: 3
Counter: 4
^C
Si pasa un número en la línea de comandos a la aplicación, solo se contará hasta esa cantidad y se cerrará.
Inténtelo con dotnet run -- 5 para contar hasta cinco.
IMPORTANT
Cualquier parámetro posterior a -- no se pasa al comando dotnet run y, en su lugar, se pasa a su aplicación.
Con este comando se compila la aplicación en la carpeta publish. La ruta de acceso a la carpeta publish desde la
carpeta de trabajo debería ser .\App\bin\Release\netcoreapp3.1\publish\ .
Windows
Linux
En la carpeta App, obtenga un listado de los directorios de la carpeta publish para comprobar que se ha creado el
archivo NetCore.Docker.dll.
dir .\bin\Release\netcoreapp3.1\publish\
Directory: C:\Users\dapine\App\bin\Release\netcoreapp3.1\publish
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
NOTE
Aquí se usa intencionadamente la imagen de runtime de ASP.NET Core, aunque se podría haber usado la imagen de
mcr.microsoft.com/dotnet/core/runtime:3.1 .
La palabra clave FROM requiere un nombre completo de imagen de contenedor de Docker. Microsoft Container
Registry (MCR, mcr.microsoft.com) es un sindicato de Docker Hub, que hospeda contenedores accesibles
públicamente. El segmento dotnet/core es el repositorio de contenedores, donde el segmento de aspnet es el
nombre de la imagen de contenedor. La imagen está etiquetada con 3.1 , que se usa para el control de versiones.
Por lo tanto, mcr.microsoft.com/dotnet/core/aspnet:3.1 es el runtime de .NET Core 3.1. Asegúrese de extraer el
runtime que coincida con el que el SDK tiene como destino. Por ejemplo, la aplicación creada en la sección
anterior usaba el SDK de .NET Core 3.1, y la imagen base a la que se hace referencia en el documento Dockerfile
se etiqueta con 3.1 .
Guarde el archivo Dockerfile. La estructura de directorios de la carpeta de trabajo debería tener el siguiente
aspecto. Algunos de los archivos y carpetas inferiores se han omitido para ahorrar espacio en este artículo:
docker-working
└──App
├──Dockerfile
├──NetCore.Docker.csproj
├──Program.cs
├──bin
│ └──Release
│ └──netcoreapp3.1
│ └──publish
│ ├──NetCore.Docker.deps.json
│ ├──NetCore.Docker.exe
│ ├──NetCore.Docker.dll
│ ├──NetCore.Docker.pdb
│ └──NetCore.Docker.runtimeconfig.json
└──obj
└──...
Docker procesará cada línea en el archivo Dockerfile. . del comando docker build indica a Docker que use la
carpeta actual para encontrar un archivo Dockerfile. Este comando crea la imagen y un repositorio local
denominado counter-image que apunta a esa imagen. Una vez que finalice este comando, ejecute
docker images para ver una lista de las imágenes instaladas:
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
counter-image latest e6780479db63 4 days ago 190MB
mcr.microsoft.com/dotnet/core/aspnet 3.1 e6780479db63 4 days ago 190MB
Observe que ambas imágenes comparten el mismo valor IMAGE ID . El valor es el mismo entre ambas imágenes
porque el único comando en Dockerfile era basar la imagen nueva en una imagen existente. Agreguemos tres
comandos a Dockerfile. Cada comando crea una capa de imagen y el comando final que representa la imagen
counter-image a la que apunta el repositorio.
El comando COPY indica a Docker que copie la carpeta especificada en el equipo a una carpeta del contenedor. En
este ejemplo, la carpeta publish se copia en una carpeta denominada App del contenedor.
El comando WORKDIR cambia el directorio actual dentro del contenedor a App.
El comando siguiente, ENTRYPOINT , indica a Docker que configure el contenedor para que se ejecute como
ejecutable. Cuando el contenedor se inicia, se ejecuta el comando ENTRYPOINT . Cuando este comando finaliza, el
contenedor se detiene automáticamente.
Desde el terminal, ejecute docker build -t counter-image -f Dockerfile . y, cuando el comando finalice, ejecute
docker images .
docker build -t counter-image -f Dockerfile .
Sending build context to Docker daemon 1.117MB
Step 1/4 : FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
---> e6780479db63
Step 2/4 : COPY bin/Release/netcoreapp3.1/publish/ App/
---> d1732740eed2
Step 3/4 : WORKDIR /App
---> Running in b1701a42f3ff
Removing intermediate container b1701a42f3ff
---> 919aab5b95e3
Step 4/4 : ENTRYPOINT ["dotnet", "NetCore.Docker.dll"]
---> Running in c12aebd26ced
Removing intermediate container c12aebd26ced
---> cd11c3df9b19
Successfully built cd11c3df9b19
Successfully tagged counter-image:latest
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
counter-image latest cd11c3df9b19 41 seconds ago 190MB
mcr.microsoft.com/dotnet/core/aspnet 3.1 e6780479db63 4 days ago 190MB
Cada comando de Dockerfile generó una capa y creó un valor IMAGE ID . El valor IMAGE ID final (el suyo será
distinto) es cd11c3df9b19 y, después, usted creará un contenedor basado en esta imagen.
Crear un contenedor
Ahora que tiene una imagen que contiene la aplicación, puede crear un contenedor. Hay dos formas de crear un
contenedor. En primer lugar, cree un contenedor que esté detenido.
El comando docker create anterior creará un contenedor basado en la imagen counter-image . La salida de ese
comando muestra el valor CONTAINER ID (el suyo será distinto) del contenedor creado. Para ver una lista de
todos los contenedores, use el comando docker ps -a :
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0f281cb3af99 counter-image "dotnet NetCore.Dock…" 40 seconds ago Created core-counter
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2f6424a7ddce counter-image "dotnet NetCore.Dock…" 2 minutes ago Up 11 seconds core-
counter
De manera similar, el comando docker stop detendrá el contenedor. En el ejemplo siguiente se usa el comando
docker stop para detener el contenedor y luego se usa el comando docker ps para mostrar que no hay ningún
contenedor en ejecución:
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Conectarse a un contenedor
Una vez que se ejecuta un contenedor, puede conectarse a él para ver la salida. Use los comandos docker start y
docker attach para iniciar el contenedor y echar un vistazo al flujo de salida. En este ejemplo, la pulsación de
teclas Ctrl+C se usa para desasociarse del contenedor en ejecución. A menos que se especifique lo contrario,
esta pulsación de teclas finalizará el proceso en el contenedor, con lo que se detendrá el contenedor. El parámetro
--sig-proxy=false garantiza que Ctrl+C no detendrá el proceso en el contenedor.
Después de desasociarse del contenedor, reasócielo para comprobar que sigue en ejecución.
Eliminación de un contenedor
Para el propósito de este artículo, no queremos contenedores que solo existan y no hagan nada. Elimine el
contenedor que creó anteriormente. Si el contenedor está en ejecución, deténgalo.
En el ejemplo siguiente se muestran todos los contenedores. Luego, usa el comando docker rm para eliminar el
contenedor y después vuelve a comprobar si hay algún contenedor en ejecución.
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS
PORTS NAMES
2f6424a7ddce counter-image "dotnet NetCore.Dock…" 7 minutes ago Exited (143) 20 seconds ago
core-counter
docker rm core-counter
core-counter
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Ejecución única
Docker proporciona el comando docker run para crear y ejecutar el contenedor como comando único. Este
comando elimina la necesidad de ejecutar docker create y luego docker start . También puede establecer este
comando en que elimine automáticamente el contenedor cuando este se detenga. Por ejemplo, use
docker run -it --rm para hacer dos cosas: primero, use automáticamente el terminal actual para conectarse al
contenedor y cuando el contenedor finalice, quítelo:
El contenedor también pasa parámetros a la ejecución de la aplicación .NET Core. Para indicar a la aplicación .NET
Core que cuente solo hasta 3, pase 3.
Con docker run -it , el comando Ctrl+C detendrá el proceso que está ejecutándose en el contenedor, lo que, a
su vez, detendrá el contenedor. Como se proporcionó el parámetro --rm , el contenedor se elimina
automáticamente cuando se detiene el proceso. Compruebe que no existe:
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Cambio de ENTRYPOINT
El comando docker run también permite modificar el comando ENTRYPOINT desde el archivo Dockerfile y ejecute
algún otro elemento, pero solo para ese contenedor. Por ejemplo, use el comando siguiente para ejecutar bash o
cmd.exe . Edite el comando según sea necesario.
Windows
Linux
En este ejemplo, ENTRYPOINT cambia a cmd.exe . Se presiona Ctrl+C para finalizar el proceso y detener el
contenedor.
docker run -it --rm --entrypoint "cmd.exe" counter-image
C:\>dir
Volume in drive C has no label.
Volume Serial Number is 3005-1E84
Directory of C:\
C:\>^C
Comandos esenciales
Docker tiene muchos comandos diferentes que crean, administran e interactúan con contenedores e imágenes.
Estos comandos de Docker son esenciales para la administración de los contenedores:
docker build
docker run
docker ps
docker stop
docker rm
docker rmi
docker image
docker ps -a
3. Eliminar el contenedor
docker rm counter-image
A continuación, elimine las imágenes que ya no quiere tener en la máquina. Elimine la imagen que creó el archivo
Dockerfile y luego elimine la imagen de .NET Core en que se basó el archivo Dockerfile. Puede usar el valor
IMAGE ID o la cadena con formato REPOSITORY:TAG .
docker rmi counter-image:latest
docker rmi mcr.microsoft.com/dotnet/core/aspnet:3.1
Use el comando docker images para ver una lista de las imágenes instaladas.
TIP
Los archivos de imagen pueden ser grandes. Por lo general, quitaría los contenedores temporales que creó al probar y
desarrollar la aplicación. Habitualmente, estas imágenes base se conservan con el runtime instalado si se planea crear otras
imágenes basadas en ese runtime.
Pasos siguientes
Obtenga información sobre cómo incluir una aplicación ASP.NET Core en contenedores.
Consulte el tutorial de ASP.NET Core para los microservicios.
Revise los servicios de Azure que admiten contenedores.
Lea sobre los comandos de Dockerfile.
Explorar las herramientas de contenedor para Visual Studio
Common Type System y Common Language
Specification
19/03/2020 • 6 minutes to read • Edit Online
De nuevo, dos términos que se usan libremente en el mundo de .NET, en realidad, son cruciales para entender
cómo permite una implementación de .NET el desarrollo de varios lenguajes y para entender cómo funciona.
Más recursos
Sistema de tipos comunes
Common Language Specification
Sistema de tipos comunes
16/09/2020 • 46 minutes to read • Edit Online
Common Type System define cómo se declaran, usan y administran los tipos en Common Language Runtime. Es
también una parte importante de la compatibilidad en tiempo de ejecución con la integración entre lenguajes. El
sistema de tipos común realiza las funciones siguientes:
Establece un marco de trabajo que ayuda a permitir la integración entre lenguajes, la seguridad de tipos y
la ejecución de código de alto rendimiento.
Proporciona un modelo orientado a objetos que admite la implementación completa de muchos lenguajes
de programación.
Define reglas que deben seguir los lenguajes, lo que ayuda a garantizar que los objetos escritos en
distintos lenguajes puedan interactuar unos con otros.
Proporciona una biblioteca que contiene los tipos de datos primitivos (como Boolean, Byte, Char, Int32 y
UInt64) que se emplean en el desarrollo de aplicaciones.
Tipos de .NET
Todos los tipos de .NET son tipos de valor o tipos de referencia.
Los tipos de valor son tipos de datos cuyos objetos se representan mediante el valor real del objeto. Si se asigna
una instancia de un tipo de valor a una variable, esa variable obtiene una copia reciente del valor.
Los tipos de referencia son tipos de datos cuyos objetos se representan mediante una referencia (similar a un
puntero) al valor real del objeto. Si se asigna un tipo de referencia a una variable, esa variable hace referencia (o
apunta) al valor original. No se realiza ninguna copia.
Common Type System en .NET admite las cinco categorías de tipos siguientes:
Clases
Estructuras
Enumeraciones
Interfaces
Delegados
Clases
Una clase es un tipo de referencia que se puede derivar directamente de otra clase y que se deriva
implícitamente de System.Object. La clase define las operaciones que un objeto (que es una instancia de la clase)
puede realizar (métodos, eventos o propiedades) y los datos que el objeto contiene (campos). Aunque una clase
suele incluir una definición y una implementación (a diferencia, por ejemplo, de las interfaces, que solo contienen
una definición sin implementación), puede tener uno o varios miembros sin implementación.
En la tabla siguiente se describen algunas de las características que una clase puede tener. Cada lenguaje
compatible con el motor en tiempo de ejecución proporciona una forma de indicar que una clase o un miembro
de clase tiene una o varias de estas características. En cambio, puede que no estén disponibles todas estas
características en los lenguajes de programación orientados a .NET.
C A RA C T ERÍST IC A DESC RIP C IÓ N
exported o not exported Indica si una clase está visible fuera del ensamblado en el que
se define. Esta característica se aplica únicamente a las clases
de nivel superior y no a las clases anidadas.
NOTE
Una clase también puede estar anidada en una estructura o clase primaria. Las clases anidadas tienen también
características de miembro. Para obtener más información, consulte Tipos anidados.
Los miembros de clase que no tienen implementación son miembros abstractos. Una clase que tiene uno o
varios miembros abstractos es abstracta y no se pueden crear nuevas instancias de ella. Algunos lenguajes
destinados al motor en tiempo de ejecución permiten marcar una clase como abstracta incluso aunque no tenga
ningún miembro abstracto. Se puede usar una clase abstracta cuando se desea encapsular un conjunto básico de
funcionalidad que las clases derivadas pueden heredar o invalidar según corresponda. Las clases que no son
abstractas se conocen como clases concretas.
Una clase puede implementar cualquier número de interfaces pero puede heredar solo de una clase base
además de la clase System.Object, de la que todas las clases heredan implícitamente. Todas las clases deben tener
al menos un constructor, que inicializa nuevas instancias de la clase. Si no se define explícitamente un constructor,
la mayoría de los compiladores proporcionarán automáticamente un constructor sin parámetros.
Estructuras
Una estructura es un tipo de valor que se deriva implícitamente de System.ValueType, que a su vez se deriva de
System.Object. Una estructura es útil para representar valores cuyos requisitos de memoria sean reducidos y
para pasar valores como parámetros por valor a los métodos que tengan parámetros fuertemente tipados. En
.NET, todos los tipos de datos primitivos (Boolean, Byte, Char, DateTime, Decimal, Double, Int16, Int32, Int64,
SByte, Single, UInt16, UInt32 y UInt64) se definen como estructuras.
Al igual que las clases, las estructuras definen datos (los campos de la estructura) y las operaciones que se
pueden realizar con esos datos (los métodos de la estructura). Esto significa que se puede llamar a los métodos
en las estructuras, incluso a los métodos virtuales definidos en las clases System.Object y System.ValueType, y a
cualquier método definido en el propio tipo de valor. Es decir, las estructuras pueden tener campos, propiedades
y eventos, así como métodos estáticos y no estáticos. Se pueden crear instancias de las estructuras, pasarlas
como parámetros, almacenarlas como variables locales o almacenarlas en un campo de otro tipo de valor o tipo
de referencia. Las estructuras también pueden implementar interfaces.
Los tipos de valor también difieren de las clases en varios aspectos. En primer lugar, aunque heredan
implícitamente de System.ValueType, no pueden heredar directamente de ningún tipo. De manera similar, todos
los tipos de valor están sellados, lo que quiere decir que de ellos no se puede derivar ningún otro tipo. Tampoco
necesitan constructores.
Para cada tipo de valor, Common Language Runtime proporciona un tipo correspondiente al que se ha aplicado
la conversión boxing, que es una clase que tiene el mismo estado y comportamiento que el tipo de valor. A una
instancia de un tipo de valor se le aplica la conversión boxing cuando se pasa a un método que acepta un
parámetro de tipo System.Object. Se le aplica la conversión unboxing (es decir, se vuelve a convertir la instancia
de una clase en una instancia de un tipo de valor) cuando se devuelve el control de una llamada a un método que
acepta un tipo de valor como parámetro por referencia. En el caso de algunos lenguajes, se debe usar una
sintaxis especial cuando se necesita el tipo al que se haya aplicado la conversión boxing, mientras que otros
emplean el tipo automáticamente cuando es necesario. Cuando se define un tipo de valor, se definen los dos
tipos: al que se ha aplicado la conversión boxing y al que se ha aplicado la conversión unboxing.
Enumeraciones
Una enumeración es un tipo de valor que hereda directamente de System.Enum y proporciona nombres
alternativos para los valores de un tipo primitivo subyacente. Un tipo de enumeración tiene un nombre, un tipo
subyacente que debe ser uno de los tipos de enteros con o sin signo integrados (como Byte, Int32 o UInt64) y un
conjunto de campos. Los campos son campos literales estáticos, cada uno de los cuales representa una
constante. El mismo valor se puede asignar a varios campos. Cuando esto sucede, se debe marcar uno de los
valores como valor de enumeración primario para la reflexión y la conversión de cadenas.
Se puede asignar un valor del tipo subyacente a una enumeración y viceversa, y no es necesario que el motor en
tiempo de ejecución realice una conversión. Se puede crear una instancia de una enumeración y llamar a los
métodos de System.Enum, además de llamar a cualquier método definido en el tipo subyacente de la
enumeración. Sin embargo, algunos lenguajes no permiten pasar una enumeración como parámetro cuando se
necesita una instancia del tipo subyacente (o viceversa).
A las enumeraciones se les aplican las restricciones siguientes:
No pueden definir sus propios métodos.
No pueden implementar interfaces.
No pueden definir propiedades ni eventos.
No pueden ser genéricas, a menos que sean genéricas solo porque están anidadas dentro de un tipo
genérico. Es decir, una enumeración no puede tener parámetros de tipo propios.
NOTE
Los tipos anidados (incluidas las enumeraciones) creados con Visual Basic, C# y C++ incluyen los parámetros de
tipo de todos los tipos genéricos envolventes, por lo que son genéricos aunque no tengan parámetros de tipo
propios. Para obtener más información, vea la sección "Tipos anidados" en el tema de referencia del método
Type.MakeGenericType.
El atributo FlagsAttribute indica una clase especial de enumeración denominada campo de bits. El motor en
tiempo de ejecución no distingue entre enumeraciones tradicionales y campos de bits, pero el lenguaje podría
hacerlo. Cuando se hace esta distinción, se pueden utilizar operadores bit a bit en estos campos de bits, para
generar valores sin nombre, pero no en las enumeraciones. Normalmente, las enumeraciones se utilizan para
listas de elementos únicos, como los días de la semana, los nombres de países o regiones, etc. Los campos de
bits se utilizan, en general, para listas de calidades o cantidades que pueden producirse en combinaciones, como
Red And Big And Fast .
En el ejemplo siguiente se muestra cómo utilizar los campos de bits y las enumeraciones tradicionales.
using System;
using System.Collections.Generic;
AvailableIn[SomeRootVegetables.HorseRadish] = Seasons.All;
AvailableIn[SomeRootVegetables.Radish] = Seasons.Spring;
AvailableIn[SomeRootVegetables.Turnip] = Seasons.Spring |
Seasons.Autumn;
AvailableIn(SomeRootVegetables.HorseRadish) = Seasons.All
AvailableIn(SomeRootVegetables.Radish) = Seasons.Spring
AvailableIn(SomeRootVegetables.Turnip) = Seasons.Spring Or _
Seasons.Autumn
Interfaces
Una interfaz define un contrato que especifica una relación de lo que se puede hacer o una relación de lo que se
tiene. Las interfaces se utilizan a menudo para implementar una funcionalidad, como comparar y ordenar
(interfaces IComparable e IComparable<T>), comprobar la igualdad (interfaz IEquatable<T>) o enumerar los
elementos de una colección (interfaces IEnumerable e IEnumerable<T>). Las interfaces pueden tener
propiedades, métodos y eventos, que son todos miembros abstractos; es decir, aunque la interfaz define los
miembros y sus firmas, deja que el tipo encargado de implementar la interfaz defina la funcionalidad de cada
miembro de la interfaz. Esto significa que cualquier clase o estructura que implemente una interfaz debe
proporcionar definiciones de los miembros abstractos declarados en la interfaz. Una interfaz puede necesitar que
cualquier clase o estructura que implemente una interfaz implemente también otras interfaces.
A las interfaces se les aplican las restricciones siguientes:
Una interfaz se puede declarar con cualquier tipo de accesibilidad, pero los miembros de la interfaz deben
tener todos accesibilidad pública.
Las interfaces no pueden definir constructores
Las interfaces no pueden definir campos.
Las interfaces solo pueden definir miembros de instancia. No pueden definir miembros estáticos.
Cada lenguaje debe proporcionar reglas para asignar una implementación a la interfaz que necesita el miembro,
ya que varias interfaces pueden declarar un miembro con la misma firma y esos miembros pueden tener
implementaciones independientes.
Delegados
Los delegados son tipos de referencia con una finalidad similar a la de los punteros a función de C++. Se usan
para los controladores de eventos y las funciones de devolución de llamada en .NET. A diferencia de los punteros
a función, los delegados son seguros, se pueden comprobar y proporcionan seguridad de tipos. Un tipo de
delegado puede representar cualquier método de instancia o método estático que tenga una firma compatible.
Un parámetro de un delegado es compatible con el parámetro correspondiente de un método si el tipo del
parámetro del delegado es más restrictivo que el del método, porque así se garantiza que el argumento que se
pase al delegado también se podrá pasar de forma segura al método.
De forma similar, el tipo de valor devuelto de un delegado es compatible con el tipo de valor devuelto de un
método si el del método es más restrictivo que el del delegado, porque así se garantiza que el tipo de valor
devuelto por el método se puede convertir con seguridad al tipo de valor devuelto del delegado.
Por ejemplo, un delegado que tiene un parámetro de tipo IEnumerable y un tipo de valor devuelto Object puede
representar un método que tiene un parámetro de tipo Object y un valor devuelto de tipo IEnumerable. Para
obtener más información y un código de ejemplo, vea Delegate.CreateDelegate(Type, Object, MethodInfo).
Se dice que un delegado está enlazado al método que representa. Además de estar enlazado al método, un
delegado puede estar enlazado a un objeto. El objeto representa el primer parámetro del método y se pasa al
método cada vez que se invoca el delegado. Si el método es un método de instancia, el objeto enlazado se pasa
como el parámetro this implícito ( Me en Visual Basic); si el método es estático, el objeto se pasa como primer
parámetro formal del método y la firma del delegado debe coincidir con los parámetros restantes. Para obtener
más información y un código de ejemplo, vea System.Delegate.
Todos los delegados heredan de System.MulticastDelegate, que hereda de System.Delegate. Los lenguajes C#,
Visual Basic y C++ no permiten que se herede de estos tipos. En su lugar, proporcionan palabras clave para
declarar los delegados.
Dado que los delegados heredan de MulticastDelegate, un delegado tiene una lista de invocación, que es una
lista de métodos que representa el delegado y que se ejecutan cuando se llama al delegado. Todos los métodos
de la lista reciben los argumentos proporcionados cuando se invoca al delegado.
NOTE
El valor devuelto no se define para los delegados que tienen más de un método en su lista de invocación, aunque el
delegado tenga un tipo de valor devuelto.
En muchos casos, como en el de los métodos de devolución de llamada, un delegado solo representa un método
y las únicas acciones que se deben llevar a cabo son la creación y la invocación del delegado.
Por lo que se refiere a los delegados que representan varios métodos, .NET proporciona métodos de las clases de
delegado Delegate y MulticastDelegate para operaciones tales como agregar un método a una lista de
invocación del delegado (el método Delegate.Combine), quitar un método (el método Delegate.Remove) y
obtener la lista de invocación (el método Delegate.GetInvocationList).
NOTE
No es preciso usar estos métodos para los delegados de controladores de eventos en C#, C++ ni Visual Basic, ya que estos
lenguajes proporcionan sintaxis para agregar y quitar controladores de eventos.
Definiciones de tipo
Una definición de tipo incluye lo siguiente:
Los atributos definidos en el tipo.
La accesibilidad del tipo (visibilidad).
El nombre del tipo.
El tipo base del tipo.
Las interfaces que implementa el tipo.
Las definiciones de todos los miembros del tipo
Atributos
Los atributos proporcionan metadatos adicionales definidos por el usuario . Normalmente, se emplean para
almacenar información adicional sobre un tipo en su ensamblado o para modificar el comportamiento de un
miembro de tipo en tiempo de diseño o en tiempo de ejecución.
Los atributos son clases que heredan de System.Attribute. Los lenguajes que admiten el uso de atributos tienen
su propia sintaxis para aplicar atributos a un elemento del lenguaje. Los atributos se pueden aplicar a casi
cualquier elemento del lenguaje; los elementos específicos a los que se puede aplicar un atributo los define la
clase AttributeUsageAttribute aplicada a esa clase de atributos.
Accesibilidad a tipos
Todos los tipos tienen un modificador que rige su accesibilidad desde otros tipos. En la tabla siguiente se
describen las accesibilidades a tipos que admite el motor en tiempo de ejecución.
La accesibilidad de un tipo anidado depende de su dominio de accesibilidad, que viene determinado por la
accesibilidad declarada del miembro y el dominio de accesibilidad del tipo contenedor inmediato. Sin embargo,
el dominio de accesibilidad de un tipo anidado no puede superar al del tipo contenedor.
El dominio de accesibilidad de un miembro anidado M declarado en un tipo T dentro de un programa P se
define de la manera siguiente (teniendo en cuenta que el propio miembro M puede ser un tipo):
Si la accesibilidad declarada de M es public , el dominio de accesibilidad de M es el dominio de
accesibilidad de T .
Si la accesibilidad declarada de M es protected internal , el dominio de accesibilidad de M es la
intersección del dominio de accesibilidad de T con el texto de programa de P y el texto de programa de
cualquier tipo derivado de T declarado fuera de P .
Si la accesibilidad declarada de M es protected , el dominio de accesibilidad de M es la intersección del
dominio de accesibilidad de T con el texto de programa de T y cualquier tipo derivado de T .
Si la accesibilidad declarada de M es internal , el dominio de accesibilidad de M es la intersección del
dominio de accesibilidad de T con el texto de programa de P .
Si la accesibilidad declarada de M es private , el dominio de accesibilidad de M es el texto de programa
de T .
Nombres de tipo
El sistema de tipos común sólo impone dos restricciones en los nombres:
Todos los nombres se codifican como cadenas de caracteres Unicode (de 16 bits).
Los nombres no pueden tener un valor incrustado (de 16 bits) de 0x0000.
Sin embargo, la mayoría de los lenguajes imponen restricciones adicionales sobre los nombres de tipo. Todas las
comparaciones se realizan byte a byte, por lo que distinguen entre mayúsculas y minúsculas y son
independientes de la configuración regional.
Aunque un tipo puede hacer referencia a tipos de otros módulos y ensamblados, es preciso que se defina
íntegramente en un solo módulo de .NET. (Sin embargo, según la compatibilidad del compilador, se puede dividir
en varios archivos de código fuente.) Los nombres de tipo solo tienen que ser únicos dentro de un espacio de
nombres. Para identificar íntegramente un tipo, su nombre debe calificarse con el espacio de nombres que
contiene la implementación del tipo.
Tipos base e interfaces
Un tipo puede heredar valores y comportamientos de otro. El sistema de tipos común no permite que los tipos
hereden de más de un tipo base.
Un tipo puede implementar cualquier número de interfaces. Para implementar una interfaz, un tipo debe
implementar todos los miembros virtuales de la interfaz. Un tipo derivado puede implementar un método
virtual, que se puede invocar estática o dinámicamente.
Miembros de tipos
El motor en tiempo de ejecución permite definir miembros de tipos, que especifican el comportamiento y el
estado de los tipos. Los miembros de tipos incluyen lo siguiente:
Campos
Propiedades
Métodos
Constructores
Eventos
Tipos anidados
Campos
Un campo describe y contiene parte del estado del tipo. Los campos pueden ser de cualquier tipo que admita el
motor en tiempo de ejecución. Normalmente, los campos son de tipo private o protected , por lo que son
accesibles únicamente desde la clase o desde una clase derivada. Si el valor de un campo se puede modificar
desde fuera de su tipo, se suele emplear un descriptor de acceso set de una propiedad. Los campos expuestos
públicamente suelen ser de solo lectura y pueden ser de dos tipos:
Constantes, cuyo valor se asigna en tiempo de diseño. Se trata de miembros estáticos de una clase,
aunque no se definen mediante la palabra clave static ( Shared en Visual Basic).
Variables de solo lectura, cuyos valores se pueden asignar en el constructor de clase.
En el ejemplo siguiente se muestran estos dos usos de los campos de solo lectura.
using System;
Propiedades
Una propiedad identifica un valor o un estado del tipo y define los métodos para obtener o establecer el valor de
la propiedad. Las propiedades pueden ser tipos primitivos, colecciones de tipos primitivos, tipos definidos por el
usuario o colecciones de tipos definidos por el usuario. Las propiedades se usan a menudo para que la interfaz
pública de un tipo se mantenga independiente de la representación real del tipo. De este modo, las propiedades
pueden reflejar valores que no están almacenados directamente en la clase (por ejemplo, cuando una propiedad
devuelve un valor calculado) o realizar la validación antes de que se asignen valores a campos privados. En el
ejemplo siguiente se muestra el último modelo.
using System;
Además de incluir la propiedad propiamente dicha, el Lenguaje Intermedio de Microsoft (MSIL) de un tipo que
contiene una propiedad de lectura incluye un método get_ propertyname y el lenguaje MSIL de un tipo que
contiene una propiedad de escritura incluye un método set_ propertyname.
Métodos
Un método describe las operaciones que están disponibles en el tipo. La firma de un método especifica los tipos
permitidos de todos sus parámetros y de su valor devuelto.
Aunque la mayoría de los métodos definen el número exacto de los parámetros necesarios para las llamadas a
métodos, algunos admiten un número de parámetros que es variable. El último parámetro declarado de estos
métodos se marca con el atributo ParamArrayAttribute. Normalmente, los compiladores de lenguaje
proporcionan una palabra clave, como params en C# y ParamArray en Visual Basic, que hace que sea innecesario
el uso explícito de ParamArrayAttribute.
Constructores
Un constructor es un tipo de método especial que crea nuevas instancias de una clase o una estructura. Al igual
que cualquier otro método, un constructor puede incluir parámetros; sin embargo, los constructores no tienen
ningún valor devuelto (es decir, devuelven void ).
Si el código fuente de una clase no define explícitamente un constructor, el compilador incluye un constructor sin
parámetros. Sin embargo, si el código fuente de una clase define solo constructores parametrizados, los
compiladores de Visual Basic y C# no generan un constructor sin parámetros.
Si el código fuente de una estructura define constructores, estos deben tener parámetros; una estructura no
puede definir un constructor sin parámetros y los compiladores no generan constructores sin parámetros para
las estructuras u otros tipos de valor. Todos los tipos de valor tienen un constructor sin parámetros implícito.
Common Language Runtime implementa este constructor, que inicializa todos los campos de la estructura en sus
valores predeterminados.
Events
Un evento define un incidente al que se puede responder, así como los métodos para suscribirse a un evento,
anular la suscripción y generar el evento. Los eventos se usan con frecuencia para informar a otros tipos de
cambios de estado. Para más información, vea Eventos.
Tipos anidados
Un tipo anidado es un tipo que es un miembro de algún otro tipo. Los tipos anidados deben estar estrechamente
acoplados a su tipo contenedor y no deben ser útiles como tipos de uso general. Los tipos anidados son útiles
cuando el tipo declarativo utiliza y crea instancias del tipo anidado y el uso de dicho tipo anidado no se expone
en miembros públicos.
Los tipos anidados resultan confusos para algunos desarrolladores y no deben estar públicamente visibles a
menos que haya una razón de peso. En una biblioteca bien diseñada, los desarrolladores rara vez deberían tener
que utilizar tipos anidados para crear instancias de objetos o declarar variables.
family
Accesible desde el mismo tipo que el
miembro y desde tipos derivados que
heredan de él.
ensamblado
Accesible sólo en el ensamblado en que
está definido el tipo.
family y assembly
Accesible sólo desde los tipos que
estén calificados para el acceso de
familia y ensamblado.
family o assembly
Accesible sólo desde los tipos que
califican el acceso de familia o
ensamblado.
public
Accesible desde cualquier tipo.
newslot
Oculta los miembros heredados que
tienen la misma firma.
override
Reemplaza la definición de un método
virtual heredado.
Sobrecarga
Cada miembro de tipo tiene una firma única. Las firmas de método están formadas por el nombre del método y
una lista de parámetros (el orden y los tipos de los argumentos del método). Se pueden definir varios métodos
con el mismo nombre dentro un tipo, siempre y cuando sus firmas sean distintas. Cuando se definen dos o más
métodos con el mismo nombre se dice que el método está sobrecargado. Por ejemplo, en System.Char, se
reemplaza el método IsDigit. Un método toma un argumento de tipo Char. El otro método toma un argumento
de tipo String y un argumento de tipo Int32.
NOTE
El tipo de valor devuelto no se considera parte de la firma de un método. Es decir, no se pueden sobrecargar los métodos
si solo difieren en el tipo de valor devuelto.
Vea también
Explorador de API de .NET
Common Language Runtime
Conversión de tipos en .NET
Independencia del lenguaje y componentes
independientes del lenguaje
16/09/2020 • 108 minutes to read • Edit Online
.NET es independiente del lenguaje. Esto significa que, como desarrollador, puede desarrollar en uno de los muchos
lenguajes que tienen como destino las implementaciones de .NET, como C#, F# y Visual Basic. Puede tener acceso a
los tipos y miembros de bibliotecas de clases desarrollados para implementaciones de .NET sin tener que conocer
el lenguaje en el que se escribieron originalmente y sin tener que seguir las convenciones del lenguaje original. Si
es un desarrollador de componentes, puede tener acceso al componente desde cualquier aplicación .NET con
independencia de su lenguaje.
NOTE
En la primera parte de este artículo se describe la creación de componentes independientes del lenguaje; es decir,
componentes que pueden usarse en aplicaciones escritas en cualquier lenguaje. También puede crear una aplicación o
componente únicos de código fuente escrito en varios lenguajes; consulte Interoperabilidad entre lenguajes en la segunda
parte de este artículo.
Para que los objetos puedan tener una interacción total con otros objetos escritos en cualquier lenguaje, estos
objetos solo deben exponer a los llamadores las características que son comunes a todos los lenguajes. Este
conjunto común de características se define mediante Common Language Specification (CLS), que es un conjunto
de reglas que se aplican a los ensamblados generados. Common Language Specification se define en el apartado I,
cláusulas 7 a 11 del estándar ECMA-335: Common Language Infrastructure.
Si el componente se ajusta a Common Language Specification, existe la garantía de que será conforme a CLS y que
será accesible desde el código de un ensamblado escrito en cualquier lenguaje de programación que admita CLS.
Para determinar si el componente se ajusta o no a Common Language Specification en tiempo de compilación,
puede aplicar el atributo CLSCompliantAttribute en el código fuente. Para más información, consulte
CLSCompliantAttribute (Atributo).
NOTE
Common Language Specification describe en cada regla la conformidad con CLS en referencia a los consumidores
(desarrolladores que acceden mediante programación a un componente que es conforme a CLS), los marcos (desarrolladores
que usan un compilador de lenguaje para crear bibliotecas conformes a CLS) y los extensores (desarrolladores que crean una
herramienta, como un compilador de lenguaje o un analizador de código, que crea componentes conformes a CLS). Este
artículo se centra en las reglas que se aplican a los marcos. Pero observe que algunas de las reglas que se aplican a los
extensores también se pueden aplicar a los ensamblados que se crean mediante Reflection.Emit.
Para diseñar un componente que sea independiente del lenguaje, solo tiene que aplicar las reglas de conformidad
con CLS a la interfaz pública del componente. La implementación privada no tiene que ajustarse a la especificación.
IMPORTANT
Las reglas de conformidad con CLS solo se aplican a la interfaz pública de un componente, y no a su implementación privada.
Por ejemplo, los números enteros sin signo distintos de Byte no son conformes a CLS. Dado que la clase Person
del siguiente ejemplo expone una propiedad Age del tipo UInt16, el código siguiente desencadena una advertencia
del compilador.
using System;
[assembly: CLSCompliant(true)]
<Assembly: CLSCompliant(True)>
Para hacer que la clase Person sea conforme a CLS, puede cambiar el tipo de la propiedad Age de UInt16 a Int16,
que es un entero de 16 bits con signo conforme a CLS. No es necesario que cambie el tipo del campo privado
personAge .
using System;
[assembly: CLSCompliant(true)]
[assembly: CLSCompliant(true)]
[CLSCompliant(false)]
public class Counter
{
UInt32 ctr;
public Counter()
{
ctr = 0;
}
<CLSCompliant(False)> _
Public Class Counter
Dim ctr As UInt32
Todos los tipos que aparecen en las signaturas de miembros, incluidos los tipos de propiedades y los tipos de
valores devueltos de un método, deben ser conformes a CLS. Además, en el caso de los tipos genéricos:
Todos los tipos que forman un tipo genérico con instancias deben ser conformes a CLS.
Todos los tipos que se utilizan como restricciones en parámetros genéricos deben ser conformes a CLS.
El sistema de tipos común de .NET incluye varios tipos integrados que se admiten directamente en Common
Language Runtime y que se codifican de forma especial en los metadatos de un ensamblado. De estos tipos
intrínsecos, los tipos enumerados en la tabla siguiente son conformes a CLS.
T IP O C O N F O RM E A C L S DESC RIP C IÓ N
UInt64 Entero de 64 bits sin signo Int64 (se puede desbordar), BigInteger
o Double
La biblioteca de clases de .NET Framework o cualquier otra biblioteca de clases puede incluir otros tipos que no
sean conformes a CLS; por ejemplo:
Tipos de valor a los que se les ha aplicado la conversión boxing. En el siguiente ejemplo de C# se crea una clase
con una propiedad pública de tipo int* denominada Value . Dado que int* es un tipo de valor al que se le ha
aplicado la conversión boxing, el compilador lo marca como no conforme a CLS.
using System;
[assembly:CLSCompliant(true)]
Referencias con establecimiento de tipos, que son construcciones especiales que contienen una referencia a un
objeto y una referencia a un tipo.
Si un tipo no es conforme a CLS, debe aplicar el atributo CLSCompliantAttribute con un parámetro isCompliant con
un valor de false en él. Para obtener más información, consulte la sección CLSCompliantAttribute (Atributo).
En el ejemplo siguiente se muestra el problema de la conformidad con CLS en la creación de instancias de tipos
genéricos y signaturas de métodos. En este ejemplo, se define una clase InvoiceItem con una propiedad de tipo
UInt32, una propiedad de tipo Nullable(Of UInt32) y un constructor con parámetros de tipo UInt32 y
Nullable(Of UInt32) . Cuando intente compilar este ejemplo, aparecerán cuatro advertencias del compilador.
using System;
[assembly: CLSCompliant(true)]
Para eliminar las advertencias del compilador, reemplace los tipos no conformes a CLS de la interfaz pública de
InvoiceItem por tipos conformes:
using System;
[assembly: CLSCompliant(true)]
qty = quantity;
}
Además de los tipos específicos indicados, algunas categorías de tipos no son conformes a CLS. Entre estas
categorías se incluyen tipos de punteros no administrados y tipos de punteros de función. En el ejemplo siguiente
se genera una advertencia del compilador, ya que se utiliza un puntero a un entero para crear una matriz de
enteros.
using System;
[assembly: CLSCompliant(true)]
[assembly: CLSCompliant(true)]
En las clases abstractas conformes a CLS (es decir, clases marcadas como abstract en C#), todos los miembros de
dichas clases deben ser también conformes a CLS.
Convenciones de nomenclatura
Dado que algunos lenguajes de programación distinguen entre mayúsculas y minúsculas, los identificadores (como
los nombres de espacios de nombres, los tipos y los miembros) deben tener otro elemento distintivo aparte del
uso de mayúsculas. Dos identificadores se consideran equivalentes si sus asignaciones de minúsculas son iguales.
En el ejemplo de C# siguiente, se definen dos clases públicas: Person y person . Como solo se distinguen por el
uso de mayúsculas, el compilador de C# las marca como no conformes a CLS.
using System;
[assembly: CLSCompliant(true)]
}
// Compilation produces a compiler warning like the following:
// Naming1.cs(11,14): warning CS3005: Identifier 'person' differing
// only in case is not CLS-compliant
// Naming1.cs(6,14): (Location of symbol related to previous warning)
Los identificadores del lenguaje de programación, como los nombres de espacios de nombres, tipos y miembros,
deben ajustarse al Estándar Unicode. Esto significa que:
El primer carácter de un identificador puede ser cualquier letra en mayúscula, letra en minúscula, letra de
inicial en mayúscula, letra modificadora, otra letra o número de letra. Para obtener información acerca de las
categorías de caracteres Unicode, consulte la enumeración System.Globalization.UnicodeCategory.
Los caracteres siguientes pueden proceder de cualquier categoría cuando funcionan como primer carácter y
también pueden incluir marcas no espaciadas, marcas de combinación de espaciado, números decimales,
puntuaciones de conexión y códigos de formato.
Antes de comparar los identificadores, debe filtrar los códigos de formato y convertir los identificadores a la forma
de normalización Unicode C, ya que un mismo carácter se puede representar mediante diferentes unidades de
código UTF-16. Las secuencias de caracteres que producen las mismas unidades de código en la forma de
normalización Unicode C no son conformes a CLS. En el ejemplo siguiente se define una propiedad llamada Å ,
que se compone del carácter SIGNO DE ANGSTROM (U+212B) y una segunda propiedad llamada Å , que se
compone de la LETRA MAYÚSCULA A LATINA CON UN ANILLO ENCIMA (U+00C5). El compilador de C# marca el
código fuente como no conforme a CLS.
public double Å
{
get { return a1; }
set { a1 = value; }
}
public double Å
{
get { return a2; }
set { a2 = value; }
}
}
// Compilation produces a compiler warning like the following:
// Naming2a.cs(16,18): warning CS3005: Identifier 'Size.Å' differing only in case is not
// CLS-compliant
// Naming2a.cs(10,18): (Location of symbol related to previous warning)
// Naming2a.cs(18,8): warning CS3005: Identifier 'Size.Å.get' differing only in case is not
// CLS-compliant
// Naming2a.cs(12,8): (Location of symbol related to previous warning)
// Naming2a.cs(19,8): warning CS3005: Identifier 'Size.Å.set' differing only in case is not
// CLS-compliant
// Naming2a.cs(13,8): (Location of symbol related to previous warning)
<Assembly: CLSCompliant(True)>
Public Class Size
Private a1 As Double
Private a2 As Double
Los nombres de miembros con un ámbito determinado (como los espacios de nombres de un ensamblado, los
tipos de un espacio de nombres o los miembros de un tipo) deben ser únicos, excepto los nombres que se
resuelven a través de la sobrecarga. Este requisito es más estricto que el del sistema de tipos comunes, que permite
a varios miembros de un ámbito tener nombres idénticos siempre que sean diferentes tipos de miembros (por
ejemplo, que uno sea un método y otro, un campo). En particular, en el caso de los miembros de tipo:
Los campos y los tipos anidados solo se distinguen por el nombre.
Los métodos, las propiedades y los eventos que tienen el mismo nombre deben distinguirse por algo más
que el tipo de valor devuelto.
En el ejemplo siguiente se muestra el requisito que establece que los nombres de miembro deben ser únicos
dentro de su ámbito. En este ejemplo se define una clase denominada Converter , que incluye cuatro miembros
denominados Conversion . Tres son métodos y uno es una propiedad. El método que incluye un parámetro Int64
recibe un nombre único, pero no ocurre lo mismo con los dos métodos que tienen un parámetro Int32 , ya que el
valor devuelto no se considera parte de la signatura del miembro. La propiedad Conversion también infringe este
requisito, ya que las propiedades no pueden tener el mismo nombre que los métodos sobrecargados.
using System;
[assembly: CLSCompliant(true)]
Todos los lenguajes contienen palabras claves únicas, de modo que los lenguajes dirigidos a Common Language
Runtime también deben proporcionar un mecanismo para hacer referencia a identificadores (como nombres de
tipo) que coincidan con las palabras clave. Por ejemplo, case es una palabra clave en C# y Visual Basic. Sin
embargo, en el siguiente ejemplo de Visual Basic se elimina la ambigüedad entre una clase denominada case y la
palabra clave case mediante llaves de apertura y cierre. De lo contrario, el ejemplo produciría el mensaje de error
"Una palabra clave no es válida como identificador" y no se compilaría.
En el siguiente ejemplo de C# se crean instancias de la clase case usando el símbolo @ para eliminar la
ambigüedad entre el identificador y la palabra clave del lenguaje. Sin él, el compilador de C# mostraría dos
mensajes de error similares a los siguientes: "Se esperaba un tipo" y "'Término 'case' de expresión no válido".
using System;
Conversión de tipos
Common Language Specification define dos operadores de conversión:
op_Implicit , que se utiliza en las conversiones de ampliación que no dan lugar a la pérdida de datos o de
precisión. Por ejemplo, la estructura Decimal contiene un operador sobrecargado op_Implicit para
convertir valores de tipos enteros y valores Char en valores Decimal .
op_Explicit , que se utiliza en las conversiones de restricción que pueden producir una pérdida de magnitud
(un valor se convierte en un valor que tiene un intervalo menor) o de precisión. Por ejemplo, la estructura
Decimal contiene un operador sobrecargado op_Explicit para convertir los valores Double y Single en
Decimal , y convertir los valores Decimal en los valores integrales Double , Single y Char .
Sin embargo, no todos los lenguajes admiten la sobrecarga de operadores o la definición de operadores
personalizados. Si decide implementar estos operadores de conversión, debe proporcionar un mecanismo
alternativo para realizar la conversión. Se recomienda proporcionar los métodos From Xxx y To Xxx.
En el ejemplo siguiente se definen conversiones implícitas y explícitas conformes a CLS. Se crea una clase UDouble
que representa un número de punto flotante con signo de precisión doble. En las conversiones implícitas, pasa de
UDouble a Double y, en las conversiones explícitas, de UDouble a Single , de Double a UDouble y de Single a
UDouble . También define un método ToDouble como alternativa al operador de conversión implícita y los métodos
ToSingle , FromDouble y FromSingle como alternativas a los operadores de conversión explícitos.
using System;
number = value;
}
number = value;
}
Matrices
Las matrices conformes a CLS cumplen las reglas siguientes:
Todas las dimensiones de una matriz deben tener un límite inferior igual a cero. En el ejemplo siguiente se
crea una matriz no conforme a CLS cuyo límite inferior es uno. Pese a la presencia del atributo
CLSCompliantAttribute, el compilador no detecta que la matriz devuelta por el método
Numbers.GetTenPrimes no es conforme a CLS.
[assembly: CLSCompliant(true)]
return arr;
}
}
<Assembly: CLSCompliant(True)>
Todos los elementos de la matriz deben componerse de tipos conformes a CLS. En el ejemplo siguiente se
definen dos métodos que devuelven matrices no conformes a CLS. El primero devuelve una matriz de
valores UInt32. El segundo devuelve una matriz Object que contiene los valores Int32 y UInt32 . Aunque el
compilador identifica la primera matriz como no conforme debido a su tipo UInt32 , no reconoce que la
segunda matriz incluye elementos no conformes a CLS.
using System;
[assembly: CLSCompliant(true)]
<Assembly: CLSCompliant(True)>
La resolución de desbordamiento de los métodos que tienen parámetros de matriz se basa en el hecho de
que son matrices y en su tipo de elemento. Por esta razón, la siguiente definición de un método GetSquares
sobrecargado es conforme a CLS.
using System;
using System.Numerics;
[assembly: CLSCompliant(true)]
}
return numbersOut;
}
return numbersOut;
}
}
Imports System.Numerics
<Assembly: CLSCompliant(True)>
Interfaces
Las interfaces conformes a CLS pueden definir propiedades, eventos y métodos virtuales (métodos sin
implementación). Una interfaz conforme a CLS no puede tener ninguno de los elementos siguientes:
Métodos estáticos o campos estáticos. El compilador de C# genera errores de compilador si se define un
miembro estático en una interfaz.
Campos. El compilador de C# genera errores de compilador si se define un campo en una interfaz.
Métodos que no son conformes a CLS. Por ejemplo, la siguiente definición de interfaz incluye un método,
INumber.GetUnsigned , que está marcado como no conforme a CLS. Este ejemplo genera una advertencia del
compilador.
using System;
[assembly:CLSCompliant(true)]
<Assembly: CLSCompliant(True)>
Debido a esta regla, no se necesitan tipos conformes a CLS para implementar miembros no conformes a
CLS. Si un marco conforme a CLS expone una clase que implementa una interfaz no conforme a CLS,
también debe proporcionar implementaciones concretas de todos los miembros no conformes a CLS.
Los compiladores de lenguaje conformes a CLS también deben permitir que una clase proporcione
implementaciones independientes de miembros con el mismo nombre y la misma signatura en varias interfaces.
C# admite implementaciones de interfaz explícitas para proporcionar diferentes implementaciones de métodos con
el mismo nombre. En el ejemplo siguiente se muestra este escenario con la definición de una clase Temperature
que implementa las interfaces ICelsius y IFahrenheit como implementaciones de interfaces explícitas.
using System;
[assembly: CLSCompliant(true)]
decimal IFahrenheit.GetTemperature()
{
return _value * 9 / 5 + 32;
}
decimal ICelsius.GetTemperature()
{
return _value;
}
}
public class Example
{
public static void Main()
{
Temperature temp = new Temperature(100.0m);
ICelsius cTemp = temp;
IFahrenheit fTemp = temp;
Console.WriteLine("Temperature in Celsius: {0} degrees",
cTemp.GetTemperature());
Console.WriteLine("Temperature in Fahrenheit: {0} degrees",
fTemp.GetTemperature());
}
}
// The example displays the following output:
// Temperature in Celsius: 100.0 degrees
// Temperature in Fahrenheit: 212.0 degrees
Assembly: CLSCompliant(True)>
Module Example
Public Sub Main()
Dim temp As New Temperature(100.0d)
Console.WriteLine("Temperature in Celsius: {0} degrees",
temp.GetCelsius())
Console.WriteLine("Temperature in Fahrenheit: {0} degrees",
temp.GetFahrenheit())
End Sub
End Module
' The example displays the following output:
' Temperature in Celsius: 100.0 degrees
' Temperature in Fahrenheit: 212.0 degrees
Enumeraciones
Las enumeraciones conformes a CLS deben seguir estas reglas:
El tipo subyacente de una enumeración debe ser un entero intrínseco conforme a CLS (Byte, Int16, Int32 o
Int64). Por ejemplo, el código siguiente intenta definir una enumeración cuyo tipo subyacente es UInt32 y
genera una advertencia del compilador.
using System;
[assembly: CLSCompliant(true)]
<Assembly: CLSCompliant(True)>
Un tipo de enumeración debe tener un campo de instancia único denominado Value__ marcado con el
atributo FieldAttributes.RTSpecialName . Esto permite hacer referencia al valor del campo de forma implícita.
Las enumeraciones incluyen campos estáticos literales del mismo tipo que el de la enumeración. Por
ejemplo, si define una enumeración State con los valores State.On y State.Off , State.On y State.Off
son campos estáticos literales cuyo tipo será State .
Hay dos tipos de enumeraciones:
Las enumeraciones que representan un conjunto de valores enteros con nombre mutuamente
excluyentes. Este tipo de enumeración se indica por la ausencia del atributo personalizado
System.FlagsAttribute.
Las enumeraciones que representan un conjunto de marcadores de bits que se pueden combinar
para generar un valor sin nombre. Este tipo de enumeración se indica por la presencia del atributo
personalizado System.FlagsAttribute.
Para obtener más información, consulte la documentación de la estructura Enum.
El valor de una enumeración no se limita al intervalo de sus valores especificados. Es decir, el intervalo de
valores de una enumeración es el intervalo de su valor subyacente. Puede utilizar el método Enum.IsDefined
para determinar si un valor especificado es miembro de una enumeración.
Miembros de tipos en general
Common Language Specification necesita todos los campos y métodos accesibles como miembros de una clase
determinada. Por tanto, los métodos y los campos estáticos globales (es decir, los métodos o los campos que se
definen con independencia de un tipo) no son conformes a CLS. Si intenta incluir un campo o un método global en
el código fuente, el compilador de C# generará un error de compilación.
Common Language Specification solo admite la convención de llamada administrada estándar. No admite
convenciones de llamada ni métodos no administrados con listas de argumentos variables marcados con la palabra
clave varargs . En el caso de las listas de argumentos variables que son compatibles con la convención de llamada
administrada estándar, use el atributo ParamArrayAttribute o la implementación del lenguaje específico, como la
palabra clave params en C# y la palabra clave ParamArray en Visual Basic.
Accesibilidad de miembros
Al reemplazar un miembro heredado no se puede cambiar la accesibilidad de dicho miembro. Por ejemplo, un
método público de una clase base no se puede reemplazar por un método privado de una clase derivada. Existe
una excepción: un miembro protected internal (en C#) o Protected Friend (en Visual Basic) de un ensamblado
que se haya reemplazado por un tipo de un ensamblado diferente. En ese caso, la accesibilidad del reemplazo es
Protected .
En el ejemplo siguiente se muestra el error que se genera cuando el atributo CLSCompliantAttribute se establece
en true y Person , que es una clase derivada de Animal , intenta cambiar la accesibilidad de la propiedad Species
de pública a privada. El ejemplo se compila correctamente si su accesibilidad se cambia a pública.
using System;
[assembly: CLSCompliant(true)]
Los tipos de la signatura de un miembro deben estar accesibles siempre que dicho miembro esté accesible. Esto
significa, por ejemplo, que un miembro público no puede incluir un parámetro cuyo tipo sea privado, protegido o
interno. En el ejemplo siguiente se muestra el error del compilador que se produce cuando un constructor de clase
StringWrapper expone un valor de enumeración StringOperationType interno que determina cómo debe ajustarse
un valor de cadena.
using System;
using System.Text;
Imports System.Text
<Assembly:CLSCompliant(True)>
using System;
[assembly:CLSCompliant(true)]
Los nombres de tipos genéricos se codifican con el formato name n, donde name es el nombre del tipo, ` es un
carácter literal y n es el número de parámetros declarados en el tipo o, en el caso de tipos genéricos anidados, el
número de parámetros de tipo recién incorporados. Esta codificación de nombres de tipo genérico tiene interés
fundamentalmente para los desarrolladores que utilizan la reflexión a fin de acceder a los tipos genéricos
conformes a CLS de una biblioteca.
Si las restricciones se aplican a un tipo genérico, los tipos utilizados como restricciones también deben ser
conformes a CLS. En el ejemplo siguiente se define una clase denominada BaseClass que no es conforme a CLS y
una clase genérica denominada BaseCollection cuyo parámetro de tipo debe derivarse de BaseClass . Sin
embargo, puesto que BaseClass no es conforme a CLS, el compilador emite una advertencia.
using System;
[assembly:CLSCompliant(true)]
Assembly: CLSCompliant(True)>
Si un tipo genérico se deriva de un tipo base genérico, es necesario volver a declarar las restricciones para que se
pueda garantizar que las restricciones del tipo base también se cumplen. En el ejemplo siguiente se define un
objeto Number<T> que puede representar cualquier tipo numérico. También se define una clase FloatingPoint<T>
que representa un valor de punto flotante. Sin embargo, el código fuente no puede compilarse, ya que no aplica la
restricción de Number<T> (T debe ser un tipo de valor) en FloatingPoint<T> .
using System;
[assembly:CLSCompliant(true)]
[assembly:CLSCompliant(true)]
Common Language Specification impone un modelo conservador adaptado a cada instancia en los tipos anidados
y los miembros protegidos. Los tipos genéricos abiertos no pueden exponer campos ni miembros con signaturas
que contengan una instancia específica de un tipo genérico anidado y protegido. Los tipos no genéricos que
amplíen una instancia específica de una interfaz o clase base genérica no pueden exponer campos ni miembros con
signaturas que contengan otra instancia de un tipo genérico anidado y protegido.
En el ejemplo siguiente se define un tipo genérico, C1<T> , y una clase protegida, C1<T>.N . C1<T> tiene dos
métodos: M1 y M2 . Pero M1 no es conforme a CLS porque intenta devolver un objeto C1<int>.N a partir de
C1<T> . Una segunda clase, C2 , se deriva de C1<long> . Esta clase tiene dos métodos, M3 y M4 . M3 o es conforme
a CLS porque intenta devolver un objeto C1<int>.N a partir de una subclase de C1<long> . Los compiladores del
lenguaje pueden ser aún más restrictivos. En este ejemplo, Visual Basic muestra un error cuando intenta compilar
M4 .
using System;
[assembly:CLSCompliant(true)]
<Assembly:CLSCompliant(True)>
Protected Sub M1(n As C1(Of Integer).N) ' Not CLS-compliant - C1<int>.N not
' accessible from within C1(Of T) in all
End Sub ' languages
Protected Sub M2(n As C1(Of T).N) ' CLS-compliant – C1(Of T).N accessible
End Sub ' inside C1(Of T)
End Class
[assembly: CLSCompliant(true)]
fName = firstName;
lName = lastName;
_id = id;
}
public string Id
{
get { return _id; }
}
fName = firstName
lName = lastName
_id = id
End Sub
No se puede llamar a un constructor de objetos excepto para crear un objeto. Además, un objeto no se
puede inicializar dos veces. Por ejemplo, esto significa que Object.MemberwiseClone no debe llamar a los
constructores.
Propiedades
Las propiedades de los tipos conformes a CLS deben seguir estas reglas:
Una propiedad debe tener un establecedor, un captador o ambos. En un ensamblado, estos elementos se
implementan como métodos especiales, lo que significa que aparecerán como métodos independientes (el
captador se llama get _propertyname y el establecedor es set _propertyname) marcados como
SpecialName en los metadatos del ensamblado. El compilador de C# aplica esta regla automáticamente sin
necesidad de aplicar el atributo CLSCompliantAttribute.
El tipo de una propiedad es el tipo de valor devuelto del captador de la propiedad y el último argumento del
establecedor. Estos tipos deben ser conformes a CLS y los argumentos no se pueden asignar a la propiedad
por referencia (es decir, no pueden ser punteros administrados).
Si una propiedad tiene un captador y un establecedor, estos deben ser virtuales, estáticos o de instancia. El
compilador de C# aplica automáticamente esta regla mediante la sintaxis de la definición de la propiedad.
Events
Un evento se define por su nombre y su tipo. El tipo de evento es un delegado que se utiliza para indicar el evento.
Por ejemplo, el evento DbConnection.StateChange es del tipo StateChangeEventHandler . Además del propio evento,
hay tres métodos con nombres basados en el nombre de evento que proporcionan la implementación del evento y
que se marcan como SpecialName en los metadatos de ensamblado:
Un método para agregar un controlador de eventos, llamado add EventName. Por ejemplo, el método de
suscripción de eventos del evento DbConnection.StateChange se denomina add_StateChange .
Un método para quitar un controlador de eventos, llamado remove EventName. Por ejemplo, el método de
eliminación del evento DbConnection.StateChange se denomina remove_StateChange .
Un método para indicar que el evento se ha producido, llamado raise _EventName.
NOTE
La mayoría de las reglas de Common Language Specification relacionadas con los eventos se implementan mediante
compiladores del lenguaje y son transparentes para los desarrolladores de componentes.
Los métodos para agregar, quitar y generar el evento deben tener la misma accesibilidad. Además, todos deben ser
estáticos, virtuales o de instancia. Los métodos para agregar y quitar un evento tienen un parámetro cuyo tipo es el
mismo que el del delegado de eventos. Los métodos para agregar y quitar deben estar presentes o ausentes al
mismo tiempo.
En el ejemplo siguiente se define una clase conforme a CLS denominada Temperature que genera un evento
TemperatureChanged si el cambio de temperatura entre dos lecturas es igual o mayor que el valor de umbral. La
clase Temperature define de manera explícita un método raise_TemperatureChanged para que pueda ejecutar
controladores de eventos de forma selectiva.
using System;
using System.Collections;
using System.Collections.Generic;
[assembly: CLSCompliant(true)]
public Example()
{
temp = new Temperature(65, 3);
temp.TemperatureChanged += this.TemperatureNotification;
RecordTemperatures();
Example ex = new Example(temp);
ex.RecordTemperatures();
}
public Example(Temperature t)
{
temp = t;
RecordTemperatures();
}
Imports System.Collections
Imports System.Collections.Generic
<Assembly: CLSCompliant(True)>
End Sub
Overloads
Common Language Specification impone los siguientes requisitos a los miembros sobrecargados:
Los miembros se pueden sobrecargar según el número de parámetros y el tipo de cualquiera de los
parámetros. A la hora de distinguir entre sobrecargas, no se tienen en cuenta los factores de convención de
llamada, el tipo de valor devuelto, los modificadores personalizados aplicados al método o a su parámetro,
ni si los parámetros se pasan por valor o por referencia. Para consultar un ejemplo, vea el código del
requisito que establece que los nombres deben ser únicos en cada ámbito que se incluye en la sección
Convenciones de nomenclatura.
Solo las propiedades y los métodos se pueden sobrecargar. Los campos y eventos no se pueden sobrecargar.
Los métodos genéricos pueden sobrecargarse en función del número de parámetros genéricos.
NOTE
Los operadores op_Explicit y op_Implicit son una excepción de la regla que establece que el valor devuelto no se
considera parte de la signatura de un método en la resolución de la sobrecarga. Estos dos operadores se pueden sobrecargar
según sus parámetros y su valor devuelto.
Excepciones
Los objetos de excepción deben derivar de System.Exception o de otro tipo derivado de System.Exception . En el
ejemplo siguiente se muestra el error de compilador que se produce cuando una clase personalizada denominada
ErrorClass se utiliza para el control de excepciones.
using System;
[assembly: CLSCompliant(true)]
<Assembly: CLSCompliant(True)>
Para corregir este error, la clase ErrorClass debe heredar de System.Exception . Además, la propiedad Message
debe invalidarse. En el ejemplo siguiente se corrigen estos errores para definir una clase ErrorClass que sea
conforme a CLS.
using System;
[assembly: CLSCompliant(true)]
Imports System.Runtime.CompilerServices
<Assembly: CLSCompliant(True)>
Atributos
En los ensamblados de .NET Framework, los atributos personalizados proporcionan un mecanismo extensible para
almacenar atributos personalizados y recuperar metadatos sobre objetos de programación, tales como
ensamblados, tipos, miembros y parámetros de método. Los atributos personalizados deben derivar de
System.Attribute o de un tipo derivado de System.Attribute .
En el ejemplo siguiente se infringe esta regla. Define una clase NumericAttribute que no se deriva de
System.Attribute . Un error del compilador solo se produce cuando se aplica el atributo no conforme a CLS, y no
cuando se define la clase.
using System;
[assembly: CLSCompliant(true)]
[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Struct)]
public class NumericAttribute
{
private bool _isNumeric;
<Assembly: CLSCompliant(True)>
<AttributeUsageAttribute(AttributeTargets.Class Or AttributeTargets.Struct)> _
Public Class NumericAttribute
Private _isNumeric As Boolean
En el ejemplo siguiente se define una clase DescriptionAttribute que se deriva de Attribute. El constructor de clase
tiene un parámetro de tipo Descriptor , de modo que la clase no es conforme a CLS. El compilador de C# emite
una advertencia, pero realiza la compilación correctamente.
using System;
[assembly:CLSCompliantAttribute(true)]
[AttributeUsage(AttributeTargets.All)]
public class DescriptionAttribute : Attribute
{
private Descriptor desc;
public DescriptionAttribute(Descriptor d)
{
desc = d;
}
<AttributeUsage(AttributeTargets.All)> _
Public Class DescriptionAttribute : Inherits Attribute
Private desc As Descriptor
CLSCompliantAttribute (Atributo)
El atributo CLSCompliantAttribute se usa para indicar si un elemento del programa es conforme a Common
Language Specification. El constructor CLSCompliantAttribute.CLSCompliantAttribute(Boolean) contiene un único
parámetro obligatorio, isCompliant, que indica si el elemento del programa es conforme a CLS.
En tiempo de compilación, el compilador detecta los elementos que supuestamente deberían ser conformes a CLS
y emite una advertencia. El compilador no emite advertencias relacionadas con los tipos o miembros cuya no
conformidad se declara explícitamente.
Los desarrolladores de componentes pueden usar el atributo CLSCompliantAttribute de dos maneras:
Para definir las partes de la interfaz pública expuestas por un componente que son conformes a CLS y las
que no lo son. Cuando el atributo se utiliza para marcar determinados elementos del programa como
conformes a CLS, este uso garantiza que dichos elementos son accesibles desde todos los lenguajes y
herramientas que tienen como destino .NET Framework.
Para garantizar que la interfaz pública de la biblioteca de componentes expone solo elementos del programa
que son conformes a CLS. Si los elementos no son conformes a CLS, los compiladores normalmente
emitirán una advertencia.
WARNING
En algunos casos, los compiladores de lenguaje aplican reglas conformes a CLS independientemente de si se usa el atributo
CLSCompliantAttribute o no. Por ejemplo, la definición de un miembro *static en una interfaz infringe una regla de CLS.
Pero si define un miembro *static en una interfaz, el compilador de C# muestra un mensaje de error y se produce un error
al compilar la aplicación.
El atributo CLSCompliantAttribute está marcado con un atributo AttributeUsageAttribute que tiene el valor
AttributeTargets.All . Este valor le permite aplicar el atributo CLSCompliantAttribute a cualquier elemento de
programa, incluidos los ensamblados, módulos, tipos (clases, estructuras, enumeraciones, interfaces, delegados),
miembros de tipo (constructores, métodos, propiedades, campos y eventos), parámetros, parámetros genéricos y
valores devueltos. Sin embargo, en la práctica, solo debe aplicar el atributo a los ensamblados, tipos y miembros
del tipo. De lo contrario, los compiladores omitirán el atributo y seguirán generando advertencias de compilación
siempre que encuentren un parámetro no conforme, un parámetro genérico o un valor devuelto en la interfaz
pública de la biblioteca.
El valor del atributo CLSCompliantAttribute lo heredan los elementos del programa contenidos. Por ejemplo, si se
marca un ensamblado como conforme a CLS, sus tipos también son conformes a CLS. Si un tipo se marca como
conforme a CLS, sus tipos anidados y miembros también serán conformes a CLS.
Puede invalidar explícitamente la conformidad heredada aplicando el atributo CLSCompliantAttribute a un
elemento del programa contenido. Por ejemplo, puede usar el atributo CLSCompliantAttribute con el valor
isCompliant establecido en false para definir un tipo no conforme en un ensamblado conforme, y puede usar el
atributo con el valor isCompliant establecido en true para definir un tipo conforme en un ensamblado no
conforme. También puede definir miembros no conformes en un tipo conforme. Pero un tipo no conforme no
puede tener miembros conformes, por lo que no puede usar el atributo con el valor isCompliant establecido en
true para invalidar la herencia de un tipo no conforme.
Cuando desarrolle componentes, debe utilizar siempre el atributo CLSCompliantAttribute para indicar si el
ensamblado, sus tipos y sus miembros son conformes a CLS.
Para crear componentes conformes a CLS:
1. Utilice CLSCompliantAttribute para marcar el ensamblado como conforme a CLS.
2. Marque como no conforme los tipos expuestos públicamente en el ensamblado que no sean conformes a
CLS.
3. Marque como no conforme los miembros expuestos públicamente en tipos conformes a CLS.
4. Proporcione una alternativa conforme a CLS para los miembros que no sean conformes a CLS.
Si ha marcado correctamente todos los tipos y miembros no conformes, el compilador no debe emitir ninguna
advertencia de no conformidad. Sin embargo, debe indicar qué miembros no son conformes a CLS y mostrar las
alternativas conformes a CLS en la documentación del producto.
En el ejemplo siguiente se usa el atributo CLSCompliantAttribute para definir un ensamblado conforme a CLS y un
tipo, CharacterUtilities , que tiene dos miembros no conformes a CLS. Dado que ambos miembros están
etiquetados con el atributo CLSCompliant(false) , el compilador no genera ninguna advertencia. La clase también
proporciona una alternativa conforme a CLS para ambos métodos. Por lo general, simplemente se agregarían dos
sobrecargas al método ToUTF16 para proporcionar alternativas conformes a CLS. Sin embargo, puesto que no se
pueden sobrecargar los métodos en función de un valor devuelto, los nombres de los métodos conformes a CLS
son distintos de los nombres de los métodos no conformes.
using System;
using System.Text;
[assembly:CLSCompliant(true)]
if (chars.Length == 2) {
if (! Char.IsSurrogatePair(chars[0], chars[1]))
throw new ArgumentException("The array must contain a low and a high surrogate.");
else
return Char.ConvertToUtf32(chars[0], chars[1]);
}
else {
return Char.ConvertToUtf32(chars.ToString(), 0);
}
}
}
Imports System.Text
<Assembly:CLSCompliant(True)>
Si está desarrollando una aplicación en lugar de una biblioteca (es decir, si no expone los tipos o miembros que
pueden consumir otros desarrolladores de aplicaciones), la conformidad con CLS de los elementos del programa
usados por la aplicación solo tendrá interés si su lenguaje admite estos elementos. En ese caso, el compilador del
lenguaje generará un error cuando intente utilizar un elemento no conforme a CLS.
Imports System.Collections.Generic
Imports System.Runtime.CompilerServices
Sub New()
Dim words() As String = { "a", "an", "and", "of", "the" }
exclusions = New List(Of String)
exclusions.AddRange(words)
End Sub
<Extension()> _
Public Function ToTitleCase(title As String) As String
Dim words() As String = title.Split()
Dim result As String = String.Empty
A continuación se muestra el código fuente para NumberUtil.cs, que define una clase NumericLib con dos
miembros, IsEven y NearZero .
using System;
Para empaquetar las dos clases en un solo ensamblado, debe compilarlas en módulos. Para compilar el archivo de
código fuente de Visual Basic en un módulo, use este comando:
A continuación, use la herramienta de vinculación (Link.exe) para compilar los dos módulos en un ensamblado:
El ejemplo siguiente llama a los métodos NumericLib.NearZero y StringLib.ToTitleCase . El código de Visual Basic y
el código de C# pueden tener acceso a los métodos de ambas clases.
using System;
Para compilar con C#, cambie el nombre del compilador de vbc a csc y cambie la extensión de archivo .vb a .cs:
.NET Framework. es independiente del lenguaje. Esto significa que, como desarrollador, puede utilizar uno de los
muchos lenguajes que tienen como destino .NET Framework; por ejemplo, C#, C++/CLI, Eiffel, F#, IronPython,
IronRuby, PowerBuilder, Visual Basic, Visual COBOL y Windows PowerShell. Puede acceder a los tipos y miembros
de las bibliotecas de clases desarrolladas para .NET Framework sin necesidad de conocer el lenguaje en el que se
escribieron originalmente y sin necesidad de seguir ninguna de las convenciones del lenguaje original. Si es un
desarrollador de componentes, podrá acceder a su componente desde cualquier aplicación de .NET Framework,
con independencia del lenguaje.
NOTE
En la primera parte de este artículo, se explica cómo se crean componentes independientes del lenguaje, es decir,
componentes que pueden utilizarse en aplicaciones escritas en cualquier lenguaje. También puede crear una aplicación o
componente únicos de código fuente escrito en varios lenguajes; consulte Interoperabilidad entre lenguajes en la segunda
parte de este artículo.
Para que los objetos puedan tener una interacción total con otros objetos escritos en cualquier lenguaje, estos
objetos solo deben exponer a los llamadores las características que son comunes a todos los lenguajes. Este
conjunto común de características se define mediante Common Language Specification (CLS), que es un
conjunto de reglas que se aplican a los ensamblados generados. Common Language Specification se define en el
apartado I, cláusulas 7 a 11 del estándar ECMA-335: Common Language Infrastructure.
Si el componente se ajusta a Common Language Specification, existe la garantía de que será conforme a CLS y
que será accesible desde el código de un ensamblado escrito en cualquier lenguaje de programación que admita
CLS. Para determinar si el componente se ajusta o no a Common Language Specification en tiempo de
compilación, puede aplicar el atributo CLSCompliantAttribute en el código fuente. Para más información, consulte
CLSCompliantAttribute (Atributo).
En este artículo:
Reglas de conformidad con CLS
Signaturas de tipos y miembros de tipo
Convenciones de nomenclatura
Conversión de tipos
Matrices
Interfaces
Enumeraciones
Miembros de tipos en general
Accesibilidad de miembros
Miembros y tipos genéricos
Constructores
Propiedades
Eventos
Sobrecargas
Excepciones
Atributos
CLSCompliantAttribute (Atributo)
Interoperabilidad entre lenguajes
NOTE
Common Language Specification describe en cada regla la conformidad con CLS en referencia a los consumidores
(desarrolladores que acceden mediante programación a un componente que es conforme a CLS), los marcos
(desarrolladores que usan un compilador de lenguaje para crear bibliotecas conformes a CLS) y los extensores
(desarrolladores que crean una herramienta, como un compilador de lenguaje o un analizador de código, que crea
componentes conformes a CLS). Este artículo se centra en las reglas que se aplican a los marcos. Sin embargo, observe que
algunas de las reglas que se aplican a los extensores también se pueden aplicar a los ensamblados que se crean mediante
Reflection.Emit.
Para diseñar un componente que sea independiente del lenguaje, solo tiene que aplicar las reglas de
conformidad con CLS a la interfaz pública del componente. La implementación privada no tiene que ajustarse a la
especificación.
IMPORTANT
Las reglas de conformidad con CLS solo se aplican a la interfaz pública de un componente, y no a su implementación
privada.
Por ejemplo, los números enteros sin signo distintos de Byte no son conformes a CLS. Dado que la clase Person
del siguiente ejemplo expone una propiedad Age del tipo UInt16, el código siguiente desencadena una
advertencia del compilador.
using System;
[assembly: CLSCompliant(true)]
Para hacer que la clase Person sea conforme a CLS, puede cambiar el tipo de la propiedad Age de UInt16 a
Int16, que es un entero de 16 bits con signo conforme a CLS. No es necesario que cambie el tipo del campo
privado personAge .
using System;
[assembly: CLSCompliant(true)]
<Assembly: CLSCompliant(True)>
[assembly: CLSCompliant(true)]
[CLSCompliant(false)]
public class Counter
{
UInt32 ctr;
public Counter()
{
ctr = 0;
}
<CLSCompliant(False)> _
Public Class Counter
Dim ctr As UInt32
Todos los tipos que aparecen en las signaturas de miembros, incluidos los tipos de propiedades y los tipos de
valores devueltos de un método, deben ser conformes a CLS. Además, en el caso de los tipos genéricos:
Todos los tipos que forman un tipo genérico con instancias deben ser conformes a CLS.
Todos los tipos que se utilizan como restricciones en parámetros genéricos deben ser conformes a CLS.
El sistema de tipos común de .NET Framework incluye varios tipos integrados que se admiten directamente en
Common Language Runtime y que se codifican de forma especial en los metadatos de un ensamblado. De estos
tipos intrínsecos, los tipos enumerados en la tabla siguiente son conformes a CLS.
T IP O C O N F O RM E A C L S DESC RIP C IÓ N
UInt64 Entero de 64 bits sin signo Int64 (se puede desbordar), BigInteger
o Double
La biblioteca de clases de .NET Framework o cualquier otra biblioteca de clases puede incluir otros tipos que no
sean conformes a CLS; por ejemplo:
Tipos de valor a los que se les ha aplicado la conversión boxing. En el siguiente ejemplo de C# se crea una
clase con una propiedad pública de tipo int* denominada Value . Dado que int* es un tipo de valor al
que se le ha aplicado la conversión boxing, el compilador lo marca como no conforme a CLS.
using System;
[assembly:CLSCompliant(true)]
Referencias con establecimiento de tipos, que son construcciones especiales que contienen una referencia
a un objeto y una referencia a un tipo. Las referencias con establecimiento de tipos se representan en .NET
Framework mediante la clase TypedReference.
Si un tipo no es conforme a CLS, deberá aplicarle el atributo CLSCompliantAttribute con el valor de isCompliant
establecido en false . Para obtener más información, vea la sección CLSCompliantAttribute (Atributo).
En el ejemplo siguiente se muestra el problema de la conformidad con CLS en la creación de instancias de tipos
genéricos y signaturas de métodos. En este ejemplo, se define una clase InvoiceItem con una propiedad de tipo
UInt32, una propiedad de tipo Nullable(Of UInt32) y un constructor con parámetros de tipo UInt32 y
Nullable(Of UInt32) . Cuando intente compilar este ejemplo, aparecerán cuatro advertencias del compilador.
using System;
[assembly: CLSCompliant(true)]
Para eliminar las advertencias del compilador, reemplace los tipos no conformes a CLS de la interfaz pública de
InvoiceItem por tipos conformes:
using System;
[assembly: CLSCompliant(true)]
qty = quantity;
}
Además de los tipos específicos indicados, algunas categorías de tipos no son conformes a CLS. Entre estas
categorías se incluyen tipos de punteros no administrados y tipos de punteros de función. En el ejemplo
siguiente se genera una advertencia del compilador, ya que se utiliza un puntero a un entero para crear una
matriz de enteros.
using System;
[assembly: CLSCompliant(true)]
En las clases abstractas conformes a CLS (es decir, clases marcadas como abstract en C# o como MustInherit
en Visual Basic), todos los miembros de dichas clases deben ser también conformes a CLS.
Convenciones de nomenclatura
Dado que algunos lenguajes de programación distinguen entre mayúsculas y minúsculas, los identificadores
(como los nombres de espacios de nombres, los tipos y los miembros) deben tener otro elemento distintivo
aparte del uso de mayúsculas. Dos identificadores se consideran equivalentes si sus asignaciones de minúsculas
son iguales. En el ejemplo de C# siguiente, se definen dos clases públicas: Person y person . Como solo se
distinguen por el uso de mayúsculas, el compilador de C# las marca como no conformes a CLS.
using System;
[assembly: CLSCompliant(true)]
Los identificadores de los lenguajes de programación, como los nombres de los espacios de nombres, tipos y
miembros, deben ajustarse al Estándar Unicode 3.0, Informe técnico 15, Anexo 7. Esto significa que:
El primer carácter de un identificador puede ser cualquier letra en mayúscula, letra en minúscula, letra de
inicial en mayúscula, letra modificadora, otra letra o número de letra. Para obtener información acerca de
las categorías de caracteres Unicode, vea la enumeración System.Globalization.UnicodeCategory.
Los caracteres siguientes pueden proceder de cualquier categoría cuando funcionan como primer carácter
y también pueden incluir marcas no espaciadas, marcas de combinación de espaciado, números
decimales, puntuaciones de conexión y códigos de formato.
Antes de comparar los identificadores, debe filtrar los códigos de formato y convertir los identificadores a la
forma de normalización Unicode C, ya que un mismo carácter se puede representar mediante diferentes
unidades de código UTF-16. Las secuencias de caracteres que producen las mismas unidades de código en la
forma de normalización Unicode C no son conformes a CLS. En el ejemplo siguiente se define una propiedad
llamada Å , que se compone del carácter SIGNO DE ANGSTROM (U+212B) y una segunda propiedad llamada
Å , que se compone de la LETRA MAYÚSCULA A LATINA CON UN ANILLO ENCIMA (U+00C5). Los compiladores
de C# y Visual Basic identifican el código fuente como no conforme a CLS.
public class Size
{
private double a1;
private double a2;
public double Å
{
get { return a1; }
set { a1 = value; }
}
public double Å
{
get { return a2; }
set { a2 = value; }
}
}
// Compilation produces a compiler warning like the following:
// Naming2a.cs(16,18): warning CS3005: Identifier 'Size.Å' differing only in case is not
// CLS-compliant
// Naming2a.cs(10,18): (Location of symbol related to previous warning)
// Naming2a.cs(18,8): warning CS3005: Identifier 'Size.Å.get' differing only in case is not
// CLS-compliant
// Naming2a.cs(12,8): (Location of symbol related to previous warning)
// Naming2a.cs(19,8): warning CS3005: Identifier 'Size.Å.set' differing only in case is not
// CLS-compliant
// Naming2a.cs(13,8): (Location of symbol related to previous warning)
<Assembly: CLSCompliant(True)>
Public Class Size
Private a1 As Double
Private a2 As Double
Los nombres de miembros con un ámbito determinado (como los espacios de nombres de un ensamblado, los
tipos de un espacio de nombres o los miembros de un tipo) deben ser únicos, excepto los nombres que se
resuelven a través de la sobrecarga. Este requisito es más estricto que el del sistema de tipos comunes, que
permite a varios miembros de un ámbito tener nombres idénticos siempre que sean diferentes tipos de
miembros (por ejemplo, que uno sea un método y otro, un campo). En particular, en el caso de los miembros de
tipo:
Los campos y los tipos anidados solo se distinguen por el nombre.
Los métodos, las propiedades y los eventos que tienen el mismo nombre deben distinguirse por algo más
que el tipo de valor devuelto.
En el ejemplo siguiente se muestra el requisito que establece que los nombres de miembro deben ser únicos
dentro de su ámbito. En este ejemplo se define una clase denominada Converter , que incluye cuatro miembros
denominados Conversion . Tres son métodos y uno es una propiedad. El método que incluye un parámetro Int64
recibe un nombre único, pero no ocurre lo mismo con los dos métodos que tienen un parámetro Int32, ya que el
valor devuelto no se considera parte de la signatura del miembro. La propiedad Conversion también infringe
este requisito, ya que las propiedades no pueden tener el mismo nombre que los métodos sobrecargados.
using System;
[assembly: CLSCompliant(true)]
Todos los lenguajes contienen palabras claves únicas, de modo que los lenguajes dirigidos a Common Language
Runtime también deben proporcionar un mecanismo para hacer referencia a identificadores (como nombres de
tipo) que coincidan con las palabras clave. Por ejemplo, case es una palabra clave en C# y Visual Basic. Sin
embargo, en el siguiente ejemplo de Visual Basic se elimina la ambigüedad entre una clase denominada case y
la palabra clave case mediante llaves de apertura y cierre. De lo contrario, el ejemplo produciría el mensaje de
error "Una palabra clave no es válida como identificador" y no se compilaría.
En el siguiente ejemplo de C# se crean instancias de la clase case utilizando el símbolo @ para eliminar la
ambigüedad entre el identificador y la palabra clave del lenguaje. Sin él, el compilador de C# mostraría dos
mensajes de error similares a los siguientes: "Se esperaba un tipo" y "'Término 'case' de expresión no válido".
using System;
Conversión de tipos
Common Language Specification define dos operadores de conversión:
op_Implicit , que se utiliza en las conversiones de ampliación que no dan lugar a la pérdida de datos o de
precisión. Por ejemplo, la estructura Decimal contiene un operador sobrecargado op_Implicit para
convertir valores de tipos enteros y valores Char en valores Decimal.
op_Explicit , que se utiliza en las conversiones de restricción que pueden producir una pérdida de
magnitud (un valor se convierte en un valor que tiene un intervalo menor) o de precisión. Por ejemplo, la
estructura Decimal contiene un operador sobrecargado op_Explicit para convertir los valores Double y
Single en Decimal y convertir Decimal en los valores integrales Double, Single y Char.
Sin embargo, no todos los lenguajes admiten la sobrecarga de operadores o la definición de operadores
personalizados. Si decide implementar estos operadores de conversión, debe proporcionar un mecanismo
alternativo para realizar la conversión. Se recomienda proporcionar los métodos From Xxx y To Xxx.
En el ejemplo siguiente se definen conversiones implícitas y explícitas conformes a CLS. En este ejemplo, se crea
una clase UDouble que representa un número de punto flotante con signo de precisión doble. En las
conversiones implícitas, pasa de UDouble a Double y, en las conversiones explícitas, de UDouble a Single, de
Double a UDouble y de Single a UDouble . También define un método ToDouble como alternativa al operador de
conversión implícita y los métodos ToSingle , FromDouble y FromSingle como alternativas a los operadores de
conversión explícitos.
using System;
number = value;
}
number = value;
}
Matrices
Las matrices conformes a CLS cumplen las reglas siguientes:
Todas las dimensiones de una matriz deben tener un límite inferior igual a cero. En el ejemplo siguiente se
crea una matriz no conforme a CLS cuyo límite inferior es uno. Observe que, a pesar de la presencia del
atributo CLSCompliantAttribute, el compilador no detecta que la matriz devuelta por el método
Numbers.GetTenPrimes no es conforme a CLS.
[assembly: CLSCompliant(true)]
return arr;
}
}
<Assembly: CLSCompliant(True)>
Return arr
End Function
End Class
Todos los elementos de la matriz deben componerse de tipos conformes a CLS. En el ejemplo siguiente se
definen dos métodos que devuelven matrices no conformes a CLS. El primero devuelve una matriz de
valores UInt32. El segundo devuelve una matriz Object que contiene los valores Int32 y UInt32. Aunque el
compilador identifica la primera matriz como no conforme debido a su tipo UInt32, no reconoce que la
segunda matriz incluye elementos no conformes a CLS.
using System;
[assembly: CLSCompliant(true)]
<Assembly: CLSCompliant(True)>
La resolución de desbordamiento de los métodos que tienen parámetros de matriz se basa en el hecho de
que son matrices y en su tipo de elemento. Por esta razón, la siguiente definición de un método
GetSquares sobrecargado es conforme a CLS.
using System;
using System.Numerics;
[assembly: CLSCompliant(true)]
return numbersOut;
}
}
Imports System.Numerics
<Assembly: CLSCompliant(True)>
Interfaces
Las interfaces conformes a CLS pueden definir propiedades, eventos y métodos virtuales (métodos sin
implementación). Una interfaz conforme a CLS no puede tener ninguno de los elementos siguientes:
Métodos estáticos o campos estáticos. Los compiladores de C# y Visual Basic generan errores de
compilación si se define un miembro estático en una interfaz.
Campos. Los compiladores de C# y Visual Basic generan errores de compilación si se define un campo en
una interfaz.
Métodos que no son conformes a CLS. Por ejemplo, la siguiente definición de interfaz incluye un método,
INumber.GetUnsigned , que está marcado como no conforme a CLS. Este ejemplo genera una advertencia
del compilador.
using System;
[assembly:CLSCompliant(true)]
<Assembly: CLSCompliant(True)>
Debido a esta regla, no se necesitan tipos conformes a CLS para implementar miembros no conformes a
CLS. Si un marco conforme a CLS expone una clase que implementa una interfaz no conforme a CLS,
también debe proporcionar implementaciones concretas de todos los miembros no conformes a CLS.
Los compiladores de lenguaje conformes a CLS también deben permitir que una clase proporcione
implementaciones independientes de miembros con el mismo nombre y la misma signatura en varias interfaces.
C# y Visual Basic admiten implementaciones de interfaz explícitas para proporcionar diferentes
implementaciones de métodos con el mismo nombre. Visual Basic también admite la palabra clave Implements ,
que permite designar explícitamente qué interfaz y miembro implementa un determinado miembro. En el
ejemplo siguiente se muestra este escenario con la definición de una clase Temperature que implementa las
interfaces ICelsius y IFahrenheit como implementaciones de interfaces explícitas.
using System;
[assembly: CLSCompliant(true)]
decimal IFahrenheit.GetTemperature()
{
return _value * 9 / 5 + 32;
}
decimal ICelsius.GetTemperature()
{
return _value;
}
}
public class Example
{
public static void Main()
{
Temperature temp = new Temperature(100.0m);
ICelsius cTemp = temp;
IFahrenheit fTemp = temp;
Console.WriteLine("Temperature in Celsius: {0} degrees",
cTemp.GetTemperature());
Console.WriteLine("Temperature in Fahrenheit: {0} degrees",
fTemp.GetTemperature());
}
}
// The example displays the following output:
// Temperature in Celsius: 100.0 degrees
// Temperature in Fahrenheit: 212.0 degrees
<Assembly: CLSCompliant(True)>
Module Example
Public Sub Main()
Dim temp As New Temperature(100.0d)
Console.WriteLine("Temperature in Celsius: {0} degrees",
temp.GetCelsius())
Console.WriteLine("Temperature in Fahrenheit: {0} degrees",
temp.GetFahrenheit())
End Sub
End Module
' The example displays the following output:
' Temperature in Celsius: 100.0 degrees
' Temperature in Fahrenheit: 212.0 degrees
Enumeraciones
Las enumeraciones conformes a CLS deben seguir estas reglas:
El tipo subyacente de una enumeración debe ser un entero intrínseco conforme a CLS (Byte, Int16, Int32 o
Int64). Por ejemplo, el código siguiente intenta definir una enumeración cuyo tipo subyacente es UInt32 y
genera una advertencia del compilador.
using System;
[assembly: CLSCompliant(true)]
<Assembly: CLSCompliant(True)>
Un tipo de enumeración debe tener un campo de instancia único denominado Value__ marcado con el
atributo FieldAttributes.RTSpecialName. Esto permite hacer referencia al valor del campo de forma
implícita.
Las enumeraciones incluyen campos estáticos literales del mismo tipo que el de la enumeración. Por
ejemplo, si define una enumeración State con los valores State.On y State.Off , State.On y State.Off
son campos estáticos literales cuyo tipo será State .
Hay dos tipos de enumeraciones:
Las enumeraciones que representan un conjunto de valores enteros con nombre mutuamente
excluyentes. Este tipo de enumeración se indica por la ausencia del atributo personalizado
System.FlagsAttribute.
Las enumeraciones que representan un conjunto de marcadores de bits que se pueden combinar
para generar un valor sin nombre. Este tipo de enumeración se indica por la presencia del atributo
personalizado System.FlagsAttribute.
Para obtener más información, consulte la documentación de la estructura Enum.
El valor de una enumeración no se limita al intervalo de sus valores especificados. Es decir, el intervalo de
valores de una enumeración es el intervalo de su valor subyacente. Puede utilizar el método
Enum.IsDefined para determinar si un valor especificado es miembro de una enumeración.
Miembros de tipos en general
Common Language Specification necesita todos los campos y métodos accesibles como miembros de una clase
determinada. Por tanto, los métodos y los campos estáticos globales (es decir, los métodos o los campos que se
definen con independencia de un tipo) no son conformes a CLS. Si intenta incluir un campo o un método global
en el código fuente, tanto el compilador de C# como el de Visual Basic generarán un error de compilación.
Common Language Specification solo admite la convención de llamada administrada estándar. No admite
convenciones de llamada ni métodos no administrados con listas de argumentos variables marcados con la
palabra clave varargs . En el caso de las listas de argumentos variables que son compatibles con la convención
de llamada administrada estándar, utilice el atributo ParamArrayAttribute o la implementación del lenguaje
específico, como la palabra clave params en C# y la palabra clave ParamArray en Visual Basic.
Accesibilidad de miembros
Al reemplazar un miembro heredado no se puede cambiar la accesibilidad de dicho miembro. Por ejemplo, un
método público de una clase base no se puede reemplazar por un método privado de una clase derivada. Existe
una excepción: un miembro protected internal (en C#) o Protected Friend (en Visual Basic) de un ensamblado
que se haya reemplazado por un tipo de un ensamblado diferente. En ese caso, la accesibilidad del reemplazo es
Protected .
En el ejemplo siguiente se muestra el error que se genera cuando el atributo CLSCompliantAttribute se establece
en true y Human , que es una clase derivada de Animal , intenta cambiar la accesibilidad de la propiedad
Species de pública a privada. El ejemplo se compila correctamente si su accesibilidad se cambia a pública.
using System;
[assembly: CLSCompliant(true)]
Los tipos de la signatura de un miembro deben estar accesibles siempre que dicho miembro esté accesible. Esto
significa, por ejemplo, que un miembro público no puede incluir un parámetro cuyo tipo sea privado, protegido o
interno. En el ejemplo siguiente se muestra el error del compilador que se produce cuando un constructor de
clase StringWrapper expone un valor de enumeración StringOperationType interno que determina cómo debe
ajustarse un valor de cadena.
using System;
using System.Text;
Imports System.Text
<Assembly: CLSCompliant(True)>
using System;
[assembly:CLSCompliant(true)]
Los nombres de tipos genéricos se codifican con el formato nombre`n, donde nombre es el nombre del tipo, ` es
un carácter literal y n es el número de parámetros declarados en el tipo o, en el caso de tipos genéricos anidados,
el número de parámetros de tipo recién incorporados. Esta codificación de nombres de tipo genérico tiene
interés fundamentalmente para los desarrolladores que utilizan la reflexión a fin de acceder a los tipos genéricos
conformes a CLS de una biblioteca.
Si las restricciones se aplican a un tipo genérico, los tipos utilizados como restricciones también deben ser
conformes a CLS. En el ejemplo siguiente se define una clase denominada BaseClass que no es conforme a CLS
y una clase genérica denominada BaseCollection cuyo parámetro de tipo debe derivarse de BaseClass . Sin
embargo, puesto que BaseClass no es conforme a CLS, el compilador emite una advertencia.
using System;
[assembly:CLSCompliant(true)]
<Assembly: CLSCompliant(True)>
Si un tipo genérico se deriva de un tipo base genérico, es necesario volver a declarar las restricciones para que se
pueda garantizar que las restricciones del tipo base también se cumplen. En el ejemplo siguiente se define un
objeto Number<T> que puede representar cualquier tipo numérico. También se define una clase FloatingPoint<T>
que representa un valor de punto flotante. Sin embargo, el código fuente no puede compilarse, ya que no aplica
la restricción de Number<T> (T debe ser un tipo de valor) en FloatingPoint<T> .
using System;
[assembly:CLSCompliant(true)]
[assembly:CLSCompliant(true)]
Common Language Specification impone un modelo conservador adaptado a cada instancia en los tipos
anidados y los miembros protegidos. Los tipos genéricos abiertos no pueden exponer campos ni miembros con
signaturas que contengan una instancia específica de un tipo genérico anidado y protegido. Los tipos no
genéricos que amplíen una instancia específica de una interfaz o clase base genérica no pueden exponer campos
ni miembros con signaturas que contengan otra instancia de un tipo genérico anidado y protegido.
En el ejemplo siguiente se define un tipo genérico, C1<T> (o C1(Of T) en Visual Basic) y una clase protegida,
C1<T>.N (o C1(Of T).N en Visual Basic). C1<T> tiene dos métodos: M1 y M2 . Sin embargo, M1 no es conforme
a CLS porque intenta devolver un objeto C1<int>.N (o C1(Of Integer).N ) a partir de C1<T> (o C1(Of T) ). Una
segunda clase, C2 , se deriva de C1<long> (o C1(Of Long) ). Esta clase tiene dos métodos, M3 y M4 . El objeto
M3 no es conforme a CLS porque intenta devolver un objeto C1<int>.N (o C1(Of Integer).N ) a partir de una
subclase de C1<long> . Observe que los compiladores del lenguaje pueden ser aun más restrictivos. En este
ejemplo, Visual Basic muestra un error cuando intenta compilar M4 .
using System;
[assembly:CLSCompliant(true)]
Protected Sub M1(n As C1(Of Integer).N) ' Not CLS-compliant - C1<int>.N not
' accessible from within C1(Of T) in all
End Sub ' languages
Protected Sub M2(n As C1(Of T).N) ' CLS-compliant – C1(Of T).N accessible
End Sub ' inside C1(Of T)
End Class
Constructores
Los constructores de clases y estructuras conformes a CLS deben seguir estas reglas:
Un constructor de una clase derivada debe llamar al constructor de instancia de su clase base antes de
tener acceso a datos de instancia heredados. Este requisito se debe al hecho de que los constructores de
clase base no se heredan por sus clases derivadas. Esta regla no se aplica a las estructuras, que no admiten
la herencia directa.
Normalmente, los compiladores aplican esta regla independientemente de la conformidad con CLS, como
se muestra en el ejemplo siguiente. En este ejemplo, se crea una clase Doctor que se deriva de una clase
Person , pero la clase Doctor no consigue llamar al constructor de la clase Person para inicializar los
campos de instancia heredados.
using System;
[assembly: CLSCompliant(true)]
fName = firstName;
lName = lastName;
_id = id;
}
public string Id
{
get { return _id; }
}
fName = firstName
lName = lastName
_id = id
End Sub
No se puede llamar a un constructor de objetos excepto para crear un objeto. Además, un objeto no se
puede inicializar dos veces. Esto significa, por ejemplo, que el método Object.MemberwiseClone y los
métodos de deserialización, como BinaryFormatter.Deserialize, no deben llamar a constructores.
Propiedades
Las propiedades de los tipos conformes a CLS deben seguir estas reglas:
Una propiedad debe tener un establecedor, un captador o ambos. En un ensamblado, estos elementos se
implementan como métodos especiales, lo que significa que aparecerán como métodos independientes (el
captador se llama get_ propertyname y el establecedor es set_ propertyname) marcados como
SpecialName en los metadatos del ensamblado. Los compiladores de C# y Visual Basic aplican esta regla
automáticamente sin necesidad de aplicar el atributo CLSCompliantAttribute.
El tipo de una propiedad es el tipo de valor devuelto del captador de la propiedad y el último argumento
del establecedor. Estos tipos deben ser conformes a CLS y los argumentos no se pueden asignar a la
propiedad por referencia (es decir, no pueden ser punteros administrados).
Si una propiedad tiene un captador y un establecedor, estos deben ser virtuales, estáticos o de instancia.
Los compiladores de C# y Visual Basic aplican automáticamente esta regla a través de la sintaxis de
definición de la propiedad.
Events
Un evento se define por su nombre y su tipo. El tipo de evento es un delegado que se utiliza para indicar el
evento. Por ejemplo, el evento AppDomain.AssemblyResolve es del tipo ResolveEventHandler. Además del propio
evento, hay tres métodos con nombres basados en el nombre de evento que proporcionan la implementación
del evento y que se marcan como SpecialName en los metadatos de ensamblado:
Un método para agregar un controlador de eventos, llamado add_ EventName. Por ejemplo, el método de
suscripción de eventos del evento AppDomain.AssemblyResolve se denomina add_AssemblyResolve .
Un método para quitar un controlador de eventos, llamado remove_ EventName. Por ejemplo, el método
de eliminación del evento AppDomain.AssemblyResolve se denomina remove_AssemblyResolve .
Un método para indicar que el evento se produjo, llamado raise_ EventName.
NOTE
La mayoría de las reglas de Common Language Specification relacionadas con los eventos se implementan mediante
compiladores del lenguaje y son transparentes para los desarrolladores de componentes.
Los métodos para agregar, quitar y generar el evento deben tener la misma accesibilidad. Además, todos deben
ser estáticos, virtuales o de instancia. Los métodos para agregar y quitar un evento tienen un parámetro cuyo
tipo es el mismo que el del delegado de eventos. Los métodos para agregar y quitar deben estar presentes o
ausentes al mismo tiempo.
En el ejemplo siguiente se define una clase conforme a CLS denominada Temperature que genera un evento
TemperatureChanged si el cambio de temperatura entre dos lecturas es igual o mayor que el valor de umbral. La
clase Temperature define de manera explícita un método raise_TemperatureChanged para que pueda ejecutar
controladores de eventos de forma selectiva.
using System;
using System.Collections;
using System.Collections.Generic;
[assembly: CLSCompliant(true)]
public Example()
{
temp = new Temperature(65, 3);
temp.TemperatureChanged += this.TemperatureNotification;
RecordTemperatures();
Example ex = new Example(temp);
ex.RecordTemperatures();
}
public Example(Temperature t)
{
temp = t;
RecordTemperatures();
}
Imports System.Collections
Imports System.Collections.Generic
<Assembly: CLSCompliant(True)>
End Sub
Overloads
Common Language Specification impone los siguientes requisitos a los miembros sobrecargados:
Los miembros se pueden sobrecargar según el número de parámetros y el tipo de cualquiera de los
parámetros. A la hora de distinguir entre sobrecargas, no se tienen en cuenta los factores de convención
de llamada, el tipo de valor devuelto, los modificadores personalizados aplicados al método o a su
parámetro, ni si los parámetros se pasan por valor o por referencia. Para consultar un ejemplo, vea el
código del requisito que establece que los nombres deben ser únicos en cada ámbito que se incluye en la
sección Convenciones de nomenclatura.
Solo las propiedades y los métodos se pueden sobrecargar. Los campos y eventos no se pueden
sobrecargar.
Los métodos genéricos pueden sobrecargarse en función del número de parámetros genéricos.
NOTE
Los operadores op_Explicit y op_Implicit son una excepción de la regla que establece que el valor devuelto no se
considera parte de la signatura de un método en la resolución de la sobrecarga. Estos dos operadores se pueden
sobrecargar según sus parámetros y su valor devuelto.
Excepciones
Los objetos de excepción deben derivar de System.Exception o de otro tipo derivado de System.Exception. En el
ejemplo siguiente se muestra el error de compilador que se produce cuando una clase personalizada
denominada ErrorClass se utiliza para el control de excepciones.
using System;
[assembly: CLSCompliant(true)]
<Assembly: CLSCompliant(True)>
Para corregir este error, la clase ErrorClass debe heredar de System.Exception. Además, la propiedad Message
debe invalidarse. En el ejemplo siguiente se corrigen estos errores para definir una clase ErrorClass que sea
conforme a CLS.
using System;
[assembly: CLSCompliant(true)]
Imports System.Runtime.CompilerServices
<Assembly: CLSCompliant(True)>
Atributos
En los ensamblados de .NET Framework, los atributos personalizados proporcionan un mecanismo extensible
para almacenar atributos personalizados y recuperar metadatos sobre objetos de programación, tales como
ensamblados, tipos, miembros y parámetros de método. Los atributos personalizados deben derivar de
System.Attribute o de un tipo derivado de System.Attribute.
En el ejemplo siguiente se infringe esta regla. Define una clase NumericAttribute que no se deriva de
System.Attribute. Tenga en cuenta que un error del compilador solo se produce cuando se aplica el atributo no
conforme a CLS, y no cuando se define la clase.
using System;
[assembly: CLSCompliant(true)]
[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Struct)]
public class NumericAttribute
{
private bool _isNumeric;
<Assembly: CLSCompliant(True)>
<AttributeUsageAttribute(AttributeTargets.Class Or AttributeTargets.Struct)> _
Public Class NumericAttribute
Private _isNumeric As Boolean
using System;
[assembly:CLSCompliantAttribute(true)]
[AttributeUsage(AttributeTargets.All)]
public class DescriptionAttribute : Attribute
{
private Descriptor desc;
public DescriptionAttribute(Descriptor d)
{
desc = d;
}
<AttributeUsage(AttributeTargets.All)> _
Public Class DescriptionAttribute : Inherits Attribute
Private desc As Descriptor
CLSCompliantAttribute (Atributo)
El atributo CLSCompliantAttribute se usa para indicar si un elemento del programa es conforme a Common
Language Specification. El constructor CLSCompliantAttribute(Boolean) contiene un único parámetro obligatorio,
isCompliant , que indica si el elemento del programa es conforme a CLS.
En tiempo de compilación, el compilador detecta los elementos que supuestamente deberían ser conformes a
CLS y emite una advertencia. El compilador no emite advertencias relacionadas con los tipos o miembros cuya
no conformidad se declara explícitamente.
Los desarrolladores de componentes pueden usar el atributo CLSCompliantAttribute de dos maneras:
Para definir las partes de la interfaz pública expuestas por un componente que son conformes a CLS y las
que no lo son. Cuando el atributo se utiliza para marcar determinados elementos del programa como
conformes a CLS, este uso garantiza que dichos elementos son accesibles desde todos los lenguajes y
herramientas que tienen como destino .NET Framework.
Para garantizar que la interfaz pública de la biblioteca de componentes expone solo elementos del
programa que son conformes a CLS. Si los elementos no son conformes a CLS, los compiladores
normalmente emitirán una advertencia.
WARNING
En algunos casos, los compiladores de lenguaje aplican reglas conformes a CLS independientemente de si se usa el atributo
CLSCompliantAttribute o no. Por ejemplo, la definición de un miembro estático en una interfaz infringe una regla de CLS.
En este sentido, si se define un miembro static (en C#) o Shared (en Visual Basic) en una interfaz, los compiladores de
C# y Visual Basic mostrarán un mensaje de error y no podrán compilar la aplicación.
El atributo CLSCompliantAttribute está marcado con un atributo AttributeUsageAttribute que tiene el valor
AttributeTargets.All. Este valor le permite aplicar el atributo CLSCompliantAttribute a cualquier elemento de
programa, incluidos los ensamblados, módulos, tipos (clases, estructuras, enumeraciones, interfaces, delegados),
miembros de tipo (constructores, métodos, propiedades, campos y eventos), parámetros, parámetros genéricos y
valores devueltos. Sin embargo, en la práctica, solo debe aplicar el atributo a los ensamblados, tipos y miembros
del tipo. De lo contrario, los compiladores omitirán el atributo y seguirán generando advertencias de compilación
siempre que encuentren un parámetro no conforme, un parámetro genérico o un valor devuelto en la interfaz
pública de la biblioteca.
El valor del atributo CLSCompliantAttribute lo heredan los elementos del programa contenidos. Por ejemplo, si se
marca un ensamblado como conforme a CLS, sus tipos también son conformes a CLS. Si un tipo se marca como
conforme a CLS, sus tipos anidados y miembros también serán conformes a CLS.
Puede invalidar explícitamente la conformidad heredada aplicando el atributo CLSCompliantAttribute a un
elemento del programa contenido. Por ejemplo, puede utilizar el atributo CLSCompliantAttribute con el valor
isCompliant establecido en false para definir un tipo no conforme en un ensamblado conforme, y puede
utilizar el atributo con el valor isCompliant establecido en true para definir un tipo conforme en un
ensamblado no conforme. También puede definir miembros no conformes en un tipo conforme. Sin embargo, un
tipo no conforme no puede tener miembros conformes, por lo que no puede utilizar el atributo con el valor
isCompliant establecido en true para invalidar la herencia de un tipo no conforme.
Cuando desarrolle componentes, debe utilizar siempre el atributo CLSCompliantAttribute para indicar si el
ensamblado, sus tipos y sus miembros son conformes a CLS.
Para crear componentes conformes a CLS:
1. Utilice CLSCompliantAttribute para marcar el ensamblado como conforme a CLS.
2. Marque como no conforme los tipos expuestos públicamente en el ensamblado que no sean conformes a
CLS.
3. Marque como no conforme los miembros expuestos públicamente en tipos conformes a CLS.
4. Proporcione una alternativa conforme a CLS para los miembros que no sean conformes a CLS.
Si ha marcado correctamente todos los tipos y miembros no conformes, el compilador no debe emitir ninguna
advertencia de no conformidad. Sin embargo, debe indicar qué miembros no son conformes a CLS y mostrar las
alternativas conformes a CLS en la documentación del producto.
En el ejemplo siguiente se usa el atributo CLSCompliantAttribute para definir un ensamblado conforme a CLS y
un tipo, CharacterUtilities , que tiene dos miembros no conformes a CLS. Dado que ambos miembros están
etiquetados con el atributo CLSCompliant(false) , el compilador no genera ninguna advertencia. La clase también
proporciona una alternativa conforme a CLS para ambos métodos. Por lo general, simplemente se agregarían
dos sobrecargas al método ToUTF16 para proporcionar alternativas conformes a CLS. Sin embargo, puesto que
no se pueden sobrecargar los métodos en función de un valor devuelto, los nombres de los métodos conformes
a CLS son distintos de los nombres de los métodos no conformes.
using System;
using System.Text;
[assembly:CLSCompliant(true)]
if (chars.Length == 2) {
if (! Char.IsSurrogatePair(chars[0], chars[1]))
throw new ArgumentException("The array must contain a low and a high surrogate.");
else
return Char.ConvertToUtf32(chars[0], chars[1]);
}
else {
return Char.ConvertToUtf32(chars.ToString(), 0);
}
}
}
Imports System.Text
<Assembly: CLSCompliant(True)>
Si está desarrollando una aplicación en lugar de una biblioteca (es decir, si no expone los tipos o miembros que
pueden consumir otros desarrolladores de aplicaciones), la conformidad con CLS de los elementos del programa
usados por la aplicación solo tendrá interés si su lenguaje admite estos elementos. En ese caso, el compilador del
lenguaje generará un error cuando intente utilizar un elemento no conforme a CLS.
Imports System.Collections.Generic
Imports System.Runtime.CompilerServices
Sub New()
Dim words() As String = {"a", "an", "and", "of", "the"}
exclusions = New List(Of String)
exclusions.AddRange(words)
End Sub
<Extension()> _
Public Function ToTitleCase(title As String) As String
Dim words() As String = title.Split()
Dim result As String = String.Empty
A continuación se muestra el código fuente para NumberUtil.cs, que define una clase NumericLib con dos
miembros, IsEven y NearZero .
using System;
Para empaquetar las dos clases en un solo ensamblado, debe compilarlas en módulos. Para compilar el archivo
de código fuente de Visual Basic en un módulo, use este comando:
Para más información sobre la sintaxis de la línea de comandos del compilador de Visual Basic, consulte
Compilación desde la línea de comandos.
Para compilar el archivo de código fuente de C# en un módulo, use este comando:
Para más información sobre la sintaxis de la línea de comandos del compilador de C#, consulte Compilación de la
línea de comandos con csc.exe.
Utilice luego las opciones del enlazador para compilar los dos módulos en un ensamblado:
El ejemplo siguiente llama a los métodos NumericLib.NearZero y StringLib.ToTitleCase . Observe que el código
de Visual Basic y el código de C# pueden tener acceso a los métodos en ambas clases.
using System;
Module Example
Public Sub Main()
Dim dbl As Double = 0.0 - Double.Epsilon
Console.WriteLine(NumericLib.NearZero(dbl))
Para compilar con C#, cambie el nombre del compilador de vbc a csc y cambie la extensión de archivo .vb a .cs:
Vea también
CLSCompliantAttribute
Conversión de tipos en .NET Framework
16/09/2020 • 43 minutes to read • Edit Online
Cada valor tiene un tipo asociado, que define atributos como la cantidad de espacio asignado al valor, el intervalo
de valores posibles que puede tener y los miembros que ofrece. Muchos valores se pueden expresar como más de
un tipo. Por ejemplo, el valor 4 se puede expresar como un entero o como un valor de punto flotante. La
conversión de tipo crea un valor en un nuevo tipo que es equivalente al valor de un tipo antiguo, pero no conserva
necesariamente la identidad (o valor exacto) del objeto original.
.NET Framework admite automáticamente las conversiones siguientes:
Conversión de una clase derivada a una clase base. Esto significa, por ejemplo, que una instancia de
cualquier clase o estructura se puede convertir en una instancia de tipo Object. Esta conversión no requiere
un operador de conversión.
Conversión de una clase base a la clase derivada original. En C#, esta conversión requiere un operador de
conversión. En Visual Basic, requiere el operador CType si Option Strict está activado.
Conversión de un tipo que implementa una interfaz a un objeto de interfaz que representa esa interfaz. Esta
conversión no requiere un operador de conversión.
Conversión de un objeto de interfaz al tipo original que implementa esa interfaz. En C#, esta conversión
requiere un operador de conversión. En Visual Basic, requiere el operador CType si Option Strict está
activado.
Además de estas conversiones automáticas, .NET Framework proporciona varias características que admiten la
conversión de tipos personalizada. Entre ellas se incluyen las siguientes:
El operador Implicit , que define las conversiones de ampliación disponibles entre los tipos. Para obtener
más información, consulte la sección Conversión implícita con el operador Implicit.
El operador Explicit , que define las conversiones de restricción disponibles entre los tipos. Para obtener
más información, consulte la sección Conversión explícita con el operador Explicit.
La interfaz IConvertible, que define las conversiones en cada uno de los tipos de datos base de .NET
Framework. Para obtener más información, vea Interfaz IConvertible.
La clase Convert, que proporciona un conjunto de métodos que implementan los métodos de la interfaz
IConvertible. Para obtener más información, vea la sección Clase Convert.
La clase TypeConverter, que es una clase base que se puede extender para admitir la conversión de un tipo
concreto en cualquier otro tipo. Para obtener más información, vea Clase TypeConverter.
Por ejemplo, el tipo Decimal admite conversiones implícitas a partir de valores Byte, Char, Int16, Int32, Int64,
SByte, UInt16, UInt32 y UInt64. En el ejemplo siguiente se muestran algunas de estas conversiones implícitas al
asignar valores a una variable Decimal.
decimal decimalValue;
decimalValue = byteValue;
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
byteValue.GetType().Name, decimalValue);
decimalValue = shortValue;
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
shortValue.GetType().Name, decimalValue);
decimalValue = intValue;
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
intValue.GetType().Name, decimalValue);
decimalValue = longValue;
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
longValue.GetType().Name, decimalValue);
decimalValue = ulongValue;
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
longValue.GetType().Name, decimalValue);
// The example displays the following output:
// After assigning a Byte value, the Decimal value is 16.
// After assigning a Int16 value, the Decimal value is -1024.
// After assigning a Int32 value, the Decimal value is -1034000.
// After assigning a Int64 value, the Decimal value is 1152921504606846976.
// After assigning a Int64 value, the Decimal value is 18446744073709551615.
Dim byteValue As Byte = 16
Dim shortValue As Short = -1024
Dim intValue As Integer = -1034000
Dim longValue As Long = CLng(1024 ^ 6)
Dim ulongValue As ULong = ULong.MaxValue
decimalValue = byteValue
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
byteValue.GetType().Name, decimalValue)
decimalValue = shortValue
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
shortValue.GetType().Name, decimalValue)
decimalValue = intValue
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
intValue.GetType().Name, decimalValue)
decimalValue = longValue
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
longValue.GetType().Name, decimalValue)
decimalValue = ulongValue
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
longValue.GetType().Name, decimalValue)
' The example displays the following output:
' After assigning a Byte value, the Decimal value is 16.
' After assigning a Int16 value, the Decimal value is -1024.
' After assigning a Int32 value, the Decimal value is -1034000.
' After assigning a Int64 value, the Decimal value is 1152921504606846976.
' After assigning a Int64 value, the Decimal value is 18446744073709551615.
A continuación, el código de cliente puede declarar una variable ByteWithSign y asignarle valores Byte y SByte sin
realizar ninguna conversión explícita ni emplear ningún operador de conversión, como se muestra en el ejemplo
siguiente.
NOTE
La finalidad principal de solicitar un método de conversión o un operador de conversión para las conversiones de restricción
es que el desarrollador sea consciente de la posibilidad de que se pierdan datos o de que se produzca una excepción
OverflowException, para que se pueda administrar en el código. Sin embargo, algunos compiladores pueden ser menos
exigentes con este requisito. Por ejemplo, en Visual Basic, si Option Strict está desactivado (la configuración
predeterminada), el compilador de Visual Basic intenta realizar las conversiones de restricción implícitamente.
Por ejemplo, los tipos de datos UInt32, Int64 y UInt64 tienen intervalos que superan el del tipo de datos Int32,
como se muestra en la tabla siguiente.
T IP O C O M PA RA C IÓ N C O N EL IN T ERVA LO DE IN T 32
Para administrar estas conversiones de restricción, .NET Framework permite que los tipos definan un operador
Explicit . A continuación, los compiladores de lenguaje individuales pueden implementar este operador usando
su propia sintaxis o se puede llamar a un miembro de la clase Convert para realizar la conversión. (Para obtener
más información sobre la clase Convert, vea Clase Convert más adelante en este tema). En el ejemplo siguiente se
muestra el uso de las características de lenguaje para administrar la conversión explícita de estos valores enteros,
que potencialmente están fuera del intervalo, a valores Int32.
long number1 = int.MaxValue + 20L;
uint number2 = int.MaxValue - 1000;
ulong number3 = int.MaxValue;
int intNumber;
try {
intNumber = checked((int) number1);
Console.WriteLine("After assigning a {0} value, the Integer value is {1}.",
number1.GetType().Name, intNumber);
}
catch (OverflowException) {
if (number1 > int.MaxValue)
Console.WriteLine("Conversion failed: {0} exceeds {1}.",
number1, int.MaxValue);
else
Console.WriteLine("Conversion failed: {0} is less than {1}.",
number1, int.MinValue);
}
try {
intNumber = checked((int) number2);
Console.WriteLine("After assigning a {0} value, the Integer value is {1}.",
number2.GetType().Name, intNumber);
}
catch (OverflowException) {
Console.WriteLine("Conversion failed: {0} exceeds {1}.",
number2, int.MaxValue);
}
try {
intNumber = checked((int) number3);
Console.WriteLine("After assigning a {0} value, the Integer value is {1}.",
number3.GetType().Name, intNumber);
}
catch (OverflowException) {
Console.WriteLine("Conversion failed: {0} exceeds {1}.",
number1, int.MaxValue);
}
Try
intNumber = CInt(number1)
Console.WriteLine("After assigning a {0} value, the Integer value is {1}.",
number1.GetType().Name, intNumber)
Catch e As OverflowException
If number1 > Integer.MaxValue Then
Console.WriteLine("Conversion failed: {0} exceeds {1}.",
number1, Integer.MaxValue)
Else
Console.WriteLine("Conversion failed: {0} is less than {1}.\n",
number1, Integer.MinValue)
End If
End Try
Try
intNumber = CInt(number2)
Console.WriteLine("After assigning a {0} value, the Integer value is {1}.",
number2.GetType().Name, intNumber)
Catch e As OverflowException
Console.WriteLine("Conversion failed: {0} exceeds {1}.",
number2, Integer.MaxValue)
End Try
Try
intNumber = CInt(number3)
Console.WriteLine("After assigning a {0} value, the Integer value is {1}.",
number3.GetType().Name, intNumber)
Catch e As OverflowException
Console.WriteLine("Conversion failed: {0} exceeds {1}.",
number1, Integer.MaxValue)
End Try
' The example displays the following output:
' Conversion failed: 2147483667 exceeds 2147483647.
' After assigning a UInt32 value, the Integer value is 2147482647.
' After assigning a UInt64 value, the Integer value is 2147483647.
Las conversiones explícitas pueden producir resultados diferentes en los distintos lenguajes y estos resultados
pueden diferir del valor devuelto por el método Convert correspondiente. Por ejemplo, si el valor Double
12.63251 se convierte en Int32, el método CInt de Visual Basic y el método Convert.ToInt32(Double) de .NET
Framework redondean el valor Double para devolver un valor de 13, pero el operador (int) de C# trunca Double
para devolver un valor de 12. De manera similar, el operador (int) de C# no admite la conversión de booleano a
entero, pero el método CInt de Visual Basic convierte un valor de true en -1. Por otra parte, el método
Convert.ToInt32(Boolean) convierte un valor de true en 1.
La mayoría de los compiladores permiten realizar conversiones explícitas de manera comprobada o no
comprobada. Cuando se realiza una conversión comprobada, se produce una excepción OverflowException si el
valor del tipo que se va a convertir está fuera del intervalo del tipo de destino. Si se realiza una conversión no
comprobada en las mismas condiciones, es posible que la conversión no produzca una excepción, pero no se
podrá determinar con exactitud cuál será el comportamiento y se puede obtener como resultado un valor
incorrecto.
NOTE
En C#, las conversiones comprobadas se pueden realizar usando la palabra clave checked junto con un operador de
conversión o especificando la opción del compilador /checked+ . Por el contrario, las conversiones no comprobadas se
pueden realizar utilizando la palabra clave unchecked junto con el operador de conversión de tipo o especificando la opción
del compilador /checked- . De forma predeterminada, las conversiones explícitas no se comprueban. En Visual Basic, para
realizar conversiones comprobadas, se puede desactivar la casilla Quitar comprobaciones de desbordamiento con
enteros en el cuadro de diálogo Configuración de compilador avanzada del proyecto o especificando la opción de
compilador /removeintchecks- . Por el contrario, para realizar conversiones no comprobadas, se puede activar la casilla
Quitar comprobaciones de desbordamiento con enteros en el cuadro de diálogo Configuración de compilador
avanzada del proyecto o especificando la opción de compilador /removeintchecks+ . De forma predeterminada, las
conversiones explícitas se comprueban.
En el siguiente ejemplo de C# se usan las palabras clave checked y unchecked para mostrar la diferencia de
comportamiento cuando un valor que está fuera del intervalo de Byte se convierte en Byte. La conversión
comprobada produce una excepción, pero la conversión no comprobada asigna Byte.MaxValue a la variable Byte.
try {
newValue = unchecked((byte) largeValue);
Console.WriteLine("Converted the {0} value {1} to the {2} value {3}.",
largeValue.GetType().Name, largeValue,
newValue.GetType().Name, newValue);
}
catch (OverflowException) {
Console.WriteLine("{0} is outside the range of the Byte data type.",
largeValue);
}
try {
newValue = checked((byte) largeValue);
Console.WriteLine("Converted the {0} value {1} to the {2} value {3}.",
largeValue.GetType().Name, largeValue,
newValue.GetType().Name, newValue);
}
catch (OverflowException) {
Console.WriteLine("{0} is outside the range of the Byte data type.",
largeValue);
}
// The example displays the following output:
// Converted the Int32 value 2147483647 to the Byte value 255.
// 2147483647 is outside the range of the Byte data type.
ByteWithSign newValue;
newValue.signValue = (SByte) Math.Sign(value);
newValue.value = (byte) Math.Abs(value);
return newValue;
}
ByteWithSign newValue;
newValue.signValue = 1;
newValue.value = (byte) value;
return newValue;
}
newValue.signValue = CSByte(Math.Sign(value))
newValue.value = CByte(Math.Abs(value))
Return newValue
End Operator
newValue.signValue = 1
newValue.value = CByte(value)
Return newValue
End Operator
A continuación, el código de cliente puede declarar una variable ByteWithSign y asignarle valores de tipo Int32 y
UInt32 si las asignaciones incluyen un operador de conversión o un método de conversión, como se muestra en el
ejemplo siguiente.
ByteWithSign value;
try {
int intValue = -120;
value = (ByteWithSign) intValue;
Console.WriteLine(value);
}
catch (OverflowException e) {
Console.WriteLine(e.Message);
}
try {
uint uintValue = 1024;
value = (ByteWithSign) uintValue;
Console.WriteLine(value);
}
catch (OverflowException e) {
Console.WriteLine(e.Message);
}
// The example displays the following output:
// -120
// '1024' is out of range of the ByteWithSign data type.
Try
Dim intValue As Integer = -120
value = CType(intValue, ByteWithSign)
Console.WriteLine(value)
Catch e As OverflowException
Console.WriteLine(e.Message)
End Try
Try
Dim uintValue As UInteger = 1024
value = CType(uintValue, ByteWithSign)
Console.WriteLine(value)
Catch e As OverflowException
Console.WriteLine(e.Message)
End Try
' The example displays the following output:
' -120
' '1024' is out of range of the ByteWithSign data type.
La interfaz IConvertible
Para admitir la conversión de cualquier tipo en un tipo base de Common Language Runtime, .NET Framework
proporciona la interfaz IConvertible. El tipo que se está implementando debe proporcionar lo siguiente:
Un método que devuelva el objeto TypeCode del tipo que se está implementando.
Métodos para convertir el tipo que se está implementando en cada uno de los tipos base de Common
Language Runtime (Boolean, Byte, DateTime, Decimal, Double, etc.).
Un método de conversión generalizado para convertir una instancia del tipo que se está implementando en
otro tipo concreto. Las conversiones que no se admiten deben iniciar una excepción InvalidCastException.
Cada uno de los tipos base de Common Language Runtime (es decir, Boolean, Byte, Char, DateTime, Decimal,
Double, Int16, Int32, Int64, SByte, Single, String, UInt16, UInt32y UInt64), así como los tipos DBNull y Enum,
implementan la interfaz IConvertible. Sin embargo, se trata de implementaciones de interfaz explícitas; solo se
puede llamar al método de conversión mediante una variable de la interfaz IConvertible, como se muestra en el
ejemplo siguiente. Este ejemplo convierte un valor Int32 en su valor Char equivalente.
El requisito de llamar al método de conversión en su interfaz, en lugar de en el tipo que se está implementando,
hace que las implementaciones de interfaz explícitas resulten relativamente costosas. En su lugar, se recomienda
llamar al miembro adecuado de la clase Convert para convertir entre los tipos base de Common Language
Runtime. Para obtener más información, consulte la próxima sección, Clase Convert.
NOTE
Además de la interfaz IConvertible y la clase Convert proporcionadas por .NET Framework, cada lenguaje puede
proporcionar también maneras de realizar conversiones. Por ejemplo, C# utiliza operadores de conversión, mientras que
Visual Basic utiliza funciones de conversión implementadas por el compilador como CType , CInt y DirectCast .
En su mayor parte, la interfaz IConvertible está diseñada para admitir la conversión entre los tipos base de .NET
Framework. Sin embargo, la interfaz también puede implementarse por un tipo personalizado con el fin de
admitir la conversión de ese tipo a otros tipos personalizados. Para obtener más información, consulte la sección
Conversiones personalizadas con el método ChangeType más adelante en este tema.
Clase Convert
Aunque se puede llamar a la implementación de la interfaz IConvertible de cada tipo base para realizar una
conversión de tipo, llamar a los métodos de la clase System.Convert es la manera recomendada de convertir de
un tipo base en otro de una manera independiente del lenguaje. Además, se puede usar el método
Convert.ChangeType(Object, Type, IFormatProvider) para convertir de un tipo personalizado concreto a otro tipo.
Conversiones entre los tipos base
La clase Convert proporciona una manera independiente del lenguaje de realizar conversiones entre los tipos base
y está disponible para todos los lenguajes cuyo destino es Common Language Runtime. Ofrece un conjunto
completo de métodos tanto para conversiones de ampliación como de restricción e inicia una excepción
InvalidCastException para las conversiones que no se admiten (como la conversión de un valor DateTime a un
valor entero). Las conversiones de restricción se realizan en un contexto comprobado y se inicia una excepción
OverflowException si se produce un error en la conversión.
IMPORTANT
Puesto que la clase Convert incluye métodos para efectuar la conversión directa e inversa de cada tipo base, elimina la
necesidad de llamar a la implementación de interfaz explícita IConvertible de cada tipo base.
En el ejemplo siguiente se muestra el uso de la clase System.Convert para realizar varias conversiones de
restricción y ampliación entre los tipos base de .NET Framework.
// Convert an Int32 value to a Decimal (a widening conversion).
int integralValue = 12534;
decimal decimalValue = Convert.ToDecimal(integralValue);
Console.WriteLine("Converted the {0} value {1} to " +
"the {2} value {3:N2}.",
integralValue.GetType().Name,
integralValue,
decimalValue.GetType().Name,
decimalValue);
// Convert a Byte value to an Int32 value (a widening conversion).
byte byteValue = Byte.MaxValue;
int integralValue2 = Convert.ToInt32(byteValue);
Console.WriteLine("Converted the {0} value {1} to " +
"the {2} value {3:G}.",
byteValue.GetType().Name,
byteValue,
integralValue2.GetType().Name,
integralValue2);
En algunos casos, en especial al efectuar conversiones directas e inversas de valores de punto flotante, una
conversión puede conllevar una pérdida de precisión, aunque no se inicie una excepción OverflowException. En el
ejemplo siguiente se muestra esta pérdida de precisión. En el primer caso, un valor Decimal tiene menos precisión
(menos dígitos significativos) cuando se convierte en el tipo Double. En el segundo caso, un valor Double se
redondea de 42,72 a 43 para completar la conversión.
double doubleValue;
doubleValue = 42.72;
try {
int integerValue = Convert.ToInt32(doubleValue);
Console.WriteLine("{0} converted to {1}.",
doubleValue, integerValue);
}
catch (OverflowException) {
Console.WriteLine("Unable to convert {0} to an integer.",
doubleValue);
}
// The example displays the following output:
// 13956810.96702888123451471211 converted to 13956810.9670289.
// 42.72 converted to 43.
doubleValue = 42.72
Try
Dim integerValue As Integer = Convert.ToInt32(doubleValue)
Console.WriteLine("{0} converted to {1}.",
doubleValue, integerValue)
Catch e As OverflowException
Console.WriteLine("Unable to convert {0} to an integer.",
doubleValue)
End Try
' The example displays the following output:
' 13956810.96702888123451471211 converted to 13956810.9670289.
' 42.72 converted to 43.
Para obtener una tabla en la que se muestra una lista de conversiones de restricción y ampliación admitidas por la
clase Convert, vea Tablas de conversiones de tipos.
Conversiones personalizadas con el método ChangeType
Además de admitir las conversiones en cada uno de los tipos base, la clase Convert se puede usar para convertir
un tipo personalizado en uno o varios tipos predefinidos. El método Convert.ChangeType(Object, Type,
IFormatProvider), que a su vez contiene una llamada al método IConvertible.ToType del parámetro value , realiza
esta conversión. Esto significa que el objeto representado por el parámetro value debe proporcionar una
implementación de la interfaz IConvertible.
NOTE
Dado que los métodos Convert.ChangeType(Object, Type) y Convert.ChangeType(Object, Type, IFormatProvider) utilizan un
objeto Type para especificar el tipo de destino al que se convierte value , se pueden utilizar para realizar una conversión
dinámica a un objeto cuyo tipo se desconoce en tiempo de compilación. Sin embargo, observe que la implementación de
IConvertible de value aún debe admitir esta conversión.
En el ejemplo siguiente se muestra una posible implementación de la interfaz IConvertible que permite convertir
un objeto TemperatureCelsius en un objeto TemperatureFahrenheit y viceversa. En el ejemplo se define una clase
base, Temperature , que implementa la interfaz IConvertible e invalida el método Object.ToString. Cada una de las
clases derivadas TemperatureCelsius y TemperatureFahrenheit invalida los métodos ToType y ToString de la
clase base.
using System;
// IConvertible implementations.
public TypeCode GetTypeCode() {
return TypeCode.Object;
}
En el ejemplo siguiente se muestran varias llamadas a estas implementaciones de IConvertible para convertir los
objetos TemperatureCelsius en objetos TemperatureFahrenheit y viceversa.
TemperatureCelsius tempC1 = new TemperatureCelsius(0);
TemperatureFahrenheit tempF1 = (TemperatureFahrenheit) Convert.ChangeType(tempC1,
typeof(TemperatureFahrenheit), null);
Console.WriteLine("{0} equals {1}.", tempC1, tempF1);
TemperatureCelsius tempC2 = (TemperatureCelsius) Convert.ChangeType(tempC1, typeof(TemperatureCelsius), null);
Console.WriteLine("{0} equals {1}.", tempC1, tempC2);
TemperatureFahrenheit tempF2 = new TemperatureFahrenheit(212);
TemperatureCelsius tempC3 = (TemperatureCelsius) Convert.ChangeType(tempF2, typeof(TemperatureCelsius), null);
Console.WriteLine("{0} equals {1}.", tempF2, tempC3);
TemperatureFahrenheit tempF3 = (TemperatureFahrenheit) Convert.ChangeType(tempF2,
typeof(TemperatureFahrenheit), null);
Console.WriteLine("{0} equals {1}.", tempF2, tempF3);
// The example displays the following output:
// 0°C equals 32°F.
// 0°C equals 0°C.
// 212°F equals 100°C.
// 212°F equals 212°F.
Clase TypeConverter
.NET Framework también permite definir un convertidor de tipos para un tipo personalizado extendiendo la clase
System.ComponentModel.TypeConverter y asociando el convertidor de tipos al tipo mediante un atributo
System.ComponentModel.TypeConverterAttribute. En la tabla siguiente se resaltan las diferencias entre este
enfoque y la implementación de la interfaz IConvertible para un tipo personalizado.
NOTE
Se puede proporcionar compatibilidad en tiempo de diseño para un tipo personalizado sólo si se ha definido un convertidor
de tipos para éste.
Se implementa para un tipo personalizado derivando una Se implementa mediante un tipo personalizado para realizar la
clase independiente de TypeConverter. Esta clase derivada se conversión. Un usuario del tipo invoca un método de
asocia al tipo personalizado aplicando un atributo conversión de IConvertible para el tipo.
TypeConverterAttribute.
C O N VERSIÓ N M EDIA N T E T Y P EC O N VERT ER C O N VERSIÓ N M EDIA N T E IC O N VERT IB L E
Se puede utilizar en tiempo de diseño y en tiempo de Sólo se puede utilizar en tiempo de ejecución.
ejecución.
Usa la reflexión; por tanto, es más lenta que la conversión No utiliza la reflexión.
mediante IConvertible.
Permite realizar conversiones bidireccionales; es decir, Permite convertir un tipo personalizado en otros tipos de
convertir el tipo personalizado en otros tipos de datos y datos, pero no otros tipos de datos en el tipo personalizado.
convertir otros tipos de datos en el tipo personalizado. Por
ejemplo, un objeto TypeConverter definido para MyType
permite conversiones de MyType a String y de String a
MyType .
Para obtener más información sobre cómo usar convertidores de tipos para realizar conversiones, vea
System.ComponentModel.TypeConverter.
Vea también
System.Convert
IConvertible
Tablas de conversión de tipos
Tablas de conversión de tipos en .NET
16/09/2020 • 4 minutes to read • Edit Online
Una conversión de ampliación se produce cuando se convierte un valor de un tipo a otro tipo que es de igual o
mayor tamaño. Una conversión de restricción se produce cuando se convierte un valor de un tipo a otro tipo que
es de un tamaño menor. En las tablas de este tema se muestran los comportamientos de ambos tipos de
conversiones.
Conversiones de ampliación
En la tabla siguiente se describen las conversiones de ampliación que pueden realizarse sin pérdida de
información.
Int64 Decimal
UInt64 Decimal
Single Double
Algunas conversiones de ampliación a Single o Double pueden provocar una pérdida de precisión. En la tabla
siguiente se describen las conversiones de ampliación que a veces se traducen en una pérdida de información.
T IP O SE P UEDE C O N VERT IR A
Int32 Single
UInt32 Single
Conversiones de restricción
Una conversión de restricción a Single o Double puede provocar una pérdida de información. Si el tipo de destino
no puede expresar correctamente la magnitud del origen, el tipo resultante se establece en la constante
PositiveInfinity o NegativeInfinity . PositiveInfinity resulta al dividir un número positivo por cero y también
se devuelve cuando el valor de Single o Double supera el valor del campo MaxValue . NegativeInfinity resulta al
dividir un número negativo por cero y también se devuelve cuando el valor de Single o Double cae por debajo del
valor del campo MinValue . Una conversión de Double a Single podría dar lugar a PositiveInfinity o
NegativeInfinity .
Una conversión de restricción también puede traducirse en una pérdida de información para otros tipos de datos.
Pero se inicia OverflowException si el valor de un tipo que se va a convertir está fuera del intervalo especificado
por los campos MaxValue y MinValue del tipo de destino y el tiempo de ejecución comprueba la conversión para
asegurarse de que el valor del tipo de destino no supera su MaxValue o MinValue . Las conversiones que se
realizan con la clase System.Convert siempre se comprueban de esta manera.
En la tabla siguiente se enumeran las conversiones que inician OverflowException con System.Convert o cualquier
conversión comprobada si el valor del tipo que se va a convertir está fuera del intervalo definido del tipo
resultante.
T IP O SE P UEDE C O N VERT IR A
Byte SByte
Vea también
System.Convert
Conversión de tipos en .NET
Bibliotecas de Framework
18/03/2020 • 6 minutes to read • Edit Online
.NET tiene un amplio conjunto estándar de bibliotecas, a las que se hace referencia como bibliotecas de clases base
(conjunto básico) o bibliotecas de clases de marco de trabajo (conjunto completo). Estas bibliotecas proporcionan
implementaciones para muchos tipos generales y específicos de las aplicaciones, algoritmos y funcionalidad de la
utilidad. Las bibliotecas comerciales y las comunitarias se basan en las bibliotecas de clases de marco de trabajo, ya
que ofrecen bibliotecas estándar fáciles de usar para un amplio conjunto de tareas informáticas.
Se proporcionan un subconjunto de estas bibliotecas con cada implementación .NET. Se esperan API de bibliotecas
de clases base (BCL) con cualquier implementación de .NET porque los programadores las querrán y porque las
bibliotecas populares necesitarán su ejecución. No estarán disponibles bibliotecas específicas de la aplicación por
encima de BCL, como ASP.NET, en todas las implementaciones .NET.
Tipos primitivos
.NET incluye un conjunto de tipos primitivos, que se usan (en distintos grados) en todos los programas. Estos tipos
contienen datos, como números, cadenas, bytes y objetos arbitrarios. El lenguaje C# incluye palabras clave para
estos tipos. A continuación se muestra un conjunto de ejemplo de estos tipos, con las palabras clave de C#
correspondientes.
System.Object (object): la clase base fundamental en el sistema de tipos de CLR. Es la raíz de la jerarquía de
tipos.
System.Int16 (short): tipo entero con signo de 16 bits. También existe el valor UInt16 sin signo.
System.Int32 (int): tipo entero con signo de 32 bits. También existe el valor UInt32 sin firmar.
System.Single (float): tipo de punto flotante de 32 bits.
System.Decimal (decimal): tipo decimal de 128 bits.
System.Byte (byte): entero sin signo de 8 bits que representa un byte de memoria.
System.Boolean (bool): tipo booleano que representa true o false .
System.Char (char): tipo numérico de 16 bits que representa un carácter Unicode.
System.String (string): representa una serie de caracteres. Diferente de char[] , pero permite la indexación en
cada char individual en string .
Estructuras de datos
.NET incluye un conjunto de estructuras de datos que son la piedra angular de casi cualquier aplicación .NET.
Principalmente se trata de colecciones, pero también incluyen otros tipos.
Array: representa una matriz de objetos fuertemente tipados a la que se puede obtener acceso por índice. Tiene
un tamaño fijo debido a su construcción.
List<T>: representa una lista de objetos fuertemente tipados a la que se puede obtener acceso por índice.
Cambia automáticamente de tamaño según sea necesario.
Dictionary<TKey,TValue>: representa una colección de valores que se indexan mediante una clave. Se puede
obtener acceso a los valores a través de la clave. Cambia automáticamente de tamaño según sea necesario.
Uri: proporciona una representación de objeto de un identificador uniforme de recursos (URI) y un acceso
sencillo a las partes del identificador URI.
DateTime: representa un instante de tiempo, normalmente expresado en forma de fecha y hora del día.
API de utilidades
.NET incluye un conjunto de API de utilidades que proporcionan funcionalidad para muchas tareas importantes.
HttpClient: una API para enviar solicitudes HTTP y recibir respuestas HTTP de un recurso identificado por un URI.
XDocument: una API para cargar y consultar documentos XML con LINQ.
StreamReader: una API para leer archivos.
StreamWriter: una API para escribir archivos.
Las implementaciones de .NET incluyen clases, interfaces, delegados y tipos de valor que agilizan y optimizan el
proceso de desarrollo y proporcionan acceso a las funciones del sistema. Para facilitar la interoperabilidad entre
lenguajes, la mayoría de los tipos de .NET son conformes a CLS y, por tanto, se pueden utilizar en todos los
lenguajes de programación cuyo compilador satisfaga los requisitos de CLS.
Los tipos de .NET son la base sobre la que se compilan aplicaciones, componentes y controles de .NET. Las
implementaciones de .NET incluyen tipos que efectúan las siguientes funciones:
Representar tipos de datos base y excepciones.
Encapsular estructuras de datos.
Realizar E/S.
Obtener acceso a información sobre tipos cargados.
Invocar las comprobaciones de seguridad de .NET Framework.
Proporcionar: acceso a datos, interfaz gráfica para el usuario (GUI) independiente de cliente e interfaz GUI de
cliente controlada por el servidor.
.NET proporciona un conjunto completo de interfaces, así como clases abstractas y concretas (no abstractas). Se
pueden utilizar las clases concretas tal como están o, en muchos casos, derivar las clases propias de ellas. Para usar
la funcionalidad de una interfaz se puede crear una clase que implemente la interfaz o derivar una clase de una de
las clases de .NET que implementa la interfaz.
Convenciones de nomenclatura
Los tipos de .NET usan un esquema de nomenclatura con sintaxis de punto lo que indica la existencia de una
jerarquía. Esta técnica agrupa tipos relacionados en espacios de nombres para que se pueda buscar y hacer
referencia a ellos más fácilmente. La primera parte del nombre completo, hasta el punto situado más a la derecha,
es el nombre del espacio de nombres. La última parte es el nombre de tipo. Por ejemplo,
System.Collections.Generic.List<T> representa el tipo List<T> , que pertenece al espacio de nombres
System.Collections.Generic . Los tipos de System.Collections.Generic se pueden usar para trabajar con colecciones
genéricas.
Este esquema de nomenclatura facilita a los desarrolladores de bibliotecas la tarea de extender .NET Framework
para crear grupos jerárquicos de tipos y asignarles nombre de forma coherente e informativa. También permite
identificar de forma inequívoca los tipos mediante su nombre completo (es decir, por su espacio de nombres y
nombre de tipo), lo que evita que se produzcan conflictos entre los nombres de tipo. Se espera que los
programadores de bibliotecas usen la siguiente convención cuando creen nombres para sus propios espacios de
nombres:
NombreCompañía.NombreTecnología
Por ejemplo, el espacio de nombres Microsoft.Word cumple esta directriz.
El uso de modelos de nomenclatura para agrupar tipos relacionados en espacios de nombres es una forma muy
útil de compilar y documentar bibliotecas de clases. Sin embargo, este esquema de nomenclatura no influye en la
visibilidad, el acceso a miembros, la herencia, la seguridad o el enlace. Se puede hacer la partición de un espacio de
nombres en varios ensamblados y un ensamblado individual puede contener tipos de varios espacios de nombres.
El ensamblado proporciona la estructura formal para el control de versiones, la implementación, la seguridad, la
carga y la visibilidad en Common Language Runtime.
Para obtener más información sobre espacios de nombres y nombres de tipos, vea Common Type System.
T IP O DE T IP O DE
N O M B RE DE DATO S EN T IP O DE DATO S EN T IP O DE
C AT EGO RÍA L A C L A SE DESC RIP C IÓ N VISUA L B A SIC DATO S EN C # C ++/ C L I DATO S EN F #
long
No es
conforme a
CLS.
No es
conforme a
CLS.
No es
conforme a
CLS.
Además de los tipos de datos base, el espacio de nombres System contiene más de 100 clases, que comprenden
desde las clases que controlan excepciones hasta las clases que tratan conceptos básicos en tiempo de ejecución,
como los dominios de aplicación y el recolector de elementos no utilizados. El espacio de nombres System también
contiene muchos espacios de nombres de segundo nivel.
Para más información sobre los espacios de nombres, use el explorador de API de .NET para examinar la biblioteca
de clases .NET. La documentación de referencia de API proporciona información sobre cada espacio de nombres,
sus tipos y cada uno de sus miembros.
Vea también
Sistema de tipos comunes
Explorador de API de .NET
Información general
Elementos genéricos en .NET
16/09/2020 • 17 minutes to read • Edit Online
Los genéricos permiten personalizar un método, clase, estructura o interfaz con respecto a los datos precisos sobre
los que se actúa. Por ejemplo, en lugar de utilizar la clase Hashtable , que permite cualquier tipo de clave y valor,
puede utilizar la clase genérica Dictionary<TKey,TValue> y especificar el tipo permitido para la clave y el tipo
permitido para el valor. Entre las ventajas de los genéricos están una mayor reutilización del código y la seguridad
de tipos.
generic<typename T>
public ref class Generics
{
public:
T Field;
};
End Class
Cuando cree una instancia de una clase genérica, especifique los tipos reales que hay que sustituir para los
parámetros de tipo. Esto establece una nueva clase genérica, a la que se hace referencia como clase genérica
construida, con los tipos que elija sustituidos en todas partes en la que aparecen los parámetros de tipo. El
resultado es una clase con seguridad de tipos que se adapta a su elección de tipos, como se muestra en el código
siguiente.
T Generic<T>(T arg)
{
T temp = arg;
//...
return temp;
}
Los métodos genéricos pueden aparecer en tipos genéricos o no genéricos. Es importante tener en cuenta que un
método no es genérico solo porque pertenezca a un tipo genérico, ni siquiera porque tenga parámetros formales
cuyos tipos sean los parámetros genéricos del tipo envolvente. Un método solo es genérico si tiene su propia lista
de parámetros de tipo. En el siguiente código, solo es genérico el método G .
ref class A
{
generic<typename T>
T G(T arg)
{
T temp = arg;
//...
return temp;
}
};
generic<typename T>
ref class Generic
{
T M(T arg)
{
T temp = arg;
//...
return temp;
}
};
class A
{
T G<T>(T arg)
{
T temp = arg;
//...
return temp;
}
}
class Generic<T>
{
T M(T arg)
{
T temp = arg;
//...
return temp;
}
}
Class A
Function G(Of T)(ByVal arg As T) As T
Dim temp As T = arg
'...
Return temp
End Function
End Class
Class Generic(Of T)
Function M(ByVal arg As T) As T
Dim temp As T = arg
'...
Return temp
End Function
End Class
Mejor rendimiento. Los tipos de colección genéricos suelen comportarse mejor en el almacenamiento y
manipulación de tipos de valor porque no es necesario realizar una conversión boxing de los tipos de valor.
Los delegados genéricos permiten devoluciones de llamada con seguridad de tipos sin necesidad de crear
varias clases de delegado. Por ejemplo, el delegado genérico Predicate<T> le permite crear un método que
implementa sus propios criterios de búsqueda para un tipo concreto y utilizar su método con métodos del
tipo Array , como Find, FindLasty FindAll.
Los genéricos optimizan el código generado dinámicamente. Cuando se utilizan genéricos con código
generado dinámicamente, no es necesario generar el tipo. Esto aumenta el número de escenarios en los que
puede utilizar métodos dinámicos ligeros en lugar de generar ensamblados completos. Para obtener más
información, vea Cómo: Definir y ejecutar métodos dinámicos y DynamicMethod.
Las siguientes son algunas limitaciones de los genéricos:
Los tipos genéricos pueden derivarse de la mayoría de las clases base, como MarshalByRefObject (y pueden
utilizarse restricciones para exigir que los parámetros de tipo genérico se deriven de clases base como
MarshalByRefObject). Sin embargo, .NET Framework no admite los tipos genéricos enlazados a un contexto.
Un tipo genérico puede derivarse de ContextBoundObject, pero al intentar crear una instancia de dicho tipo,
se genera una TypeLoadException.
Las enumeraciones no pueden tener parámetros de tipo genérico. Una enumeración solo puede ser
genérica a propósito (por ejemplo, porque está anidada en un tipo genérico definido mediante Visual Basic,
C# o C++). Para más información, vea "Enumeraciones" en Common Type System.
Los métodos dinámicos ligeros no pueden ser genéricos.
En Visual Basic, C# y C++, no se pueden crear instancias de un tipo anidado incluido en un tipo genérico, a
menos que los tipos se hayan asignado a los parámetros de tipo de todos los tipos envolventes. Dicho de
otro modo: en la reflexión, un tipo anidado definido mediante estos lenguajes incluye los parámetros de
tipo de todos sus tipos envolventes. Esto permite que los parámetros de tipo de tipos envolventes se usen
en las definiciones de miembro de un tipo anidado. Para obtener más información, vea “Tipos anidados” en
MakeGenericType.
NOTE
Un tipo anidado definido mediante la emisión de código en un ensamblado dinámico o mediante el uso de Ilasm.exe
(Ensamblado de IL) no necesita incluir los parámetros de tipo de sus tipos envolventes. Sin embargo, si no los incluye,
los parámetros de tipo no estarán en el ámbito de la clase anidada.
Temas relacionados
T IT L E DESC RIP C IÓ N
Colecciones genéricas en .NET Describe las clases de colección genérica y otros tipos
genéricos en .NET.
Delegados genéricos para manipular matrices y listas Describe los delegados genéricos para conversiones,
predicados de búsqueda y acciones realizadas en los
elementos de una matriz o colección.
Tipos de colección utilizados normalmente Proporciona información de resumen sobre las características
y escenarios de uso de los tipos de colección en .NET, incluidos
los tipos genéricos.
Cuándo utilizar colecciones genéricas Describe las reglas generales para determinar cuándo utilizar
los tipos de colección genéricos.
Cómo: Definir un tipo genérico con emisión de reflexión Explica cómo generar ensamblados dinámicos que incluyan
métodos y tipos genéricos.
Tipos genéricos en Visual Basic Describe los genéricos a los usuarios de Visual Basic e incluye
temas sobre el uso y la definición de tipos genéricos.
Información general sobre genéricos en Visual C++ Describe los genéricos a los usuarios de C++ e incluye las
diferencias entre genéricos y plantillas.
Referencia
System.Collections.Generic
System.Collections.ObjectModel
System.Reflection.Emit.OpCodes
Información general de tipos genéricos
16/09/2020 • 5 minutes to read • Edit Online
Los desarrolladores utilizan genéricos en todo momento en .NET, ya sea implícita o explícitamente. Al usar LINQ en
.NET, ¿alguna vez observó que estaba trabajando con IEnumerable<T>? O, si alguna vez ha visto un ejemplo en
línea de un "repositorio genérico" para comunicarse con bases de datos mediante Entity Framework, ha observado
que la mayoría de los métodos devuelven IQueryable<T> ? Probablemente se pregunte qué significa la T en estos
ejemplos y por qué aparece.
Introducidos por primera vez en .NET Framework 2.0, los genéricos son esencialmente una "plantilla de código"
que permite a los desarrolladores definir estructuras de datos con seguridad de tipos sin confirmar un tipo de
datos real. Por ejemplo, List<T> es una colección genérica que se puede declarar y usar con cualquier tipo, como
List<int> , List<string> o List<Person> .
Para entender por qué los genéricos son útiles, vamos a echar un vistazo a una clase específica antes y después de
agregar genéricos: ArrayList. En .NET Framework 1.0, los elementos ArrayList eran de tipo Object. Cualquier
elemento agregado a la colección se convertía desapercibidamente en un elemento Object . Lo mismo podría
suceder al leer elementos de la lista. Este proceso se conoce como conversión boxing y unboxing, y afecta al
rendimiento. Sin embargo, además del rendimiento, no hay ninguna manera de determinar el tipo de datos de la
lista en tiempo de compilación, lo que hace que algunos códigos sean frágiles. Los genéricos solucionan este
problema definiendo el tipo de datos que va a contener cada instancia de la lista. Por ejemplo, solo puede agregar
enteros a List<int> y solo puede agregar personas a List<Person> .
Los genéricos también están disponibles en tiempo de ejecución. El tiempo de ejecución conoce qué tipo de
estructura de datos está usando y puede almacenarla en memoria de forma más eficaz.
El ejemplo siguiente es un pequeño programa que muestra la eficacia que supone conocer el tipo de estructura de
datos en tiempo de ejecución:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace GenericsExample {
class Program {
static void Main(string[] args) {
//generic list
List<int> ListGeneric = new List<int> { 5, 9, 1, 4 };
//non-generic list
ArrayList ListNonGeneric = new ArrayList { 5, 9, 1, 4 };
// timer for generic list sort
Stopwatch s = Stopwatch.StartNew();
ListGeneric.Sort();
s.Stop();
Console.WriteLine($"Generic Sort: {ListGeneric} \n Time taken: {s.Elapsed.TotalMilliseconds}ms");
Lo primero que verá aquí es que la ordenación de la lista genérica es significativamente más rápida que la de la
lista no genérica. También puede observar que el tipo de la lista genérica es distinto ([System.Int32]), mientras que
el tipo de la lista no genérica es generalizado. Dado que el tiempo de ejecución sabe que el genérico List<int> es
de tipo Int32, puede almacenar los elementos de la lista en una matriz de enteros subyacente en memoria,
mientras el no genérico ArrayList tiene que convertir cada elemento de la lista en un objeto. Como se muestra en
este ejemplo, las conversiones adicionales consumen tiempo y ralentizan la ordenación de la lista.
Otra ventaja adicional de que el tiempo de ejecución conozca el tipo de la clase genérica es una mejor experiencia
de depuración. Cuando se depura un genérico en C#, sabe qué tipo de cada elemento se encuentra en la estructura
de datos. Sin genéricos, no sabría qué tipo de cada elemento estaba presente.
Vea también
Guía de programación de C#: genéricos
Colecciones genéricas en .NET
16/09/2020 • 3 minutes to read • Edit Online
La biblioteca de clases de .NET ofrece varias clases de colección genéricas en los espacios de nombres
System.Collections.Generic y System.Collections.ObjectModel. Para obtener información más detallada sobre estas
clases, vea Tipos de colección utilizados normalmente.
System.Collections.Generic
Muchos de los tipos de colección genéricos son análogos directos de tipos no genéricos. Dictionary<TKey,TValue>
es una versión genérica de Hashtable; usa la estructura genérica KeyValuePair<TKey,TValue> para la enumeración
en lugar de DictionaryEntry.
List<T> es una versión genérica de ArrayList. Hay clases Queue<T> y Stack<T> genéricas que se corresponden
con las versiones no genéricas.
Hay versiones genéricas y no genéricas de SortedList<TKey,TValue>. Ambas versiones son híbridos de un
diccionario y una lista. La clase genérica SortedDictionary<TKey,TValue> es un diccionario puro y no tiene ninguna
homóloga no genérica.
La clase genérica LinkedList<T> es una lista vinculada genuina. No tiene ninguna homóloga no genérica.
System.Collections.ObjectModel
La clase genérica Collection<T> proporciona una clase base para derivar sus propios tipos de colección genéricos.
La clase ReadOnlyCollection<T> proporciona una manera sencilla de generar una colección de solo lectura de
cualquier tipo que implementa la interfaz genérica IList<T>. La clase genérica KeyedCollection<TKey,TItem>
proporciona una manera de almacenar objetos que contienen sus propias claves.
NOTE
En C# y Visual Basic no hay que usar Nullable<T> explícitamente porque el lenguaje tiene sintaxis para tipos que aceptan
valores NULL. Consulte Tipos de valor que admiten un valor NULL (referencia de C#) y Tipos de valor que admiten un valor
NULL (Visual Basic).
La estructura genérica ArraySegment<T> proporciona una manera de delimitar un intervalo de elementos en una
matriz unidimensional basada en cero de cualquier tipo. El parámetro de tipo genérico es el tipo de los elementos
de la matriz.
El delegado genérico EventHandler<TEventArgs> elimina la necesidad de declarar un tipo de delegado para
controlar los eventos, siempre que el evento siga el patrón de control de eventos que usa .NET Framework. Por
ejemplo, supongamos que ha creado una clase MyEventArgs , derivada de EventArgs, para contener los datos del
evento. Puede declarar el evento de la siguiente manera:
public:
event EventHandler<MyEventArgs^>^ MyEvent;
Vea también
System.Collections.Generic
System.Collections.ObjectModel
Genéricos
Delegados genéricos para manipular matrices y listas
Interfaces genéricas
Delegados genéricos para manipular matrices y listas
16/09/2020 • 5 minutes to read • Edit Online
En este tema se ofrece una introducción a los delegados genéricos para conversiones, predicados de búsqueda y
acciones realizadas en los elementos de una matriz o colección.
NOTE
Este es un aspecto interesante de los tipos y métodos genéricos. El método Array.ForEach debe ser estático ( Shared en
Visual Basic) y genérico porque Array no es un tipo genérico; la única razón por la que se puede especificar un tipo para que
Array.ForEach opere sobre él es que el método tiene su propia lista de parámetros de tipo. Por el contrario, el método no
genérico List<T>.ForEach pertenece a la clase genérica List<T>, por lo que simplemente utiliza el parámetro de tipo de su
clase. La clase está fuertemente tipada, por lo que el método puede ser un método de instancia.
El delegado genérico Predicate<T> representa un método que determina si un determinado elemento cumple los
criterios definidos. Puede usar los siguientes métodos genéricos estáticos de Array para buscar un elemento o un
conjunto de elementos: Exists, Find, FindAll, FindIndex, FindLast, FindLastIndex y TrueForAll.
Predicate<T> también trabaja con los correspondientes métodos de instancia no genéricos de la clase genérica
List<T>.
El delegado genérico Comparison<T> permite proporcionar un criterio de ordenación para los elementos de la
matriz o la lista que no tienen un criterio de ordenación nativo, o para reemplazar el criterio de ordenación nativo.
Cree un método que realice la comparación, cree una instancia del delegado Comparison<T> para representar el
método y, después, pase la matriz y el delegado al método genérico estático Array.Sort<T>(T[], Comparison<T>).
La clase genérica List<T> proporciona una sobrecarga del método de instancia correspondiente,
List<T>.Sort(Comparison<T>).
El delegado genérico Converter<TInput,TOutput> permite definir una conversión entre dos tipos y convertir una
matriz de un tipo en una matriz del otro, o convertir una lista de un tipo a una lista del otro. Cree un método que
convierta los elementos de la lista existente a un nuevo tipo, cree una instancia de delegado para representar el
método y use el método estático genérico Array.ConvertAll para producir una matriz del nuevo tipo a partir de la
matriz original, o el método de instancia genérico List<T>.ConvertAll<TOutput>(Converter<T,TOutput>) para
producir una lista del nuevo tipo a partir de la lista original.
Encadenar delegados
Muchos de los métodos que usan estos delegados devuelven una matriz o una lista, que se puede pasar a otro
método. Por ejemplo, si quiere seleccionar determinados elementos de una matriz, convertirlos a un nuevo tipo y
guardarlos en una nueva matriz, puede pasar la matriz devuelta por el método genérico FindAll al método
genérico ConvertAll. Si el nuevo tipo de elemento carece de un criterio de ordenación natural, puede pasar la
matriz devuelta por el método genérico ConvertAll al método genérico Sort<T>(T[], Comparison<T>).
Vea también
System.Collections.Generic
System.Collections.ObjectModel
Genéricos
Colecciones genéricas en .NET Framework
Interfaces genéricas
Covarianza y contravarianza
Interfaces genéricas
16/09/2020 • 4 minutes to read • Edit Online
Este tema ofrece una introducción a las interfaces genéricas que proporcionan funcionalidad común a distintas
familias de tipos genéricos.
Interfaces genéricas
Las interfaces genéricas proporcionan homólogas con seguridad de tipos a las interfaces no genéricas para
realizar comparaciones de ordenación y de igualdad, y para obtener funcionalidad compartida por los tipos de
colección genéricos.
NOTE
A partir de .NET Framework 4, los parámetros de tipo de varias interfaces genéricas están marcados como covariantes o
contravariantes, y ofrecen una mayor flexibilidad para asignar y usar tipos que implementan estas interfaces. Vea Covarianza
y contravarianza.
Vea también
System.Collections.Generic
System.Collections.ObjectModel
Genéricos
Colecciones genéricas en .NET Framework
Delegados genéricos para manipular matrices y listas
Covarianza y contravarianza
Covarianza y contravarianza en genéricos
16/09/2020 • 26 minutes to read • Edit Online
Covarianza y contravarianza son términos que hacen referencia a la capacidad de usar un tipo más derivado (más
específico) o menos derivado (menos específico) que el indicado originalmente. Los parámetros de tipo genérico
admiten la covarianza y contravarianza para proporcionar mayor flexibilidad a la hora de asignar y usar tipos
genéricos. Cuando se hace referencia a un sistema de tipos, la covarianza, contravarianza e invarianza tienen las
siguientes definiciones. En el ejemplo se presupone una clase base denominada Base y una clase derivada
denominada Derived .
Covariance
Permite usar un tipo más genérico (menos derivado) que el especificado originalmente.
Puede asignar una instancia de Action<Base> ( Action(Of Base) en Visual Basic) a una variable de tipo
Action<Derived> .
Invariance
Significa que solo se puede usar el tipo especificado originalmente. Así, un parámetro de tipo genérico
invariable no es covariante ni contravariante.
No se puede asignar una instancia de List<Base> ( List(Of Base) en Visual Basic) a una variable de tipo
List<Derived> o viceversa.
Los parámetros de tipo covariante permiten realizar asignaciones muy similares al polimorfismo, como se
muestra en el código siguiente.
La clase List<T> implementa la interfaz IEnumerable<T> , por lo que List<Derived> ( List(Of Derived) en Visual
Basic) implementa IEnumerable<Derived> . El parámetro de tipo covariante se encarga del resto.
La contravarianza, sin embargo, parece poco intuitiva. En el siguiente ejemplo, se crea un delegado de tipo
Action<Base> ( Action(Of Base) en Visual Basic) y, a continuación, se asigna ese delegado a una variable de tipo
Action<Derived> .
Parece un paso hacia atrás, pero lo que se compila y se ejecuta es código con seguridad de tipos. La expresión
lambda se corresponde con el delegado que tiene asignado, por lo que define un método que toma un parámetro
de tipo Base y no tiene ningún valor devuelto. El delegado resultante puede asignarse a una variable de tipo
Action<Derived> porque el parámetro de tipo T del delegado Action<T> es contravariante. El código tiene
seguridad de tipos porque T especifica un tipo de parámetro. Cuando se invoca el delegado de tipo
Action<Base> como si fuera un delegado de tipo Action<Derived> , su argumento debe ser de tipo Derived . Este
argumento siempre se puede pasar de manera segura al método subyacente porque el parámetro del método es
de tipo Base .
En general, los parámetros de tipo covariante se pueden utilizar como tipos de valor devuelto de un delegado, y
los parámetros de tipo contravariante se pueden usar como tipos de parámetro. En el caso de una interfaz, los
parámetros de tipo covariante se pueden utilizar como tipos de valor devuelto de los métodos de la interfaz, y los
parámetros de tipo contravariante se pueden usar como tipos de parámetro de los métodos de la interfaz.
La covarianza y la contravarianza se denominan colectivamente varianza. Un parámetro de tipo genérico que no
está marcado como covariante ni contravariante se denomina invariable. Un breve resumen de hechos
relacionados con la varianza en Common Language Runtime:
En .NET Framework 4, los parámetros de tipo variante están restringidos a los tipos de interfaz genérica y
delegado genérico.
Un tipo de interfaz genérica o de delegado genérico puede tener parámetros de tipo covariante y
contravariante.
La varianza se aplica únicamente a los tipos de referencia; si se especifica un tipo de valor para un
parámetro de tipo variante, ese parámetro de tipo es invariable para el tipo construido resultante.
La varianza no se aplica a la combinación de delegados. Es decir, si hay dos delegados de tipo
Action<Derived> y de tipo Action<Base> ( Action(Of Derived) y Action(Of Base) en Visual Basic), no se
puede combinar el segundo delegado con el primero aunque el resultado tuviese seguridad de tipos. La
varianza permite la asignación del segundo delegado a una variable de tipo Action<Derived> , pero los
delegados solo se pueden combinar si tienen exactamente el mismo tipo.
class Base
{
public static void PrintBases(IEnumerable<Base> bases)
{
foreach(Base b in bases)
{
Console.WriteLine(b);
}
}
}
Derived.PrintBases(dlist);
IEnumerable<Base> bIEnum = dlist;
}
}
Imports System.Collections.Generic
Class Base
Public Shared Sub PrintBases(ByVal bases As IEnumerable(Of Base))
For Each b As Base In bases
Console.WriteLine(b)
Next
End Sub
End Class
Class Derived
Inherits Base
Derived.PrintBases(dlist)
Dim bIEnum As IEnumerable(Of Base) = dlist
End Sub
End Class
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// You can pass ShapeAreaComparer, which implements IComparer<Shape>,
// even though the constructor for SortedSet<Circle> expects
// IComparer<Circle>, because type parameter T of IComparer<T> is
// contravariant.
SortedSet<Circle> circlesByArea =
new SortedSet<Circle>(new ShapeAreaComparer())
{ new Circle(7.2), new Circle(100), null, new Circle(.01) };
null
Circle with area 0.000314159265358979
Circle with area 162.860163162095
Circle with area 31415.9265358979
*/
Imports System.Collections.Generic
Class Circle
Inherits Shape
Private r As Double
Public Sub New(ByVal radius As Double)
r = radius
End Sub
Public ReadOnly Property Radius As Double
Get
Return r
End Get
End Property
Public Overrides ReadOnly Property Area As Double
Get
Return Math.Pi * r * r
End Get
End Property
End Class
Class ShapeAreaComparer
Implements System.Collections.Generic.IComparer(Of Shape)
Class Program
Shared Sub Main()
' You can pass ShapeAreaComparer, which implements IComparer(Of Shape),
' even though the constructor for SortedSet(Of Circle) expects
' IComparer(Of Circle), because type parameter T of IComparer(Of T)
' is contravariant.
Dim circlesByArea As New SortedSet(Of Circle)(New ShapeAreaComparer()) _
From {New Circle(7.2), New Circle(100), Nothing, New Circle(.01)}
Esto se ilustra en el código siguiente: En el primer fragmento de código, se definen una clase denominada Base ,
una clase denominada Derived que hereda de Base y otra clase con un método static ( Shared en Visual
Basic) denominado MyMethod . El método toma una instancia de Base y devuelve una instancia de Derived . (Si el
argumento es una instancia de Derived , MyMethod la devuelve; si el argumento es una instancia de Base ,
MyMethod devuelve una nueva instancia de Derived .) En Main() , se crea en el ejemplo una instancia de
Func<Base, Derived> ( Func(Of Base, Derived) en Visual Basic) que representa MyMethod , y la almacena en la
variable f1 .
En el segundo fragmento de código, se muestra que el delegado puede asignarse a una variable de tipo
Func<Base, Base> ( Func(Of Base, Base) en Visual Basic) ya que el tipo de valor devuelto es covariante.
En el tercer fragmento de código, se muestra que el delegado puede asignarse a una variable de tipo
Func<Derived, Derived> ( Func(Of Derived, Derived) en Visual Basic) ya que el tipo de parámetro es
contravariante.
En el último fragmento de código, se muestra que el delegado puede asignarse a una variable de tipo
Func<Derived, Base> ( Func(Of Derived, Base) en Visual Basic), combinando los efectos del tipo de parámetro
contravariante y el tipo de valor devuelto covariante.
NOTE
A partir de .NET Framework versión 2.0, Common Language Runtime admite anotaciones de varianza en parámetros de
tipo genérico. Antes de .NET Framework 4, la única manera de definir una clase genérica que tuviera estas anotaciones era
usar el lenguaje intermedio de Microsoft (MSIL), ya fuera mediante la compilación de la clase con Ilasm.exe (Ensamblador de
IL) o con la emisión en un ensamblado dinámico.
Un parámetro de tipo covariante se marca con la palabra clave out (palabra clave Out en Visual Basic, + para el
Ensamblador de MSIL). Puede usar un parámetro de tipo covariante como el valor devuelto de un método que
pertenece a una interfaz o como el tipo de valor devuelto de un delegado. No puede usar un parámetro de tipo
covariante como una restricción de tipo genérico para los métodos de interfaz.
NOTE
Si un método de una interfaz tiene un parámetro que es un tipo de delegado genérico, se puede usar un parámetro de tipo
covariante del tipo de interfaz para especificar un parámetro de tipo contravariante del tipo de delegado.
Un parámetro de tipo contravariante se marca con la palabra clave in (palabra clave In en Visual Basic, - para
el Ensamblador de MSIL). Puede usar un parámetro de tipo contravariante como el tipo de un parámetro de un
método que pertenece a una interfaz o como el tipo de un parámetro de un delegado. Puede usar un parámetro
de tipo contravariante como una restricción de tipo genérico para un método de interfaz.
Solo los tipos de interfaz y los tipos de delegado pueden tener parámetros de tipo variante. Un tipo de interfaz o
un tipo de delegado puede tener parámetros de tipo covariante y contravariante.
Visual Basic y C# no le permiten infringir las reglas de uso de parámetros de tipo covariante y contravariante ni
agregar anotaciones de covarianza y contravarianza a los parámetros de tipo de tipos distintos de interfaces y
delegados. El Ensamblador de MSIL no realiza esas comprobaciones, pero se produce TypeLoadException si
intenta cargar un tipo que infringe las reglas.
Para obtener información y código de ejemplo, vea Varianza en interfaces genéricas (C#) y Varianza en interfaces
genéricas (Visual Basic).
PA RÁ M ET RO S DE T IP O
T IP O PA RÁ M ET RO S DE T IP O C O VA RIA N T E C O N T RAVA RIA N T E
Action<T> a Sí
Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,
T11,T12,T13,T14,T15,T16>
Comparison<T> Sí
Converter<TInput,TOutput> Sí Sí
Func<TResult> Sí
Func<T,TResult> a Sí Sí
Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T
11,T12,T13,T14,T15,T16,TResult>
IComparable<T> Sí
Predicate<T> Sí
IComparer<T> Sí
IEnumerable<T> Sí
IEnumerator<T> Sí
PA RÁ M ET RO S DE T IP O
T IP O PA RÁ M ET RO S DE T IP O C O VA RIA N T E C O N T RAVA RIA N T E
IEqualityComparer<T> Sí
IGrouping<TKey,TElement> Sí
IOrderedEnumerable<TElement> Sí
IOrderedQueryable<T> Sí
IQueryable<T> Sí
Vea también
Covarianza y contravarianza (C#)
Covarianza y contravarianza (Visual Basic)
Varianza en delegados (C#)
Varianza en delegados (Visual Basic)
Colecciones y estructuras de datos
16/09/2020 • 13 minutes to read • Edit Online
A menudo, los datos similares pueden controlarse de forma más eficaz si se almacenan y manipulan como si
fuesen una colección. Puede utilizar la clase System.Array o las clases de los espacios de nombres
System.Collections, System.Collections.Generic,System.Collections.Concurrent y System.Collections.Immutable
para agregar, quitar y modificar elementos individuales o un intervalo de elementos de una colección.
Hay dos tipos principales de colecciones: las colecciones genéricas y las colecciones no genéricas. Las colecciones
genéricas se agregaron en la versión 2.0 de.NET Framework y son colecciones con seguridad de tipos en tiempo de
compilación. Debido a esto, las colecciones genéricas normalmente ofrecen un mejor rendimiento. Las colecciones
genéricas aceptan un parámetro de tipo cuando se construyen y no requieren conversiones con el tipo Object al
agregar o quitar elementos de la colección. Además, la mayoría de colecciones genéricas son compatibles con las
aplicaciones de la Tienda Windows. Las colecciones no genéricas almacenan elementos como Object, requieren
una conversión y la mayoría de ellas no son compatibles con el desarrollo de aplicaciones de la Tienda Windows.
Sin embargo, puede que vea colecciones no genéricas en código antiguo.
A partir de .NET Framework 4, las colecciones del espacio de nombres System.Collections.Concurrent proporcionan
operaciones eficaces y seguras para subprocesos con el fin de obtener acceso a los elementos de la colección
desde varios subprocesos. Las clases de colección inmutables en el espacio de nombres
System.Collections.Immutable (paquete de NuGet) son intrínsecamente seguras para los subprocesos, ya que las
operaciones se realizan en una copia de la colección original, mientras que la colección original no se puede
modificar.
O P C IO N ES DE C O L EC C IÓ N
O P C IO N ES DE C O L EC C IÓ N O P C IO N ES DE C O L EC C IÓ N DE SUB P RO C ESO S O
DESEO. . . GEN ÉRIC A N O GEN ÉRIC A IN M UTA B L E
ImmutableSortedSet<T>
List<T>.Enumerator O( n ) O( n ) O( n )
ImmutableList<T>.Enumerator
Un objeto List<T> se puede enumerar eficientemente utilizando un bucle for o un bucle foreach . Sin embargo,
un objeto ImmutableList<T> realiza un trabajo insuficiente dentro de un bucle for , debido al tiempo de O(log n )
de su indizador. Enumerar un objeto ImmutableList<T> usando un bucle foreach es eficiente porque
ImmutableList<T> usa un árbol binario para almacenar sus datos en lugar de una matriz simple como la que usa
List<T> . Una matriz se puede indexar muy rápidamente, mientras que un árbol binario debe desplazarse hasta
que se encuentre el nodo con el índice deseado.
Además, SortedSet<T> tiene la misma complejidad que ImmutableSortedSet<T> . Esto es porque ambos usan
árboles binarios. La diferencia significativa, por supuesto, es que ImmutableSortedSet<T> usa un árbol binario
inmutable. Dado que ImmutableSortedSet<T> también ofrece una clase
System.Collections.Immutable.ImmutableSortedSet<T>.Builder que permite la mutación, puede obtener tanto
inmutabilidad como rendimiento.
Temas relacionados
T IT L E DESC RIP C IÓ N
Seleccionar una clase de colección Describe las diferentes colecciones y le ayuda a seleccionar una
para su escenario.
Tipos de colección utilizados normalmente Describe los tipos de colección genéricos y no genéricos más
utilizados, como System.Array,
System.Collections.Generic.List<T> y
System.Collections.Generic.Dictionary<TKey,TValue>.
Cuándo utilizar colecciones genéricas Describe el uso de los tipos de colección genéricos.
Tipos de las colecciones Hashtable y Dictionary Describe las características de los tipos de diccionarios basados
en hash genéricos y no genéricos.
Asegúrese de elegir con cuidado la clase de colección. Usar un tipo incorrecto puede restringir el uso de la
colección.
IMPORTANT
Evite usar los tipos del espacio de nombres System.Collections. Se recomiendan las versiones genéricas y simultáneas de las
colecciones por la mayor seguridad de los tipos y otras mejoras.
Pregúntese lo siguiente:
¿Necesita una lista secuencial en la que normalmente se descarta el elemento después de recuperar su
valor?
En caso afirmativo, considere usar la clase Queue o la clase genérica Queue<T> si necesita un
comportamiento de tipo primero en entrar, primero en salir (FIFO). Considere usar la clase Stack o la
clase genérica Stack<T> si necesita un comportamiento de tipo último en entrar, primero en salir
(LIFO). Para obtener acceso seguro desde varios subprocesos, use las versiones simultáneas
ConcurrentQueue<T> y ConcurrentStack<T>. En el caso de la inmutabilidad, tenga en cuenta las
versiones inmutables, ImmutableQueue<T> y ImmutableStack<T>.
En caso contrario, considere usar las demás colecciones.
¿Necesita acceder a los elementos en cierto orden, como FIFO, LIFO o aleatorio?
La clase Queue, así como las clases genéricas Queue<T>, ConcurrentQueue<T> y
ImmutableQueue<T>, ofrecen acceso FIFO. Para obtener más información, consulte Cuándo usar
una colección segura para subprocesos.
La clase Stack, así como las clases genéricas Stack<T>, ConcurrentStack<T> y ImmutableStack<T>,
ofrecen acceso LIFO. Para obtener más información, consulte Cuándo usar una colección segura para
subprocesos.
La clase genérica LinkedList<T> permite el acceso secuencial desde el encabezado hasta el final o
desde el final hasta el encabezado.
¿Necesita acceder a cada elemento por índice?
Las clases ArrayList y StringCollection y la clase genérica List<T> ofrecen acceso a sus elementos por
el índice de base cero del elemento. En el caso de la inmutabilidad, tenga en cuenta las versiones
genéricas inmutables, ImmutableArray<T> y ImmutableList<T>.
Las clases Hashtable, SortedList, ListDictionary y StringDictionary, y las clases genéricas
Dictionary<TKey,TValue> y SortedDictionary<TKey,TValue> ofrecen acceso a sus elementos por la
clave del elemento. Además, hay versiones inmutables de varios tipos correspondientes:
ImmutableHashSet<T>, ImmutableDictionary<TKey,TValue>, ImmutableSortedSet<T> y
ImmutableSortedDictionary<TKey,TValue>.
Las clases NameObjectCollectionBase y NameValueCollection, y las clases genéricas
KeyedCollection<TKey,TItem> y SortedList<TKey,TValue> ofrecen acceso a sus elementos por el
índice de base cero o por la clave del elemento.
¿Cada elemento contendrá un valor, una combinación de una clave y un valor, o una combinación de una
clave y varios valores?
Un valor: use cualquiera de las colecciones basadas en la interfaz IList o en la interfaz genérica
IList<T>. En el caso de una opción inmutable, tenga en cuenta la interfaz genérica de
IImmutableList<T>.
Una clave y un valor: use cualquiera de las colecciones basadas en la interfaz IDictionary o en la
interfaz genérica IDictionary<TKey,TValue>. En el caso de una opción inmutable, tenga en cuenta las
interfaces genérica de IImmutableSet<T> o IImmutableDictionary<TKey,TValue>.
Un valor con clave insertada: use la clase genérica KeyedCollection<TKey,TItem>.
Una clave y varios valores: use la clase NameValueCollection.
¿Necesita ordenar los elementos de manera diferente a como se especificaron?
La clase Hashtable ordena los elementos por sus códigos hash.
La clase SortedList y las clases genéricas SortedList<TKey,TValue> y SortedDictionary<TKey,TValue>
ordenan sus elementos por la clave. El criterio de ordenación se basa en la implementación de la
interfaz IComparer para la clase SortedList y en la implementación de la interfaz genérica
IComparer<T> para las clases genéricas SortedList<TKey,TValue> y SortedDictionary<TKey,TValue>.
De los dos tipos genéricos, SortedDictionary<TKey,TValue> ofrece mejor rendimiento que
SortedList<TKey,TValue>, mientras que SortedList<TKey,TValue> consume menos memoria.
ArrayList proporciona un método Sort que toma una implementación de IComparer como
parámetro. Su homóloga genérica, la clase genérica List<T>, proporciona un método Sort que toma
una implementación de la interfaz genérica IComparer<T> como parámetro.
¿Necesita realizar búsquedas y recuperaciones rápidas de información?
ListDictionary es más rápida que Hashtable para colecciones pequeñas (de 10 elementos o menos). La
clase genérica Dictionary<TKey,TValue> proporciona búsquedas más rápidas que la clase genérica
SortedDictionary<TKey,TValue>. La implementación multiproceso es
ConcurrentDictionary<TKey,TValue>. ConcurrentBag<T> proporciona una inserción multiproceso
rápida para datos no ordenados. Para más información sobre ambos tipos multiproceso, consulte
Cuándo usar una colección segura para subprocesos.
¿Necesita colecciones que acepten solo cadenas?
StringCollection (basada en IList) y StringDictionary (basada en IDictionary) están en el espacio de
nombres System.Collections.Specialized.
Además, puede usar cualquiera de las clases de colección genéricas del espacio de nombres
System.Collections.Generic como colecciones de cadenas fuertemente tipadas especificando la clase
String para sus argumentos de tipo genéricos. Por ejemplo, puede declarar una variable para que sea
de tipo List<String> o Dictionary<String,String>.
Vea también
System.Collections
System.Collections.Specialized
System.Collections.Generic
Colecciones seguras para subprocesos
Tipos de colección utilizados normalmente
16/09/2020 • 5 minutes to read • Edit Online
Los tipos de colecciones son las variaciones comunes de las colecciones de datos, como tablas hash, colas, pilas,
bolsas, diccionarios y listas.
Las colecciones se basan en las interfaces ICollection, IList, IDictionary o en sus equivalentes genéricos. La interfaz
IList y la interfaz IDictionary se derivan ambas de la interfaz ICollection; por lo tanto, todas las colecciones se
basan en la interfaz ICollection directa o indirectamente. En colecciones basadas en la interfaz IList (como Array,
ArrayList o List<T>) o directamente en la interfaz ICollection (como Queue, ConcurrentQueue<T>, Stack,
ConcurrentStack<T> o LinkedList<T>), cada elemento contiene un solo valor. En colecciones basadas en la
interfaz IDictionary (como las clases Hashtable y SortedList, y las clases genéricas Dictionary<TKey,TValue> y
SortedList<TKey,TValue>), o en las clases ConcurrentDictionary<TKey,TValue>, cada elemento contiene una clave
y un valor. La clase KeyedCollection<TKey,TItem> es única porque es una lista de valores con claves insertadas en
los valores y, por lo tanto, se comporta como una lista y como un diccionario.
Las colecciones genéricas son la mejor solución para elementos fuertemente tipados. Sin embargo, si su lenguaje
no admite genéricos, el espacio de nombres System.Collections incluye colecciones base, como CollectionBase,
ReadOnlyCollectionBase y DictionaryBase, que son clases base abstractas que se pueden extender para crear
clases de colección fuertemente tipadas. Cuando se requiere un acceso eficaz a una colección multiproceso, utilice
las colecciones genéricas del espacio de nombres System.Collections.Concurrent.
Las colecciones pueden variar en función de cómo se almacenan los elementos, cómo se ordenan, cómo se
realizan las búsquedas y cómo se realizan las comparaciones. La clase Queue y la clase genérica Queue<T>
proporcionan listas de tipo “el primero en entrar es el primero en salir”, mientras que la clase Stack y la clase
genérica Stack<T> proporcionan listas de tipo “el último en entrar es el primero en salir”. La clase SortedList y la
clase genérica SortedList<TKey,TValue> proporcionan versiones ordenadas de la clase Hashtable y de la clase
genérica Dictionary<TKey,TValue>. El acceso a los elementos de Hashtable o de Dictionary<TKey,TValue> solo es
posible mediante la clave del elemento, pero el acceso a los elementos de SortedList o de
KeyedCollection<TKey,TItem> es posible mediante la clave o mediante el índice del elemento. Los índices de todas
las colecciones son de base cero, excepto Array, que permite matrices que no son de base cero.
La característica LINQ to Objects permite usar consultas LINQ para obtener acceso a los objetos en memoria
mientras el tipo de objeto implemente la interfaz IEnumerable o IEnumerable<T>. Las consultas LINQ
proporcionan un patrón común para acceder a los datos; suelen ser más concisas y legibles que los bucles
foreach estándar y proporcionan capacidades de filtrado, ordenación y agrupación. Las consultas LINQ también
pueden mejorar el rendimiento. Para más información, vea LINQ to Objects (C#), LINQ to Objects (Visual Basic) y
Parallel LINQ (PLINQ).
Temas relacionados
T IT L E DESC RIP C IÓ N
Colecciones y estructuras de datos Describe los diversos tipos de colecciones disponibles en .NET
Framework, incluidas las pilas, colas, listas, matrices y
diccionarios.
Tipos de las colecciones Hashtable y Dictionary Describe las características de los tipos de diccionario basado
en hash genéricos y no genéricos.
T IT L E DESC RIP C IÓ N
Referencia
System.Collections
System.Collections.Generic
System.Collections.ICollection
System.Collections.Generic.ICollection<T>
System.Collections.IList
System.Collections.Generic.IList<T>
System.Collections.IDictionary
System.Collections.Generic.IDictionary<TKey,TValue>
Cuándo utilizar colecciones genéricas
16/09/2020 • 7 minutes to read • Edit Online
El uso de colecciones genéricas ofrece la ventaja inmediata de la seguridad de tipos sin necesidad de derivar de un
tipo de colección base ni de implementar miembros específicos de tipo. Los tipos de colección genéricos también
suelen funcionan mejor que los correspondientes tipos de colección no genéricos (y mejor que los tipos que se
derivan de los tipos de colección base no genéricos) cuando los elementos de la colección son tipos de valor. Esto
se debe a que con los genéricos no es necesario realizar una conversión boxing de los elementos.
En programas destinados a .NET Standard 1.0 o una versión posterior, utilice las clases de colección genérica en el
espacio de nombres System.Collections.Concurrent cuando varios subprocesos puedan agregar o quitar
elementos de la colección simultáneamente. Además, cuando busque inmutabilidad, tenga en cuenta las clases de
colección genéricas en el espacio de nombres System.Collections.Immutable.
Los siguientes tipos genéricos corresponden a los tipos de colección existentes:
List<T> es la clase genérica que se corresponde con ArrayList.
Dictionary<TKey,TValue> y ConcurrentDictionary<TKey,TValue> son las clases genéricas que se
corresponden con Hashtable.
Collection<T> es la clase genérica que se corresponde con CollectionBase. Collection<T> puede utilizarse
como una clase base pero, a diferencia de CollectionBase, no es abstracta, lo que hace que sea mucho más
fácil de usar.
ReadOnlyCollection<T> es la clase genérica que se corresponde con ReadOnlyCollectionBase. La colección
ReadOnlyCollection<T> no es abstracta y tiene un constructor que hace más fácil exponer una colección
List<T> existente como de solo lectura.
Las clases genéricas Queue<T>, ConcurrentQueue<T>, ImmutableQueue<T>, ImmutableArray<T>,
SortedList<TKey,TValue> y ImmutableSortedSet<T> se corresponden con las respectivas clases no
genéricas de igual nombre.
Tipos adicionales
Hay varios tipos de colección genéricos que no tienen un tipo homólogo no genérico. Entre esos tipos se incluyen
los siguientes:
LinkedList<T> es una lista vinculada de uso general que proporciona operaciones de eliminación e
inserción O(1).
SortedDictionary<TKey,TValue> es un diccionario ordenado con operaciones de eliminación e inserción
O(log n ), lo que lo convierte en una alternativa útil a SortedList<TKey,TValue>.
KeyedCollection<TKey,TItem> es un híbrido entre una lista y un diccionario, lo que proporciona una manera
de almacenar objetos que contienen sus propias claves.
BlockingCollection<T> implementa una clase de colección con funcionalidad de límite y bloqueo.
ConcurrentBag<T> proporciona una rápida inserción y eliminación de elementos no ordenados.
Generadores inmutables
Si quiere disponer de la funcionalidad de inmutabilidad en la aplicación, el espacio de nombres
System.Collections.Immutable ofrece tipos de colección genéricos que se pueden usar. Todos los tipos de colección
inmutables ofrecen clases Builder que pueden optimizar el rendimiento cuando se realicen varias mutaciones. La
clase Builder agrupa las operaciones en un estado mutable. Cuando se hayan completado todas las mutaciones,
llame al método ToImmutable para "inmovilizar" todos los nodos y crear una colección genérica inmutable, por
ejemplo, una colección ImmutableList<T>.
Se puede crear el objeto Builder llamando al método CreateBuilder() no genérico. En una instancia de Builder ,
se puede llamar a ToImmutable() . Del mismo modo, en la colección Immutable* , se puede llamar a ToBuilder()
para crear una instancia de generador a partir de la colección inmutable genérica. Estos son los distintos tipos de
Builder .
ImmutableArray<T>.Builder
ImmutableDictionary<TKey,TValue>.Builder
ImmutableHashSet<T>.Builder
ImmutableList<T>.Builder
ImmutableSortedDictionary<TKey,TValue>.Builder
ImmutableSortedSet<T>.Builder
LINQ to Objects
La característica LINQ to Objects permite usar consultas LINQ para obtener acceso a los objetos en memoria
mientras el tipo de objeto implemente la interfaz System.Collections.IEnumerable o
System.Collections.Generic.IEnumerable<T> . Las consultas LINQ proporcionan un patrón común para acceder a
los datos; suelen ser más concisas y legibles que los bucles foreach estándar y proporcionan capacidades de
filtrado, ordenación y agrupación. Las consultas LINQ también pueden mejorar el rendimiento. Para más
información, vea LINQ to Objects (C#), LINQ to Objects (Visual Basic) y Parallel LINQ (PLINQ).
Funcionalidad adicional
Algunos de los tipos genéricos tienen funcionalidades que no se encuentran en los tipos de colección no genéricos.
Por ejemplo, la clase List<T> , que se corresponde con la clase ArrayList no genérica, tiene una serie de métodos
que aceptan delegados genéricos, como el delegado Predicate<T> que permite especificar los métodos de
búsqueda en la lista, el delegado Action<T> que representa los métodos que actúan en cada elemento de la lista y
el delegado Converter<TInput,TOutput> que permite definir conversiones entre tipos.
La clase List<T> permite especificar sus propias implementaciones de interfaz genérica IComparer<T> para la
ordenación y búsqueda en la lista. Las clases SortedDictionary<TKey,TValue> y SortedList<TKey,TValue> también
tienen esta capacidad. Además, estas clases le permiten especificar los comparadores cuando se crea la colección.
De forma similar, las clases Dictionary<TKey,TValue> y KeyedCollection<TKey,TItem> le permiten especificar sus
propios comparadores de igualdad.
Vea también
Colecciones y estructuras de datos
Tipos de colección utilizados normalmente
Genéricos
Comparaciones y ordenaciones en colecciones
16/09/2020 • 10 minutes to read • Edit Online
Las clases System.Collections realizan comparaciones en casi todos los procesos implicados en la administración
de colecciones, bien al buscar el elemento que se va a quitar, bien al devolver el valor de un par de clave y valor.
Normalmente, las colecciones usan un comparador de igualdad o un comparador de orden. En las comparaciones
se usan dos constructores.
Comprobación de la igualdad
Los métodos como Contains , IndexOf, LastIndexOfy Remove utilizan un comparador de igualdad para los
elementos de la colección. Si la colección es genérica, se compara la igualdad de los elementos según las siguientes
directrices:
Si el tipo T implementa la interfaz genérica IEquatable<T> , el comparador de igualdad es el método Equals
de dicha interfaz.
Si el tipo T no implementa IEquatable<T>, se utiliza Object.Equals .
Además, algunas sobrecargas de constructores para colecciones de diccionario aceptan una implementación de
IEqualityComparer<T>, que se utiliza para comparar la igualdad de claves. Para ver un ejemplo, consulte el
constructor Dictionary<TKey,TValue> .
using System;
using System.Collections.Generic;
// Write out the parts in the list. This will call the overridden
// ToString method in the Part class.
Console.WriteLine("\nBefore sort:");
parts.ForEach(Console.WriteLine);
parts.ForEach(Console.WriteLine);
/*
Before sort:
ID: 1434 Name: regular seat
ID: 1234 Name: crank arm
ID: 1634 Name: shift lever
ID: 1334 Name:
ID: 1444 Name: banana seat
ID: 1534 Name: cassette
*/
}
}
Imports System.Collections.Generic
' Simple business object. A PartId is used to identify the type of part
' but the part name can change.
Public Class Part
Implements IEquatable(Of Part)
Implements IComparable(Of Part)
Public Property PartName() As String
Get
Return m_PartName
End Get
Set(value As String)
m_PartName = Value
m_PartName = Value
End Set
End Property
Private m_PartName As String
Return name1.CompareTo(name2)
End Function
Return Me.PartId.CompareTo(comparePart.PartId)
End If
End Function
Public Overrides Function GetHashCode() As Integer
Return PartId
End Function
Public Overloads Function Equals(other As Part) As Boolean Implements IEquatable(Of
ListSortVB.Part).Equals
If other Is Nothing Then
Return False
End If
Return (Me.PartId.Equals(other.PartId))
End Function
' Should also override == and != operators.
End Class
Public Class Example
Public Shared Sub Main()
' Create a list of parts.
Dim parts As New List(Of Part)()
' Write out the parts in the list. This will call the overridden
' ToString method in the Part class.
Console.WriteLine(vbLf & "Before sort:")
For Each aPart As Part In parts
Console.WriteLine(aPart)
Next
'
'
' Before sort:
' ID: 1434 Name: regular seat
' ID: 1234 Name: crank arm
' ID: 1234 Name: crank arm
' ID: 1634 Name: shift lever
' ID: 1334 Name:
' ID: 1444 Name: banana seat
' ID: 1534 Name: cassette
'
' After sort by part number:
' ID: 1234 Name: crank arm
' ID: 1334 Name:
' ID: 1434 Name: regular seat
' ID: 1444 Name: banana seat
' ID: 1534 Name: cassette
' ID: 1634 Name: shift lever
'
' After sort by name:
' ID: 1334 Name:
' ID: 1444 Name: banana seat
' ID: 1534 Name: cassette
' ID: 1234 Name: crank arm
' ID: 1434 Name: regular seat
' ID: 1634 Name: shift lever
End Sub
End Class
Vea también
IComparer
IEquatable<T>
IComparer<T>
IComparable
IComparable<T>
Tipos de colecciones ordenadas
16/09/2020 • 3 minutes to read • Edit Online
NOTE
La clase SortedList no genérica devuelve objetos DictionaryEntry cuando se enumera, aunque los dos tipos genéricos
devuelven objetos KeyValuePair<TKey,TValue>.
Las propiedades que devuelven claves y valores se indizan, lo Sin recuperación indizada.
que permite una recuperación indizada eficaz.
Usa menos memoria que SortedDictionary<TKey,TValue>. Usa más memoria que la clase no genérica SortedList y la
clase genérica SortedList<TKey,TValue>.
Para listas ordenadas o diccionarios que deben ser accesibles simultáneamente desde varios subprocesos, se
puede agregar una lógica de ordenación a una clase que deriva de ConcurrentDictionary<TKey,TValue>. Al
considerar la inmutabilidad, los siguientes tipos inmutables correspondientes siguen una semántica de ordenación
similar: ImmutableSortedSet<T> y ImmutableSortedDictionary<TKey,TValue>.
NOTE
Para los valores que contienen sus propias claves (por ejemplo, registros de empleados que contienen un número de id. de
empleado), puede derivar de la clase genérica KeyedCollection<TKey,TItem> para crear una colección con clave que tenga
algunas características de lista y algunas características de diccionario.
A partir de .NET Framework 4, la clase SortedSet<T> proporciona un árbol que mantiene los datos ordenados
después de las inserciones, eliminaciones y búsquedas. Esta clase y la clase HashSet<T> implementan la interfaz
ISet<T>.
Vea también
System.Collections.IDictionary
System.Collections.Generic.IDictionary<TKey,TValue>
ConcurrentDictionary<TKey,TValue>
Tipos de colección utilizados normalmente
Tipos de las colecciones Hashtable y Dictionary
16/09/2020 • 4 minutes to read • Edit Online
Vea también
Hashtable
IDictionary
IHashCodeProvider
Dictionary<TKey,TValue>
System.Collections.Generic.IDictionary<TKey,TValue>
System.Collections.Concurrent.ConcurrentDictionary<TKey,TValue>
Tipos de colección utilizados normalmente
Colecciones seguras para subprocesos
16/09/2020 • 7 minutes to read • Edit Online
.NET Framework 4 introduce el espacio de nombres System.Collections.Concurrent, que incluye varias clases de
colección que son a la vez seguras para subprocesos y escalables. Varios subprocesos pueden agregar o quitar
elementos de estas colecciones sin ningún riesgo y de un modo eficaz, sin requerir una sincronización adicional
en código de usuario. Al escribir un código nuevo, utilice las clases de colección simultáneas siempre que varios
subprocesos se vayan a escribir en la colección de forma simultánea. Si solo está leyendo en una colección
compartida, puede utilizar las clases en el espacio de nombres System.Collections.Generic. Recomendamos no
utilizar clases de colección 1.0 a menos que estén destinadas a .NET Framework 1.1. o un runtime de una versión
anterior.
T IP O DESC RIP C IÓ N
Temas relacionados
T IT L E DESC RIP C IÓ N
Cómo: agregar y quitar elementos de ConcurrentDictionary Describe cómo agregar y quitar los elementos de
ConcurrentDictionary<TKey,TValue>
Cómo: agregar y tomar elementos de forma individual en una Describe cómo agregar y recuperar elementos de una
clase BlockingCollection colección de bloqueo sin utilizar el enumerador de solo
lectura.
Cómo: agregar la funcionalidad de límite y bloqueo a una Describe cómo utilizar cualquier clase de colección como
colección mecanismo de almacenamiento subyacente para una
colección IProducerConsumerCollection<T>.
Cómo: utilizar ForEach para quitar elementos de Describe cómo utilizar foreach , ( For Each en Visual Basic)
BlockingCollection para quitar todos los elementos en una colección de bloqueo.
Cómo: usar matrices de colecciones de bloqueo en una Describe cómo utilizar varias colecciones de bloqueo para
canalización implementar una canalización al mismo tiempo.
T IT L E DESC RIP C IÓ N
Cómo: crear un grupo de objetos usando ConcurrentBag Muestra cómo usar un controlador simultáneo para mejorar
el rendimiento en escenarios donde puede reutilizar objetos
en lugar de crear continuamente otros nuevos.
Referencia
System.Collections.Concurrent
Delegados y expresiones lambda
16/09/2020 • 7 minutes to read • Edit Online
Los delegados definen un tipo que especifica una firma de método concreta. Se puede asignar un método (estático
o de instancia) que satisfaga esta firma a una variable de ese tipo y luego llamarlo directamente (con los
argumentos adecuados) o pasarlo como argumento a otro método y después llamarlo. El siguiente ejemplo
muestra el uso de delegados.
using System;
using System.Linq;
Console.WriteLine(rev("a string"));
}
}
En la línea public delegate string Reverse(string s); se crea un tipo delegado de una firma determinada, en
este caso un método que toma un parámetro de cadena y luego devuelve un parámetro de cadena.
El método static string ReverseString(string s) , que tiene exactamente la misma firma que el tipo delegado
definido, implementa el delegado.
En la línea Reverse rev = ReverseString; se muestra que se puede asignar un método a una variable del tipo de
delegado correspondiente.
En la línea Console.WriteLine(rev("a string")); se muestra cómo se usa una variable de un tipo de delegado
para invocar al delegado.
Para simplificar el proceso de desarrollo, .NET incluye un conjunto de tipos delegados que los programadores
pueden volver a usar para no tener que crear nuevos tipos. Estos tipos son Func<> , Action<> y Predicate<> , y se
pueden usar sin necesidad de definir nuevos tipos de delegado. Hay algunas diferencias entre los tres tipos que
tienen que ver con la forma en que se van a usar:
Action<> se usa cuando es necesario realizar una acción mediante los argumentos del delegado. El método
que encapsula no devuelve ningún valor.
Func<> se usa normalmente cuando se tiene una transformación a mano; es decir, cuando se necesita
transformar los argumentos del delegado en un resultado diferente. Las proyecciones son un buen ejemplo. El
método que encapsula devuelve un valor especificado.
Predicate<> se usa cuando es necesario determinar si el argumento cumple la condición del delegado.
También se puede escribir como Func<T, bool> , lo que significa que el método devuelve un valor booleano.
Ahora podemos tomar el ejemplo anterior y volver a escribirlo mediante el delegado Func<> en lugar de un tipo
personalizado. El programa seguirá ejecutándose de la misma forma.
using System;
using System.Linq;
Console.WriteLine(rev("a string"));
}
}
En este ejemplo sencillo, tener un método definido fuera del método Main parece un poco superfluo. .NET
Framework 2.0 ha introducido el concepto de delegados anónimos, que permiten crear delegados "insertados" sin
necesidad de especificar ningún otro tipo o método.
En el ejemplo siguiente, un delegado anónimo filtra una lista solo por los números pares y, después, los imprime
en la consola.
using System;
using System.Collections.Generic;
Como puede ver, el cuerpo del delegado es simplemente un conjunto de expresiones, como cualquier otro
delegado. Pero en lugar de ser una definición independiente, se ha introducido ad hoc en la llamada al método
List<T>.FindAll.
Pero incluso con este enfoque, sigue habiendo mucho código del que es posible deshacerse. Aquí es donde entran
en juego las expresiones lambda. Las expresiones lambda, o las "lambda", para abreviar, se presentaron en C# 3.0
como uno de los bloques de compilación fundamentales de Language Integrated Query (LINQ). Constituyen una
sintaxis más cómoda para el uso de delegados. Declaran una firma y un cuerpo de método, pero no tienen una
identidad formal propia, a menos que se asignen a un delegado. A diferencia de los delegados, se pueden asignar
directamente como lado izquierdo del registro de eventos, o bien en distintas cláusulas y métodos de LINQ.
Puesto que una expresión lambda es solo otra forma de especificar un delegado, debería ser posible volver a
escribir el ejemplo anterior para usar una expresión lambda en lugar de un delegado anónimo.
using System;
using System.Collections.Generic;
En el ejemplo anterior, la expresión lambda que se usa es i => i % 2 == 0 . De nuevo, constituyen una sintaxis más
cómoda para el uso de delegados. Lo que sucede en segundo plano es similar a lo que ocurre con el delegado
anónimo.
De nuevo, las expresiones lambda son solo delegados, lo que significa que se pueden usar como controlador de
eventos sin problemas, como se muestra en el siguiente fragmento de código.
public MainWindow()
{
InitializeComponent();
En este contexto, el operador += se usa para suscribirse a un evento. Para obtener más información, vea
Procedimiento para suscribir y cancelar la suscripción a eventos.
Los eventos de .NET se basan en el modelo de delegado. El modelo de delegado sigue el patrón de diseño del
observador, que permite que un suscriptor se registre con un proveedor y reciba notificaciones de él. El emisor de
un evento inserta una notificación de que se ha producido un evento, y un receptor de eventos recibe la
notificación y define una respuesta a la misma. En este artículo se describen los componentes principales del
modelo de delegado, cómo consumir eventos en las aplicaciones y cómo implementar eventos en el código.
Para obtener información sobre el control de eventos en las aplicaciones de la Tienda de Windows 8.x, vea
Introducción a eventos y eventos enrutados.
Events
Un evento es un mensaje que envía un objeto cuando ocurre una acción. La acción podría deberse a la interacción
del usuario, como hacer clic en un botón, o podría derivarse de cualquier otra lógica del programa, como el
cambio del valor de una propiedad. El objeto que provoca el evento se conoce como emisor del evento. El emisor
del evento no sabe qué objeto o método recibirá (controlará) los eventos que genera. El evento normalmente es
un miembro del emisor del evento; por ejemplo, el evento Click es un miembro de la clase Button, y el evento
PropertyChanged es un miembro de la clase que implementa la interfaz INotifyPropertyChanged.
Para definir un evento, se utiliza la palabra clave event de C# o Event de Visual Basic en la signatura de la clase
de eventos y se especifica el tipo de delegado para el evento. Los delegados se describen en la sección siguiente.
Normalmente, para generar un evento, se agrega un método marcado como protected y virtual (en C#) o
Protected y Overridable (en Visual Basic). Asigne a este método el nombre On EventName; por ejemplo,
OnDataReceived . El método debe tomar un parámetro que especifica un objeto de datos de evento, que es un
objeto de tipo EventArgs o un tipo derivado. Este método se proporciona para permitir que las clases derivadas
reemplacen la lógica para generar el evento. Una clase derivada siempre debería llamar al método On EventName
de la clase base para asegurarse de que los delegados registrados reciben el evento.
En el ejemplo siguiente se muestra cómo declarar un evento denominado ThresholdReached . El evento está
asociado al delegado EventHandler y se genera en un método denominado OnThresholdReached .
class Counter
{
public event EventHandler ThresholdReached;
Delegados
Un delegado es un tipo que tiene una referencia a un método. Un delegado se declara con una signatura que
muestra el tipo de valor devuelto y los parámetros para los métodos a los que hace referencia, y únicamente
puede contener referencias a los métodos que coinciden con su signatura. Por lo tanto, un delegado equivale a un
puntero a función con seguridad o a una devolución de llamada. Una declaración de delegado es suficiente para
definir una clase de delegado.
Los delegados tienen muchos usos en .NET. En el contexto de los eventos, un delegado es un intermediario (o un
mecanismo de puntero) entre el origen del evento y el código que lo controla. Para asociar un delegado a un
evento se incluye el tipo de delegado en la declaración del evento, como se muestra en el ejemplo de la sección
anterior. Para obtener más información sobre los delegados, vea la clase Delegate.
.NET proporciona los delegados EventHandler y EventHandler<TEventArgs> que admiten la mayoría de los
escenarios de eventos. Use el delegado EventHandler para todos los eventos que no incluyen datos de evento. Use
el delegado EventHandler<TEventArgs> para los eventos que incluyen datos sobre el evento. Estos delegados no
tienen ningún valor de tipo devuelto y toman dos parámetros (un objeto para el origen del evento y un objeto
para los datos del evento).
Los delegados son de multidifusión, lo que significa que pueden guardar referencias a más de un método de
control de eventos. Para obtener información detallada, vea la página de referencia de Delegate. Los delegados
permiten realizar un control de eventos más flexible y detallado. Un delegado actúa como remitente de eventos de
la clase que genera el evento y mantiene una lista de los controladores registrados para el evento.
Para los escenarios en que no funcionan los delegados EventHandler y EventHandler<TEventArgs>, puede definir
un delegado. Los escenarios para los es necesario definir un delegado son poco habituales, como cuando se debe
ejecutar código que no reconoce genéricos. Los delegados se marcan con la palabra clave delegate de C# y
Delegate de Visual Basic en la declaración. En el ejemplo siguiente se muestra cómo declarar un delegado
denominado ThresholdReachedEventHandler .
Datos de evento
Los datos asociados a un evento se pueden proporcionar a través de una clase de datos de evento. .NET
proporciona muchas clases de datos de evento que puede utilizar en las aplicaciones. Por ejemplo, la clase
SerialDataReceivedEventArgs es la clase de datos de evento del evento SerialPort.DataReceived. En .NET se sigue
un patrón de nombres que consiste en finalizar todas las clases de datos de evento con EventArgs . Para
determinar qué clase de datos de evento está asociada a un evento, basta con examinar el delegado del evento.
Por ejemplo, el delegado SerialDataReceivedEventHandler incluye entre sus parámetros la clase
SerialDataReceivedEventArgs.
La clase EventArgs es el tipo base para todas las clases de datos de evento. EventArgs también es la clase que se
usa cuando un evento no tiene datos asociados. Cuando cree un evento que solo sirva para notificar a otras clases
que algo ha sucedido y que no necesite pasar ningún dato, incluya la clase EventArgs como segundo parámetro
del delegado. Puede pasar el valor EventArgs.Empty cuando no se proporciona ningún dato. El delegado
EventHandler incluye la clase EventArgs como parámetro.
Si desea crear una clase de datos de evento personalizada, cree una clase que se derive de EventArgs y, a
continuación, especifique los miembros que sean necesarios para pasar los datos relacionados con el evento.
Normalmente, debe usar el mismo patrón de asignación de nombres que se usa en .NET y finalizar el nombre de
la clase de los datos de evento con EventArgs .
En el ejemplo siguiente se muestra una clase de datos de evento denominada ThresholdReachedEventArgs .
Contiene propiedades específicas del evento que se genera.
Controladores de eventos
Para responder a un evento, se define un método controlador de eventos en el receptor de eventos. Este método
debe coincidir con la signatura del delegado del evento que se está controlando. En el controlador de eventos, se
realizan las acciones que es necesario llevar a cabo cuando se genera el evento, como recopilar los datos
proporcionados por el usuario cuando este hace clic en un botón. Para recibir notificaciones cuando se genera el
evento, el método controlador de eventos debe suscribirse al evento.
En el ejemplo siguiente se muestra un método de control de eventos denominado c_ThresholdReached que
coincide con la signatura del delegado EventHandler. El método se suscribe al evento ThresholdReached .
class Program
{
static void Main()
{
var c = new Counter();
c.ThresholdReached += c_ThresholdReached;
Sub Main()
Dim c As New Counter()
AddHandler c.ThresholdReached, AddressOf c_ThresholdReached
Temas relacionados
T IT L E DESC RIP C IÓ N
Cómo: Provocar y utilizar eventos Contiene ejemplos de cómo generar y consumir eventos.
Cómo: Controlar varios eventos mediante las propiedades de Muestra cómo utilizar propiedades de evento para controlar
evento varios eventos.
Modelo de diseño de observador Describe el patrón de diseño que permite que un suscriptor se
registre con un proveedor y reciba notificaciones de dicho
proveedor.
Cómo: Consumir eventos en una aplicación de formularios Muestra cómo controlar un evento generado por un control
Web Forms de formularios Web Forms.
Vea también
EventHandler
EventHandler<TEventArgs>
EventArgs
Delegate
Eventos (Visual Basic)
Eventos (Guía de programación de C#)
Introducción a eventos y eventos enrutados (aplicaciones de UWP)
Procedimiento para provocar y consumir eventos
16/09/2020 • 6 minutes to read • Edit Online
En los ejemplos de este tema se muestra cómo trabajar con eventos. Se incluyen ejemplos del delegado
EventHandler, el delegado EventHandler<TEventArgs> y un delegado personalizado, para ilustrar eventos con y sin
datos.
En los ejemplos se usan los conceptos descritos en el artículo Eventos.
Ejemplo
En el primer ejemplo se muestra cómo generar y consumir un evento que no tiene datos. Contiene una clase
denominada Counter que tiene un evento denominado ThresholdReached . Este evento se genera cuando un valor
de contador iguala o supera el valor de umbral. El delegado EventHandler está asociado al evento, porque no se
proporcionan datos de evento.
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Counter c = new Counter(new Random().Next(10));
c.ThresholdReached += c_ThresholdReached;
class Counter
{
private int threshold;
private int total;
Sub Main()
Dim c As Counter = New Counter(New Random().Next(10))
AddHandler c.ThresholdReached, AddressOf c_ThresholdReached
Class Counter
Private threshold As Integer
Private total As Integer
Ejemplo
En el ejemplo siguiente se muestra cómo generar y consumir un evento que proporciona datos. El delegado
EventHandler<TEventArgs> está asociado al evento, y se proporciona una instancia de un objeto de datos de
eventos personalizados.
using namespace System;
public:
Counter() {};
Counter(int passedThreshold)
{
threshold = passedThreshold;
}
void Add(int x)
{
total += x;
if (total >= threshold) {
ThresholdReachedEventArgs^ args = gcnew ThresholdReachedEventArgs();
args->Threshold = threshold;
args->TimeReached = DateTime::Now;
OnThresholdReached(args);
}
}
protected:
virtual void OnThresholdReached(ThresholdReachedEventArgs^ e)
{
ThresholdReached(this, e);
}
};
void main()
{
Counter^ c = gcnew Counter((gcnew Random())->Next(10));
c->ThresholdReached += gcnew EventHandler<ThresholdReachedEventArgs^>(SampleHandler::c_ThresholdReached);
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Counter c = new Counter(new Random().Next(10));
c.ThresholdReached += c_ThresholdReached;
class Counter
{
private int threshold;
private int total;
Sub Main()
Dim c As Counter = New Counter(New Random().Next(10))
AddHandler c.ThresholdReached, AddressOf c_ThresholdReached
Class Counter
Private threshold As Integer
Private total As Integer
Class ThresholdReachedEventArgs
Inherits EventArgs
Ejemplo
En el ejemplo siguiente se muestra cómo declarar un delegado para un evento. El delegado se denomina
ThresholdReachedEventHandler . Esto es simplemente una ilustración. Normalmente no es necesario declarar un
delegado para un evento, porque se puede usar el delegado EventHandler o EventHandler<TEventArgs>. Solo
debe declararse un delegado en escenarios poco habituales, como cuando se facilita el uso de una clase a código
heredado que no puede usar elementos genéricos.
using System;
namespace ConsoleApplication1
{
class Program
class Program
{
static void Main(string[] args)
{
Counter c = new Counter(new Random().Next(10));
c.ThresholdReached += c_ThresholdReached;
class Counter
{
private int threshold;
private int total;
Sub Main()
Dim c As Counter = New Counter(New Random().Next(10))
AddHandler c.ThresholdReached, AddressOf c_ThresholdReached
Class Counter
Private threshold As Integer
Private total As Integer
Vea también
Eventos
Procedimiento para controlar varios eventos
mediante las propiedades de evento
16/09/2020 • 6 minutes to read • Edit Online
Para poder utilizar propiedades de evento, hay que definirlas en la clase que provoca los eventos y, a continuación,
establecer los delegados de las propiedades de evento en las clases que controlan los eventos. Para implementar
varias propiedades de evento en una clase, esta clase deberá almacenar internamente y mantener el delegado
definido para cada evento. Para hacerlo, uno de los enfoques típicos consiste en implementar una colección de
delegados que se indice por medio de una clave de evento.
Para almacenar los delegados de cada evento, puede utilizar la clase EventHandlerList o implementar su propia
colección. La clase de colección debe proporcionar métodos para establecer, obtener acceso y recuperar el
delegado del controlador de eventos basándose en la clave del evento. Por ejemplo, se puede utilizar una clase
Hashtable o derivar una clase personalizada a partir de la clase DictionaryBase. No es necesario exponer los
detalles de implementación de la colección de delegados fuera de la clase.
Cada una de las propiedades de evento de la clase define un método de descriptor de acceso add y un método de
descriptor de acceso remove. El descriptor de acceso add de una propiedad de evento agrega la instancia de
delegado de entrada a la colección de delegados. El descriptor de acceso remove de una propiedad de evento
quita la instancia de delegado de entrada de la colección de delegados. Los descriptores de acceso de las
propiedades de eventos utilizan la clave predefinida de la propiedad de evento para agregar y quitar instancias en
la colección de delegados.
Para controlar varios eventos mediante las propiedades de evento
1. Defina una colección de delegados dentro de la clase que provoca los eventos.
2. Defina una clave para cada evento.
3. Defina las propiedades de evento de la clase que provoca los eventos.
4. Utilice la colección de delegados para implementar los métodos de descriptor de acceso add y remove de
las propiedades de evento.
5. Utilice las propiedades de evento públicas para agregar y quitar delegados de controlador de eventos en las
clases que controlan los eventos.
Ejemplo
En el siguiente ejemplo de C#, se implementan las propiedades de evento MouseDown y MouseUp mediante el uso
de EventHandlerList para almacenar el delegado de cada evento. Las palabras clave de las construcciones de
propiedades de evento están en negrita.
// The class SampleControl defines two event properties, MouseUp and MouseDown.
ref class SampleControl : Component
{
// :
// Define other control methods and properties.
// :
private:
// Define a unique key for each event.
static Object^ mouseDownEventKey = gcnew Object();
static Object^ mouseUpEventKey = gcnew Object();
Vea también
System.ComponentModel.EventHandlerList
Eventos
Control.Events
Cómo: Declarar eventos personalizados para conservar memoria
Modelo de diseño de observador
16/09/2020 • 21 minutes to read • Edit Online
El modelo de diseño de observador permite que un suscriptor se registre con un proveedor y reciba notificaciones
de dicho proveedor. Este modelo es adecuado para cualquier escenario que requiera notificaciones push. El
modelo define un proveedor (también conocido como un tema o una observable) y cero, uno o más
observadores. Los observadores se registran con el proveedor y siempre que se produce una condición
predefinida, un evento o un cambio de estado, el proveedor notifica automáticamente a todos los observadores
mediante la llamada a uno de sus métodos. En esta llamada al método, el proveedor puede proporcionar también
información sobre el estado actual a los observadores. En .NET Framework, el modelo de diseño de observador se
aplica con la implementación de las interfaces genéricas System.IObservable<T> y System.IObserver<T>. El
parámetro de tipo genérico representa el tipo que proporciona información de notificación.
using System;
using System.Collections.Generic;
Friend Sub New(ByVal flight As Integer, ByVal from As String, ByVal carousel As Integer)
Me.flightNo = flight
Me.origin = from
Me.location = carousel
End Sub
Una clase BaggageHandler es la responsable de recibir la información sobre la llegada de los vuelos y las cintas de
recogida de equipaje. Internamente, mantiene dos colecciones:
observers : colección de clientes que recibirán información actualizada.
flights : colección de los vuelos y sus cintas asignadas.
Ambas colecciones están representadas por objetos List<T> genéricos de los que se crean instancias en el
constructor de clase BaggageHandler . El código fuente de la clase BaggageHandler se muestra en el ejemplo
siguiente.
public class BaggageHandler : IObservable<BaggageInfo>
{
private List<IObserver<BaggageInfo>> observers;
private List<BaggageInfo> flights;
public BaggageHandler()
{
observers = new List<IObserver<BaggageInfo>>();
flights = new List<BaggageInfo>();
}
flightsToRemove.Clear();
}
}
observers.Clear();
}
}
Public Class BaggageHandler : Implements IObservable(Of BaggageInfo)
Public Sub BaggageStatus(ByVal flightNo As Integer, ByVal from As String, ByVal carousel As Integer)
Dim info As New BaggageInfo(flightNo, from, carousel)
Los clientes que deseen recibir información actualizada llaman al método BaggageHandler.Subscribe . Si el cliente
no se ha suscrito previamente a las notificaciones, en la colección de observers se agrega una referencia a la
implementación de IObserver<T> del cliente.
Se puede llamar al método BaggageHandler.BaggageStatus sobrecargado para indicar que el equipaje de un vuelo
se está descargando o ya no se está descargando. En el primer caso, se pasa al método un número de vuelo, el
aeropuerto de origen del vuelo y la cinta en la que se está descargando el equipaje. En el segundo caso, solo se
pasa al método un número de vuelo. Para el equipaje que se está descargado, el método comprueba si la
información BaggageInfo que se pasa al método existe en la colección flights . Si no es así, el método agrega la
información y llama al método OnNext de cada observador. Para los vuelos cuyo equipaje ya no se está
descargando, el método comprueba si la información del vuelo se almacena en la colección flights . Si es así, el
método llama al método OnNext de cada observador y quita el objeto BaggageInfo de la colección flights .
Una vez que aterrice el último vuelo del día y se procese su equipaje, se llama al método
BaggageHandler.LastBaggageClaimed . Este método llama al método OnCompleted de cada observador para indicar
que se han completado todas las notificaciones y, a continuación, borra la colección observers .
El método Subscribe del proveedor devuelve una implementación IDisposable que permite a los observadores
dejar de recibir notificaciones antes de que se llame al método OnCompleted. El código fuente para esta clase
Unsubscriber(Of BaggageInfo) se muestra en el ejemplo siguiente. Cuando se crea una instancia de la clase en el
método BaggageHandler.Subscribe , se pasa una referencia a la colección observers y una referencia al observador
que se agrega a la colección. Estas referencias se asignan a variables locales. Cuando se llama al método Dispose
del objeto, comprueba si el observador todavía existe en la colección observers y, si es así, se quita el observador.
Friend Sub New(ByVal observers As List(Of IObserver(Of BaggageInfo)), ByVal observer As IObserver(Of
BaggageInfo))
Me._observers = observers
Me._observer = observer
End Sub
using System;
using System.Collections.Generic;
this.name = name;
}
// Update information.
public virtual void OnNext(BaggageInfo info)
{
bool updated = false;
flightsToRemove.Clear();
}
else {
// Add flight if it does not exist in the collection.
string flightInfo = String.Format(fmt, info.From, info.FlightNumber, info.Carousel);
if (! flightInfos.Contains(flightInfo)) {
flightInfos.Add(flightInfo);
flightInfos.Add(flightInfo);
updated = true;
}
}
if (updated) {
flightInfos.Sort();
Console.WriteLine("Arrivals information from {0}", this.name);
foreach (var flightInfo in flightInfos)
Console.WriteLine(flightInfo);
Console.WriteLine();
}
}
}
Me.name = name
End Sub
' Flight has unloaded its baggage; remove from the monitor.
If info.Carousel = 0 Then
Dim flightsToRemove As New List(Of String)
Dim flightNo As String = String.Format("{0,5}", info.FlightNumber)
For Each flightInfo In flightInfos
If flightInfo.Substring(21, 5).Equals(flightNo) Then
flightsToRemove.Add(flightInfo)
updated = True
End If
Next
For Each flightToRemove In flightsToRemove
flightInfos.Remove(flightToRemove)
Next
flightsToRemove.Clear()
Else
' Add flight if it does not exist in the collection.
Dim flightInfo As String = String.Format(fmt, info.From, info.FlightNumber, info.Carousel)
If Not flightInfos.Contains(flightInfo) Then
flightInfos.Add(flightInfo)
updated = True
End If
End If
If updated Then
flightInfos.Sort()
Console.WriteLine("Arrivals information from {0}", Me.name)
For Each flightInfo In flightInfos
Console.WriteLine(flightInfo)
Next
Console.WriteLine()
End If
End Sub
End Class
La clase ArrivalsMonitor contiene los métodos Subscribe y Unsubscribe . El método Subscribe permite que la
clase guarde la implementación de IDisposable devuelta por la llamada a Subscribe a una variable privada. El
método Unsubscribe permite que la clase cancele la suscripción a notificaciones mediante una llamada a la
implementación de Dispose del proveedor. ArrivalsMonitor también proporciona implementaciones de los
métodos OnNext, OnError y OnCompleted. Tan solo la implementación de OnNext contiene una cantidad
significativa de código. El método funciona con un objeto List<T> privado, ordenado y genérico que posee
información sobre los aeropuertos de origen de los vuelos de llegada y las cintas en las que está disponible el
correspondiente equipaje. Si la clase BaggageHandler notifica la llegada de un nuevo vuelo, la implementación del
método OnNext agrega información sobre ese vuelo a la lista. Si la clase BaggageHandler informa de que el
equipaje del vuelo se ha descargado, el método OnNext quita este vuelo de la lista. Cada vez que se produce un
cambio, la lista se ordena y se muestra en la consola.
El ejemplo siguiente contiene el punto de entrada de la aplicación que crea una instancia de la clase
BaggageHandler , así como dos instancias de la clase ArrivalsMonitor , y usa el método
BaggageHandler.BaggageStatus para agregar y quitar información sobre los vuelos de llegada. En cada caso, los
observadores reciben actualizaciones y muestran correctamente la información de recogida de equipaje.
using System;
using System.Collections.Generic;
provider.BaggageStatus(712, "Detroit", 3)
observer1.Subscribe(provider)
provider.BaggageStatus(712, "Kalamazoo", 3)
provider.BaggageStatus(400, "New York-Kennedy", 1)
provider.BaggageStatus(712, "Detroit", 3)
observer2.Subscribe(provider)
provider.BaggageStatus(511, "San Francisco", 2)
provider.BaggageStatus(712)
observer2.Unsubscribe()
provider.BaggageStatus(400)
provider.LastBaggageClaimed()
End Sub
End Module
' The example displays the following output:
' Arrivals information from BaggageClaimMonitor1
' Detroit 712 3
'
' Arrivals information from BaggageClaimMonitor1
' Detroit 712 3
' Kalamazoo 712 3
'
' Arrivals information from BaggageClaimMonitor1
' Detroit 712 3
' Kalamazoo 712 3
' New York-Kennedy 400 1
'
' Arrivals information from SecurityExit
' Detroit 712 3
'
' Arrivals information from SecurityExit
' Detroit 712 3
' Kalamazoo 712 3
'
' Arrivals information from SecurityExit
' Detroit 712 3
' Kalamazoo 712 3
' New York-Kennedy 400 1
'
' Arrivals information from BaggageClaimMonitor1
' Detroit 712 3
' Kalamazoo 712 3
' New York-Kennedy 400 1
' San Francisco 511 2
'
' Arrivals information from SecurityExit
' Detroit 712 3
' Kalamazoo 712 3
' New York-Kennedy 400 1
' San Francisco 511 2
'
' Arrivals information from BaggageClaimMonitor1
' New York-Kennedy 400 1
' San Francisco 511 2
'
' Arrivals information from SecurityExit
' New York-Kennedy 400 1
' San Francisco 511 2
'
' Arrivals information from BaggageClaimMonitor1
' San Francisco 511 2
Temas relacionados
T IT L E DESC RIP C IÓ N
Procedimientos recomendados para modelos de diseño de Describe los procedimientos recomendados que deben
observador adoptarse en el desarrollo de aplicaciones que implementan el
modelo de diseño de observador.
En .NET Framework, el patrón de diseño de observador se implementa como un conjunto de interfaces. La interfaz
System.IObservable<T> representa al proveedor de datos, que también es responsable de proporcionar una
implementación IDisposable que permite a los observadores cancelar la suscripción a las notificaciones. La interfaz
System.IObserver<T> representa al observador. En este tema se describen los procedimientos recomendados que
los desarrolladores deben seguir al implementar el patrón de diseño de observador con estas interfaces.
Subprocesos
Normalmente, para implementar el método IObservable<T>.Subscribe, un proveedor agrega un observador
determinado a una lista de suscriptores que se representa mediante un objeto de colección; por su parte, para
implementar el método IDisposable.Dispose, el proveedor quita un observador determinado de la lista de
suscriptores. Un observador puede llamar a estos métodos en cualquier momento. Además, dado que el contrato
de proveedor/observador no especifica quién es el responsable de cancelar la suscripción después del método de
devolución de llamada IObserver<T>.OnCompleted, el proveedor y el observador pueden intentar quitar el mismo
miembro de la lista. Debido a esta posibilidad, ambos métodos Subscribe y Dispose deben ser seguros para
subprocesos. Normalmente, esto implica el uso de una colección simultánea o un bloqueo. Las implementaciones
que no son seguras para subprocesos deben documentar explícitamente que no lo son.
Las posibles garantías adicionales deben especificarse en un nivel por encima del contrato de
proveedor/observador. Los implementadores deben dejar claro que imponen requisitos adicionales para evitar
confusiones al usuario sobre el contrato de observador.
Recomendaciones adicionales
Intentar anular el registro en el método IObservable<T>.Subscribe puede producir una referencia nula. Por lo
tanto, se recomienda evitar esta práctica.
Aunque es posible adjuntar un observador a varios proveedores, el patrón recomendado consiste en adjuntar una
instancia de IObserver<T> a una única instancia de IObservable<T>.
Vea también
Modelo de diseño de observador
Implementar un observador
Implementar un proveedor
Cómo: Implementar un proveedor
16/09/2020 • 10 minutes to read • Edit Online
El modelo de diseño de observador requiere una división entre un proveedor, que controla los datos y envía
notificaciones, y uno o varios observadores, que reciben notificaciones (devoluciones de llamada) del proveedor.
En este tema se describe cómo crear un proveedor. En un tema relacionado, Cómo: Implementar un observador, se
describe cómo crear un observador.
Para crear un proveedor
1. Defina los datos de los que el proveedor es responsable de enviar a los observadores. Aunque el proveedor
y los datos que envía a los observadores pueden ser de un solo tipo, suelen representarse con tipos
distintos. Por ejemplo, en una aplicación de supervisión de temperatura, la estructura Temperature define
los datos que el proveedor (representado por la clase TemperatureMonitor definida en el paso siguiente)
controla y al que se suscriben los observadores.
using System;
using System;
using System.Collections.Generic;
Imports System.Collections.Generic
3. Determine cómo el proveedor almacenará las referencias a los observadores, para que cada observador
reciba una notificación cuando sea oportuno. Lo más común es que, para este propósito, se use un objeto
de colección como un objeto List<T> genérico. En el ejemplo siguiente se define un objeto List<T> privado
del que se crea una instancia en el constructor de clases TemperatureMonitor .
using System;
using System.Collections.Generic;
public TemperatureMonitor()
{
observers = new List<IObserver<Temperature>>();
}
Imports System.Collections.Generic
4. Defina una implementación IDisposable que el proveedor pueda devolver a los suscriptores, para que
puedan dejar de recibir notificaciones en cualquier momento. En el ejemplo siguiente se define una clase
Unsubscriber anidada de la que se pasa una referencia a la colección de suscriptores y al suscriptor cuando
se crea la instancia de la clase. Este código permite que el suscriptor llame a la implementación
IDisposable.Dispose del objeto para quitarse de la colección de suscriptores.
private class Unsubscriber : IDisposable
{
private List<IObserver<Temperature>> _observers;
private IObserver<Temperature> _observer;
6. Notifique a los observadores según proceda con una llamada a sus implementaciones
IObserver<T>.OnNext, IObserver<T>.OnError y IObserver<T>.OnCompleted. En algunos casos, un
proveedor no puede llamar al método OnError cuando se produce un error. Por ejemplo, el método
GetTemperature siguiente simula un monitor que lee los datos de temperatura cada cinco segundos y
notifica a los observadores si la temperatura ha cambiado por al menos 1 grado desde la lectura anterior. Si
el dispositivo no informa de una temperatura (es decir, si su valor es null), el proveedor notifica a los
observadores que la transmisión está completa. Tenga en cuenta que, además de llamar al método
OnCompleted de cada observador, el método GetTemperature borra la colección List<T>. En este caso, el
proveedor no realiza ninguna llamada al método OnError de sus observadores.
observers.Clear();
break;
}
}
}
If temp.HasValue Then
If start OrElse Math.Abs(temp.Value - previous.Value) >= 0.1 Then
Dim tempData As New Temperature(temp.Value, Date.Now)
For Each observer In observers
observer.OnNext(tempData)
Next
previous = temp
If start Then start = False
End If
Else
For Each observer In observers.ToArray()
If observer IsNot Nothing Then observer.OnCompleted()
Next
observers.Clear()
Exit For
End If
Next
End Sub
Ejemplo
El siguiente ejemplo contiene el código fuente completo para definir una implementación IObservable<T> para
una aplicación de supervisión de temperatura. Incluye la estructura Temperature , que son los datos enviados a los
observadores, y la clase TemperatureMonitor , que es la implementación IObservable<T>.
using System.Threading;
using System;
using System.Collections.Generic;
public TemperatureMonitor()
{
observers = new List<IObserver<Temperature>>();
}
observers.Clear();
break;
}
}
}
}
Imports System.Threading
Imports System.Collections.Generic
Public Sub New(ByVal observers As List(Of IObserver(Of Temperature)), ByVal observer As IObserver(Of
Temperature))
Me._observers = observers
Me._observer = observer
End Sub
If temp.HasValue Then
If start OrElse Math.Abs(temp.Value - previous.Value) >= 0.1 Then
Dim tempData As New Temperature(temp.Value, Date.Now)
For Each observer In observers
observer.OnNext(tempData)
Next
previous = temp
If start Then start = False
End If
Else
For Each observer In observers.ToArray()
If observer IsNot Nothing Then observer.OnCompleted()
Next
observers.Clear()
Exit For
End If
Next
End Sub
End Class
Vea también
IObservable<T>
Modelo de diseño de observador
Implementar un observador
Procedimientos recomendados para modelos de diseño de observador
Procedimiento para implementar un observador
16/09/2020 • 5 minutes to read • Edit Online
El modelo de diseño de observador requiere una división entre un observador, que registra notificaciones, y un
proveedor, que controla los datos y envía notificaciones a uno o varios observadores. En este tema se describe
cómo crear un observador. En un tema relacionado, Cómo: Implementar un proveedor, se describe cómo crear un
proveedor.
Para crear un observador
1. Defina el observador, que es un tipo que implementa la interfaz System.IObserver<T>. Por ejemplo, el
código siguiente define un tipo llamado TemperatureReporter , que es una implementación
System.IObserver<T> construida con un argumento de tipo genérico de Temperature .
2. Si el observador puede dejar de recibir notificaciones antes de que el proveedor llame a su implementación
IObserver<T>.OnCompleted, defina una variable privada que contenga la implementación IDisposable
devuelta por el método IObservable<T>.Subscribe del proveedor. También debe definir un método de
suscripción que llame al método Subscribe del proveedor y almacene el objeto IDisposable devuelto. Por
ejemplo, el código siguiente define una variable privada denominada unsubscriber y define un método
Subscribe que llama al método Subscribe del proveedor y asigna el objeto devuelto a la variable
unsubscriber .
3. Defina un método que permita al observador dejar de recibir notificaciones antes de que el proveedor
llame a su implementación IObserver<T>.OnCompleted, en caso de que esta característica sea necesaria.
En el ejemplo siguiente, se define un método Unsubscribe .
public virtual void Unsubscribe()
{
unsubscriber.Dispose();
}
Ejemplo
El siguiente ejemplo contiene el código fuente completo para la clase TemperatureReporter , que ofrece la
implementación IObserver<T> de una aplicación de supervisión de temperatura.
public class TemperatureReporter : IObserver<Temperature>
{
private IDisposable unsubscriber;
private bool first = true;
private Temperature last;
Vea también
IObserver<T>
Modelo de diseño de observador
Cómo: Implementar un proveedor
Procedimientos recomendados para modelos de diseño de observador
Controlar y generar excepciones en .NET
16/09/2020 • 5 minutes to read • Edit Online
Las aplicaciones tienen que ser capaces de controlar de forma coherente los errores que se producen durante la
ejecución. .NET proporciona un modelo para notificar errores a las aplicaciones de manera uniforme: las
operaciones .NET indican errores iniciando excepciones.
Excepciones
Una excepción es cualquier condición de error o comportamiento inesperado que encuentra un programa en
ejecución. Las excepciones pueden iniciarse debido a un error en el código propio o en el código al que se llama
(por ejemplo, una biblioteca compartida), a recursos del sistema operativo no disponibles, a condiciones
inesperadas que encuentra el runtime (por ejemplo, imposibilidad de comprobar el código), etc. La aplicación
puede recuperarse de algunas de estas condiciones, pero no de todas. Aunque es posible recuperarse de la
mayoría de las excepciones que se producen en la aplicación, no ocurre lo mismo con las excepciones de runtime.
En .NET, una excepción es un objeto que hereda de la clase System.Exception. Una excepción se inicia desde un
área del código en la que ha producido un problema. La excepción se pasa hacia arriba en la pila hasta que la
aplicación la controla o el programa finaliza.
Excepciones comunes
En la tabla siguiente se muestra algunas excepciones comunes con ejemplos de las causas que las originan.
Exception Clase base de todas las excepciones. Ninguno (utilice una clase derivada de
esta excepción).
T IP O DE EXC EP C IÓ N DESC RIP C IÓ N E JEM P LO
ArgumentException Clase base de todas las excepciones de Ninguno (utilice una clase derivada de
argumento. esta excepción).
Vea también
Clase Exception y propiedades
Cómo: Utilizar el bloque Try/Catch para detectar excepciones
Cómo: Usar excepciones específicas en un bloque Catch
Cómo: Iniciar excepciones explícitamente
Cómo: Crear excepciones definidas por el usuario
Utilizar controladores de excepciones filtradas por el usuario
Cómo: Usar bloques Finally
Controlar excepciones de interoperabilidad COM
Procedimientos recomendados para excepciones
¿Qué necesitan saber todos los desarrolladores acerca de las excepciones producidas en el runtime?
Clase Exception y propiedades
18/03/2020 • 4 minutes to read • Edit Online
La clase Exception es la clase base de la que heredan las excepciones. Por ejemplo, la jerarquía de clases de
InvalidCastException es como sigue:
Object
Exception
SystemException
InvalidCastException
La clase Exception tiene las siguientes propiedades que facilitan la comprensión de una excepción.
InnerException Esta propiedad puede usarse para crear y mantener una serie
de excepciones durante el control de excepciones. Puede
usarla para crear una nueva excepción que contiene
excepciones detectadas con anterioridad. La segunda
excepción en la propiedad InnerException puede capturar la
excepción original, lo que permite que el código que controla
la segunda excepción examine la información adicional. Por
ejemplo, supongamos que tiene un método que recibe un
argumento que contiene un formato incorrecto. El código
intenta leer el argumento, pero se inicia una excepción. El
método detecta la excepción e inicia FormatException. Para
mejorar la capacidad del autor de llamada a la hora de
determinar por qué se inicia una excepción, a veces, para un
método es recomendable detectar una excepción iniciada por
una rutina del asistente y, después, iniciar una excepción más
indicativa del error que se ha producido. Puede crear una
excepción nueva y más significativa, donde se puede
establecer la referencia a la excepción interna en la excepción
original. Después, se puede iniciar esta excepción más
significativa para el autor de llamada. Tenga en cuenta que con
esta funcionalidad, puede crear una serie de excepciones
vinculadas que termina con la excepción que se inicia en
primer lugar.
La mayoría de las clases que heredan de Exception no implementan miembros adicionales ni proporcionan
funciones adicionales; simplemente heredan de Exception. Por tanto, se puede encontrar la información más
importante para una excepción en la jerarquía de clases de excepción, el nombre de la excepción y la información
contenida en la excepción.
Se recomienda iniciar y detectar solo los objetos que se derivan de Exception, pero puede iniciar cualquier objeto
que se deriva de la clase Object como una excepción. Tenga en cuenta que no todos los lenguajes admiten iniciar y
detectar objetos que no se derivan de Exception.
Vea también
Excepciones
Cómo usar el bloque Try/Catch para detectar
excepciones
16/09/2020 • 3 minutes to read • Edit Online
Coloque las instrucciones de código que podrían elevar o producir una excepción en un bloque try , y las que se
usan para controlar la excepción o excepciones en uno o varios bloques catch debajo del bloque try . Cada
bloque catch incluye el tipo de excepción y puede contener instrucciones adicionales necesarias para controlar
ese tipo de excepción.
En el ejemplo siguiente, un elemento StreamReader abre un archivo denominado data.txt y recupera una línea del
archivo. Como es posible que el código genere cualquiera de las tres excepciones, se coloca en un bloque try . Tres
bloques catch detectan las excepciones y las controlan mostrando los resultados en la consola.
using System;
using System.IO;
Common Language Runtime (CLR) detecta las excepciones no controladas por los bloques catch . Si CLR detecta
una excepción, puede producirse uno de los resultados siguientes, en función de la configuración de CLR:
Aparece un cuadro de diálogo Depurar .
El programa detiene la ejecución y aparece un cuadro de diálogo con información de la excepción.
Se imprime un error en el flujo de salida de error estándar.
NOTE
La mayoría del código puede producir una excepción y algunas excepciones, como OutOfMemoryException, las puede
generar el propio CLR en cualquier momento. Aunque no es obligatorio que las aplicaciones controlen estas excepciones,
tenga en cuenta esta posibilidad al escribir bibliotecas que puedan usar otros usuarios. Para obtener sugerencias sobre
cuándo establecer el código en un bloque try , vea Procedimientos recomendados para excepciones.
Vea también
Excepciones
Control de errores de E/S en .NET
Cómo: Utilizar excepciones específicas en un bloque
Catch
18/03/2020 • 3 minutes to read • Edit Online
En general, en programación es recomendable detectar un tipo de excepción específico en lugar de usar una
instrucción catch básica.
Cuando se produce una excepción, se pasa a la pila y cada bloque Catch tiene la oportunidad de controlarla. El
orden de las instrucciones Catch es importante. Ponga los bloques Catch dirigidos a excepciones específicas antes
de un bloque Catch de excepción general o el compilador podría emitir un error. El bloque Catch adecuado se
determina haciendo coincidir el tipo de la excepción con el nombre de la excepción especificada en el bloque Catch.
Si no hay ningún bloque Catch específico, la excepción se detecta mediante un bloque Catch general, si existe
alguno.
En el siguiente ejemplo de código se usa un bloque try / catch para detectar una InvalidCastException. El ejemplo
crea una clase denominada Employee con una propiedad única, el nivel de empleado ( Emlevel ). Un método,
PromoteEmployee , toma un objeto e incrementa el nivel del empleado. Una InvalidCastException se produce cuando
una instancia de DateTime se pasa al método PromoteEmployee .
using namespace System;
private:
int emlevel;
};
int main()
{
Ex13::Main();
}
using System;
Vea también
Excepciones
Cómo iniciar excepciones explícitamente
16/09/2020 • 2 minutes to read • Edit Online
Puede iniciar explícitamente una excepción mediante la instrucción throw de C# o la instrucción Throw de Visual
Basic. También se puede iniciar una excepción detectada usando de nuevo la instrucción throw . En diseño de
código, es recomendable agregar información a una excepción que se vuelve a iniciar para proporcionar más
información durante la depuración.
En el siguiente ejemplo de código se usa un bloque try / catch para detectar una posible FileNotFoundException.
Seguido del bloque try va un bloque catch que detecta FileNotFoundException y escribe un mensaje a la
consola si no se encuentra el archivo de datos. La siguiente instrucción es throw que inicia un parámetro
FileNotFoundException nuevo y agrega información de texto a la excepción.
using System;
using System.IO;
Imports System.IO
' A value is read from the file and output to the console.
Dim line As String = sr.ReadLine()
Console.WriteLine(line)
Catch e As FileNotFoundException
Console.WriteLine($"[Data File Missing] {e}")
Throw New FileNotFoundException("[data.txt not in c:\temp directory]", e)
Finally
If fs IsNot Nothing Then fs.Close()
End Try
End Sub
End Class
Vea también
Excepciones
Cómo crear excepciones definidas por el usuario
16/09/2020 • 2 minutes to read • Edit Online
.NET proporciona una jerarquía de clases de excepciones derivadas en última instancia de la clase base Exception.
Pero si ninguna de las excepciones predefinidas satisface sus necesidades, puede crear sus propias clases de
excepciones derivadas de la clase Exception.
Al crear sus propias excepciones, termine el nombre de clase de la excepción definida por el usuario con la palabra
"Exception" e implemente los tres constructores comunes, como se muestra en el ejemplo siguiente. En el ejemplo
se define una nueva clase de excepción denominada EmployeeListNotFoundException . La clase se deriva de
Exception e incluye tres constructores.
EmployeeListNotFoundException(String^ message)
: Exception(message)
{
}
using System;
NOTE
En situaciones en que use la comunicación remota, debe asegurarse de que los metadatos de todas las excepciones definidas
por el usuario estén disponibles en el servidor (destinatario) y en el cliente (el objeto proxy o autor de la llamada). Para
obtener más información, consulte Procedimientos recomendados para excepciones.
Vea también
Excepciones
Creación de excepciones definidas por el usuario con
mensajes de excepción localizados
16/09/2020 • 5 minutes to read • Edit Online
En este artículo, obtendrá información sobre cómo crear excepciones definidas por el usuario que se hereden de la
clase base Exception con mensajes de excepción localizados mediante ensamblados satélite.
[Serializable]
public class StudentNotFoundException : Exception { }
<Serializable>
Public Class StudentNotFoundException
Inherits Exception
End Class
[Serializable]
public class StudentNotFoundException : Exception
{
public StudentNotFoundException() { }
[Serializable]
public class StudentNotFoundException : Exception
{
public string StudentName { get; }
public StudentNotFoundException() { }
<Serializable>
Public Class StudentNotFoundException
Inherits Exception
El problema con la línea anterior es que "The student cannot be found." es simplemente una cadena constante. En
una aplicación localizada, quiere tener mensajes diferentes en función de la referencia cultural del usuario. Los
ensamblados satélite son una buena manera de hacerlo. Un ensamblado satélite es un archivo .dll que contiene
recursos para un idioma específico. Cuando se solicitan recursos específicos en tiempo de ejecución, el CLR
encuentra ese recurso en función de la referencia cultural del usuario. Si no se encuentra ningún ensamblado
satélite para esa referencia cultural, se usan los recursos de la referencia cultural predeterminada.
Para crear los mensajes de excepción localizados:
1. Cree una carpeta llamada Recursos para contener los archivos de recursos.
2. Agréguele un archivo de recursos nuevo. Para ello, en Visual Studio, haga clic con el botón derecho en la
carpeta en el Explorador de soluciones y seleccione Agregar > Nuevo elemento > Archivo de
recursos . Asígnele al archivo el nombre ExceptionMessages.resx. Este es el archivo de recursos
predeterminado.
3. Agregue un par nombre-valor para el mensaje de excepción, como se muestra en la imagen siguiente:
6. Después de compilar el proyecto, la carpeta de la salida de compilación debe contener la carpeta fr-FR con
un archivo .dll, que es el ensamblado satélite.
7. Inicia la excepción con código como el siguiente:
Vea también
Cómo crear excepciones definidas por el usuario
Crear ensamblados satélite para aplicaciones de escritorio
base (Referencia de C#)
this (Referencia de C#)
Cómo usar bloques Finally
18/03/2020 • 2 minutes to read • Edit Online
try
{
Array::Copy(array1, array2, -1);
}
catch (ArgumentOutOfRangeException^ e)
{
Console::WriteLine("Error: {0}", e);
throw;
}
finally
{
Console::WriteLine("This statement is always executed.");
}
}
};
int main()
{
ArgumentOutOfRangeExample::Main();
}
using System;
class ArgumentOutOfRangeExample
{
public static void Main()
{
int[] array1 = {0, 0};
int[] array2 = {0, 0};
try
{
Array.Copy(array1, array2, -1);
}
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine("Error: {0}", e);
throw;
}
finally
{
Console.WriteLine("This statement is always executed.");
}
}
}
Class ArgumentOutOfRangeExample
Public Shared Sub Main()
Dim array1() As Integer = {0, 0}
Dim array2() As Integer = {0, 0}
Try
Array.Copy(array1, array2, -1)
Catch e As ArgumentOutOfRangeException
Console.WriteLine("Error: {0}", e)
Throw
Finally
Console.WriteLine("This statement is always executed.")
End Try
End Sub
End Class
Vea también
Excepciones
Utilizar controladores de excepciones filtradas por el
usuario
18/03/2020 • 2 minutes to read • Edit Online
Actualmente, Visual Basic admite excepciones filtradas por el usuario. Los controladores de excepciones filtradas
por usuario detectan y controlan las excepciones en función de los requisitos que se definen para la excepción.
Estos controladores utilizan la instrucción Catch con la palabra clave When .
Esta técnica es útil cuando un objeto de excepción concreto corresponde a varios errores. En este caso, el objeto
normalmente tiene una propiedad que contiene el código de error específico asociado con el error. Puede utilizar la
propiedad de código de error en la expresión para seleccionar solo el error concreto que desea administrar en esa
cláusula Catch .
El siguiente ejemplo de Visual Basic ilustra la instrucción Catch/When .
Try
'Try statements.
Catch When Err = VBErr_ClassLoadException
'Catch statements.
End Try
La expresión de la cláusula filtrada por el usuario no está restringida en modo alguno. Si se produce una excepción
durante la ejecución de la expresión filtrada por el usuario, esa excepción se descarta y la expresión de filtro se
considera evaluada como false. En este caso, Common Language Runtime continúa la búsqueda de un controlador
para la excepción actual.
Try
'Try statements.
Catch cle As ClassLoadException When cle.IsRecoverable()
'Catch statements.
End Try
Vea también
Excepciones
Controlar excepciones de interoperabilidad COM
16/09/2020 • 2 minutes to read • Edit Online
El código administrado y el código no administrado pueden trabajar juntos para controlar excepciones. Si un
método produce una excepción en código administrado, Common Language Runtime puede pasar un HRESULT a
un objeto COM. Si un método produce un error en código no administrado y devuelve un HRESULT de error, el
tiempo de ejecución produce una excepción que el código administrado puede capturar.
El tiempo de ejecución asigna automáticamente el HRESULT de la interoperabilidad COM a excepciones más
específicas. Por ejemplo, E_ACCESSDENIED se convierte en UnauthorizedAccessException, E_OUTOFMEMORY se
convierte en OutOfMemoryException, y así sucesivamente.
Si el HRESULT es un resultado personalizado o es desconocido para el tiempo de ejecución, este pasa una
COMException genérica al cliente. La propiedad ErrorCode de COMException contiene el valor HRESULT.
Vea también
Excepciones
Procedimientos recomendados para excepciones
16/09/2020 • 17 minutes to read • Edit Online
Una aplicación diseñada correctamente controla las excepciones y los errores para evitar que se bloquee. En este
artículo se describen los procedimientos recomendados para controlar y crear excepciones.
if (conn->State != ConnectionState::Closed)
{
conn->Close();
}
if (conn.State != ConnectionState.Closed)
{
conn.Close();
}
try
{
conn.Close();
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.GetType().FullName);
Console.WriteLine(ex.Message);
}
Try
conn.Close()
Catch ex As InvalidOperationException
Console.WriteLine(ex.GetType().FullName)
Console.WriteLine(ex.Message)
End Try
El método que se elija depende de la frecuencia con la que espera que se produzca el evento.
Utilice el control de excepciones si el evento no se produce con frecuencia, es decir, si el evento es muy
excepcional e indica un error (como un final de archivo inesperado). Cuando se usa el control de
excepciones, se ejecuta menos código en condiciones normales.
Compruebe condiciones de error en el código cuando el evento se produce con frecuencia y se puede
considerar como parte de la ejecución normal. Cuando se buscan condiciones de error comunes, se ejecuta
menos código porque se evitan excepciones.
int b;
class FileRead
{
public void ReadAll(FileStream fileToRead)
{
// This if statement is optional
// as it is very unlikely that
// the stream would ever be null.
if (fileToRead == null)
{
throw new ArgumentNullException();
}
int b;
Dim b As Integer
Otro modo de evitar excepciones es devolver un valor NULL (o el valor predeterminado) para los casos de errores
muy frecuentes en lugar de iniciar una excepción. Un caso de error muy común se puede considerar como un
flujo de control normal. Al devolver un valor NULL en estos casos, se minimiza el impacto en el rendimiento de
una aplicación.
Para los tipos de valores, el uso de Nullable<T> o de valores predeterminados como indicador de error es algo
que se debe tener en cuenta para la aplicación en particular. Al utilizar Nullable<Guid> , default se convierte en
null en lugar de Guid.Empty . Algunas veces, agregar Nullable<T> puede aclarar cuando un valor está presente
o ausente. Otras veces, agregar Nullable<T> puede crear casos adicionales para comprobar que no son
necesarios, y solo sirven para crear posibles orígenes de errores.
public:
FileReader(String^ path)
{
fileName = path;
}
FileReaderException^ NewFileIOException()
{
String^ description = "My NewFileIOException Description";
FileReaderException NewFileIOException()
{
string description = "My NewFileIOException Description";
Class FileReader
Private fileName As String
En algunos casos, es más apropiado usar el constructor de excepciones para generar la excepción. Un ejemplo es
una clase de excepción global, como ArgumentException.
El método anterior no produce ninguna excepción directamente, pero debe escribirse de forma defensiva para
que, si se produce un error en la operación de depósito, se invierta la retirada.
Para controlar esta situación se puede detectar cualquier excepción iniciada por la transacción del depósito y
revertir la retirada.
Este ejemplo muestra el uso de throw para volver a iniciar la excepción original, que puede facilitar a los autores
de llamada ver la causa del problema real sin tener que examinar la propiedad InnerException. Como alternativa
se puede iniciar una nueva excepción e incluir la excepción original como excepción interna:
Vea también
Excepciones
Valores numéricos en .NET
06/05/2020 • 8 minutes to read • Edit Online
.NET proporciona una serie de tipos primitivos de entero numérico y de punto flotante, así como
System.Numerics.BigInteger, que es un tipo entero sin límite inferior ni superior teórico, System.Numerics.Complex,
que representa números complejos y un conjunto de tipos habilitados para SIMD en el espacio de nombres
System.Numerics.
Tipos enteros
.NET admite tipos enteros de 8, 16, 32 y 64 bits con signo y sin signo, que se enumeran en la tabla siguiente:
C O N SIGN O / SIN
T IP O SIGN O TA M A Ñ O ( EN B Y T ES) VA LO R M ÍN IM O VA LO R M Á XIM O
Cada tipo de entero admite un conjunto de operadores aritméticos estándar. La clase System.Math proporciona
métodos para un conjunto más amplio de funciones matemáticas.
También puede trabajar con los bits individuales de un valor entero usando la clase System.BitConverter.
NOTE
Los tipos enteros sin signo no son conformes a CLS. Para obtener más información, consulte Independencia del lenguaje y
componentes independientes del lenguaje.
BigInteger
La estructura System.Numerics.BigInteger es un tipo inmutable que representa un entero arbitrariamente grande
cuyo valor, en teoría, no tiene ningún límite superior o inferior. Los métodos del tipo BigInteger son análogos a los
de otros tipos integrales.
Tipos de punto flotante
.NET incluye tres tipos primitivos de punto flotante, que se enumeran en la tabla siguiente:
Los tipos Single y Double admiten valores especiales que representan un valor no numérico e infinito. Por ejemplo,
el tipo Double proporciona los siguientes valores: Double.NaN, Double.NegativeInfinity y Double.PositiveInfinity.
Los métodos Double.IsNaN, Double.IsInfinity, Double.IsPositiveInfinity y Double.IsNegativeInfinity se usan para
comprobar estos valores especiales.
Cada tipo de punto flotante admite un conjunto de operadores aritméticos estándar. La clase System.Math
proporciona métodos para un conjunto más amplio de funciones matemáticas. .NET Core 2.0 y versiones
posteriores incluyen la clase System.MathF que proporciona métodos que aceptan argumentos del tipo Single.
También puede trabajar con bits individuales de valores Double y Single usando la clase System.BitConverter. La
estructura System.Decimal tiene sus propios métodos, Decimal.GetBits y Decimal(Int32[]), para trabajar con los bits
individuales de un valor decimal, así como su propio conjunto de métodos para realizar algunas operaciones
matemáticas adicionales.
Los tipos Double y Single están diseñados para usarse con valores que, por su naturaleza, no son precisos (por
ejemplo, la distancia entre dos estrellas) y para aplicaciones en las que no se necesita un alto grado de precisión ni
un mínimo error de redondeo. Use el tipo System.Decimal para los casos en los que se necesite una mayor
precisión y se deban minimizar los errores de redondeo.
NOTE
El tipo Decimal no elimina la necesidad de redondeo. En su lugar, minimiza los errores debido al redondeo.
Complex
La estructura System.Numerics.Complex representa un número complejo, es decir, un número con una parte de
número real y una parte de número imaginario. Admite un conjunto estándar de operadores de aritmética,
comparación, igualdad, conversión explícita e implícita, así como métodos matemáticos, algebraicos y
trigonométricos.
NOTE
El Vector<T> tipo no está incluido en .NET Framework. Debe instalar el paquete NuGet System.Numerics.Vectors para
acceder a este tipo.
Los tipos habilitados para SIMD se implementan de tal forma que se pueden utilizar con hardware no habilitado
para SIMD o compiladores JIT. Para aprovechar las instrucciones de SIMD, las aplicaciones de 64 bits las debe
ejecutar el entorno en tiempo de ejecución que usa el compilador RyuJIT, que se incluye en .NET Core y en .NET
Framework 4.6 y versiones posteriores. Agrega compatibilidad con SIMD cuando se usan procesadores de 64 bits
como destino.
Para obtener más información, vea Uso de tipos numéricos acelerados por SIMD.
Vea también
Cadenas con formato numérico estándar
Fechas, horas y zonas horarias
16/09/2020 • 7 minutes to read • Edit Online
Además de la estructura básica DateTime, .NET proporciona las siguientes clases que permiten trabajar con zonas
horarias:
TimeZone
Use esta clase para trabajar con la zona horaria local del sistema y la zona de la hora universal coordinada
(UTC). La TimeZone clase reemplaza en gran medida la funcionalidad de la clase TimeZoneInfo .
TimeZoneInfo
Use esta clase para trabajar con cualquier zona horaria predefinida en un sistema, para crear zonas horarias
nuevas y para convertir fácilmente fechas y horas desde una zona horaria a otra. Para desarrollo nuevo,
debe usar la clase TimeZoneInfo, en lugar de la clase TimeZone.
DateTimeOffset
Use esta estructura para trabajar con fechas y horas cuyo desplazamiento (o diferencia) con respecto a la
hora UTC es conocido. La estructura DateTimeOffset combina un valor de fecha y hora con ese
desplazamiento de hora de UTC. Debido a su relación con la hora UTC, un valor individual de fecha y hora
identifica de forma inequívoca un punto temporal único. Esto hace que un valor de DateTimeOffset sea más
portátil de un equipo a otro que un valor de DateTime.
Esta sección de la documentación proporciona la información que necesita para trabajar con zonas horarias y para
crear aplicaciones basadas en la zona horaria que puedan convertir fechas y horas de una zona horaria a otra.
En esta sección
Información general sobre zonas horarias describe la terminología, los conceptos y los problemas relacionados con
la creación de aplicaciones basadas en la zona horaria.
Elegir entre DateTime, DateTimeOffset, TimeSpan y TimeZoneInfo Describe cuándo usar los DateTime
DateTimeOffset tipos, y TimeZoneInfo al trabajar con datos de fecha y hora.
En Finding the time zones defined on a local system (Buscar las zonas horarias definidas en un sistema local) se
describe cómo enumerar las zonas horarias que se encuentran en un sistema local.
En How to: Enumerate time zones present on a computer (Cómo: enumerar zonas horarias presentes en un equipo)
se proporcionan ejemplos que enumeran las zonas horarias definidas en el Registro de un equipo y que permiten a
los usuarios seleccionar una zona horaria predefinida de una lista.
En How to: Access the predefined UTC and local time zone objects (Cómo: obtener acceso a los objetos de zona
horaria local y UTC predefinidos) se describe cómo tener acceso a la hora universal coordinada y a la zona horaria
local.
En How to: Instantiate a TimeZoneInfo object (Cómo: crear instancias de un objeto TimeZoneInfo) se describe cómo
crear una instancia de un objeto TimeZoneInfo desde el Registro del sistema local.
En Instantiating a DateTimeOffset object (Crear instancias de un objeto DateTimeOffset) se describen las formas en
que pueden crearse instancias de un objeto DateTimeOffset y las formas en que un valor DateTime se puede
convertir en un valor DateTimeOffset.
En How to: Create time zones without adjustment rules (Cómo: crear zonas horarias sin reglas de ajuste) se
describe cómo crear una zona horaria personalizada que no admite la transición al horario de verano o desde este.
En How to: Create time zones with adjustment rules (Cómo: crear zonas horarias con reglas de ajuste) se describe
cómo crear una zona horaria personalizada que admite una o más transiciones al horario de verano o desde este.
En Saving and restoring time zones (Guardar y restaurar zonas horarias) se describe la compatibilidad de
TimeZoneInfo con la serialización y la deserialización de datos de zona horaria y muestra algunos de los escenarios
en que se pueden usar estas características.
En How to: Save time zones to an embedded resource (Cómo: guardar zonas horarias en un recurso insertado) se
describe cómo crear una zona horaria personalizada y guardar su información en un archivo de recursos.
En How to: Restore time zones from an embedded resource (Cómo: restaurar zonas horarias de un recurso
insertado) se describe cómo crear instancias de zonas horarias personalizadas que se han guardado en un archivo
de recursos insertado.
En Performing arithmetic operations with dates and times (Efectuar operaciones aritméticas con fechas y horas) se
describen los aspectos necesarios para agregar, sustraer y comparar los valores DateTime y DateTimeOffset.
En How to: Use time zones in date and time arithmetic (Cómo: usar zonas horarias en aritmética de fecha y hora) se
describe cómo realizar la aritmética de fecha y hora que refleja las reglas de ajuste de una zona horaria.
En Converting between DateTime and DateTimeOffset (Convertir entre DateTime y DateTimeOffset) se describe
cómo convertir entre valores DateTime y DateTimeOffset.
En Converting times between time zones (Convertir horas entre zonas horarias) se describe cómo convertir las
horas de una zona horaria a otra.
En How to: Resolve ambiguous times (Cómo: resolver horas ambiguas) se describe cómo resolver una hora
ambigua mediante su asignación a la hora estándar de la zona horaria.
Cómo: Permitir que los usuarios resuelvan horas ambiguas describe cómo permitir que los usuarios determinen la
asignación entre una hora local ambigua y la hora universal coordinada.
Referencia
System.TimeZoneInfo
Tipos de formato en .NET
16/09/2020 • 64 minutes to read • Edit Online
Aplicar formato es el proceso de convertir una instancia de una clase, una estructura o un valor de enumeración
en su representación de cadena, a menudo para que la cadena resultante se pueda mostrar a los usuarios o
deserializar para restaurar el tipo de datos original. Esta conversión puede plantear varios desafíos:
La forma en que se almacenan internamente los valores no refleja necesariamente la manera en que los
usuarios desean verlos. Por ejemplo, un número de teléfono podría almacenarse con el formato
8009999999, que no es fácil de usar. Se debería mostrar en su lugar como 800-999-9999. Consulte la
sección Cadenas de formato personalizado para obtener un ejemplo en el que da formato a un número
de esta forma.
A veces, la conversión de un objeto en su representación de cadena no es intuitiva. Por ejemplo, no está
claro cómo debe aparecer la representación de cadena de un objeto Temperature o un objeto Person. Para
obtener un ejemplo en el que se da formato a un objeto Temperature de varias formas, consulte la
sección Cadenas de formato estándar .
A menudo, los valores requieren un formato dependiente de la referencia cultural. Por ejemplo, en una
aplicación en la que se usan números para reflejar los valores monetarios, las cadenas numéricas deben
incluir el símbolo de divisa, el separador de grupo (que en la mayoría de las referencias culturales es el
separador de miles) y el símbolo decimal correspondientes a la referencia cultural actual. Para ver un
ejemplo, consulte la sección Formato según la referencia cultural con proveedores de formato.
Puede que una aplicación muestre el mismo valor de diferentes maneras. Por ejemplo, es posible que una
aplicación represente un miembro de enumeración mostrando una representación de cadena de su
nombre o mostrando su valor subyacente. Para obtener un ejemplo en el que se da formato a un
miembro de la enumeración DayOfWeek de maneras diferentes, consulte la sección Cadenas de formato
estándar .
NOTE
La aplicación de formato convierte el valor de un tipo en una representación de cadena. El análisis es lo contrario que la
aplicación de formato. Una operación de análisis crea una instancia de un tipo de datos a partir de su representación de
cadena. Para información sobre cómo convertir cadenas en otros tipos de datos, vea Analizar cadenas en .NET.
.NET proporciona compatibilidad de formato enriquecida que permite a los desarrolladores hacer frente a estos
requisitos.
Formato en .NET
El mecanismo básico para aplicar formato es la implementación predeterminada del método Object.ToString,
que se explica en la sección Formato predeterminado mediante el método ToString más adelante en este tema.
Pero .NET proporciona varias formas de modificar y extender su compatibilidad de formato predeterminado.
Entre ellas se incluyen las siguientes:
Invalidar el método Object.ToString para definir una representación de cadena personalizada del valor de
un objeto. Para obtener más información, consulte la sección Invalidación del método ToString más
adelante en este tema.
Definir especificadores de formato que permitan que la representación de cadena del valor de un objeto
adopte varios formatos. Por ejemplo, el especificador de formato "X" en la siguiente instrucción convierte
un entero en la representación de cadena de un valor hexadecimal.
Para obtener más información sobre los especificadores de formato, vea la sección Método ToString y
cadenas de formato .
Usar proveedores de formato para aprovechar las convenciones de formato de una referencia cultural
concreta. Por ejemplo, la instrucción siguiente muestra un valor de divisa usando las convenciones de
formato de la referencia cultural en-US.
Para obtener más información sobre cómo aplicar formato con proveedores de formato, consulte la
sección Proveedores de formato.
Implementar la interfaz IFormattable para admitir tanto la conversión de cadenas con la clase Convert
como formatos compuestos. Para obtener más información, vea la sección Interfaz IFormattable .
Usar formatos compuestos para incrustar la representación de cadena de un valor en una cadena más
larga. Para obtener más información, vea la sección Formatos compuestos .
Implementar ICustomFormatter y IFormatProvider para proporcionar una solución de formato
personalizado completa. Para obtener más información, vea la sección Formato personalizado con
ICustomFormatter .
En las secciones siguientes se examinan estos métodos para convertir un objeto en su representación de cadena.
Module Example
Public Sub Main()
Dim firstAuto As New Automobile()
Console.WriteLine(firstAuto)
End Sub
End Module
' The example displays the following output:
' Automobile
WARNING
A partir de Windows 8.1, Windows Runtime incluye una interfaz IStringable con un solo método, IStringable.ToString, que
ofrece compatibilidad con el formato predeterminado. Sin embargo, es recomendable que los tipos administrados no
implementen la interfaz IStringable . Para obtener más información, vea la sección "Windows Runtime y la interfaz
IStringable " de la página de referencia Object.ToString.
Puesto que todos los tipos distintos de las interfaces se derivan de Object, esta funcionalidad se proporciona
automáticamente a sus clases o estructuras personalizadas. Si bien, la funcionalidad que ofrece el método
ToString predeterminado es limitada: Aunque identifica el tipo, no proporciona ninguna información sobre una
instancia del tipo. Para proporcionar una representación de cadena de un objeto que proporciona información
sobre ese objeto, debe invalidar el método ToString .
NOTE
Las estructuras heredan de ValueType, que a su vez se deriva de Object. Aunque ValueType invalida Object.ToString, su
implementación es idéntica.
Module Example
Public Sub Main()
Dim currentTemperature As New Temperature(23.6d)
Console.WriteLine("The current temperature is " +
currentTemperature.ToString())
End Sub
End Module
' The example displays the following output:
' The current temperature is 23.6°C.
En .NET, el método ToString de cada tipo de valor primitivo se ha invalidado para que se muestre el valor del
objeto en lugar de su nombre. En la tabla siguiente se muestra la invalidación para cada tipo primitivo. Observe
que la mayoría de los métodos invalidados llaman a otra sobrecarga del método ToString y le pasan el
especificador de formato "G", que define el formato general de su tipo, y un objeto IFormatProvider que
representa la referencia cultural actual.
T IP O IN VA L IDA C IÓ N DE TO ST RIN G
Byte Llama a
Byte.ToString("G", NumberFormatInfo.CurrentInfo)
para dar formato al valor de tipo Byte correspondiente a la
referencia cultural actual.
DateTime Llama a
DateTime.ToString("G",
DatetimeFormatInfo.CurrentInfo)
para dar formato al valor de fecha y hora correspondiente a
la referencia cultural actual.
Decimal Llama a
Decimal.ToString("G", NumberFormatInfo.CurrentInfo)
para dar formato al valor de tipo Decimal correspondiente a
la referencia cultural actual.
Double Llama a
Double.ToString("G", NumberFormatInfo.CurrentInfo)
para dar formato al valor de tipo Double correspondiente a
la referencia cultural actual.
Int16 Llama a
Int16.ToString("G", NumberFormatInfo.CurrentInfo)
para dar formato al valor de tipo Int16 correspondiente a la
referencia cultural actual.
Int32 Llama a
Int32.ToString("G", NumberFormatInfo.CurrentInfo)
para dar formato al valor de tipo Int32 correspondiente a la
referencia cultural actual.
Int64 Llama a
Int64.ToString("G", NumberFormatInfo.CurrentInfo)
para dar formato al valor de tipo Int64 correspondiente a la
referencia cultural actual.
SByte Llama a
SByte.ToString("G", NumberFormatInfo.CurrentInfo)
para dar formato al valor de tipo SByte correspondiente a la
referencia cultural actual.
Single Llama a
Single.ToString("G", NumberFormatInfo.CurrentInfo)
para dar formato al valor de tipo Single correspondiente a la
referencia cultural actual.
UInt16 Llama a
UInt16.ToString("G", NumberFormatInfo.CurrentInfo)
para dar formato al valor de tipo UInt16 correspondiente a
la referencia cultural actual.
T IP O IN VA L IDA C IÓ N DE TO ST RIN G
UInt32 Llama a
UInt32.ToString("G", NumberFormatInfo.CurrentInfo)
para dar formato al valor de tipo UInt32 correspondiente a
la referencia cultural actual.
UInt64 Llama a
UInt64.ToString("G", NumberFormatInfo.CurrentInfo)
para dar formato al valor de tipo UInt64 correspondiente a
la referencia cultural actual.
Para información sobre las cadenas de formato de enumeración, vea Enumeration Format Strings.
Las cadenas de formato estándar para tipos numéricos normalmente definen una cadena de resultado cuya
apariencia exacta está controlada por uno o más valores de propiedad. Por ejemplo, el especificador de formato
"C" da formato a un número como un valor de divisa. Al llamar al método ToString con el especificador de
formato "C" como único parámetro, se usan los siguientes valores de propiedad del objeto NumberFormatInfo
de la referencia cultural actual para definir la representación de cadena del valor numérico:
La propiedad CurrencySymbol, que especifica el símbolo de divisa de la referencia cultural actual.
La propiedad CurrencyNegativePattern o CurrencyPositivePattern , que devuelve un entero que
determina lo siguiente:
La posición del símbolo de divisa.
Si los valores negativos se indican mediante un signo negativo inicial, un signo negativo final o
paréntesis.
Si aparece un espacio entre el valor numérico y el símbolo de divisa.
La propiedad CurrencyDecimalDigits , que define el número de dígitos fraccionarios en la cadena de
resultado.
La propiedad CurrencyDecimalSeparator , que define el símbolo del separador decimal en la cadena de
resultado.
La propiedad CurrencyGroupSeparator , que define el símbolo del separador de grupo.
La propiedad CurrencyGroupSizes , que define el número de dígitos de cada grupo que hay a la izquierda
del decimal.
La propiedad NegativeSign , que determina el signo negativo usado en la cadena de resultado si no se
emplean paréntesis para indicar valores negativos.
Además, las cadenas de formato numérico pueden incluir un especificador de precisión. El significado de este
especificador depende de la cadena de formato con la que se usa, pero suele indicar el número total de dígitos o
el número de dígitos fraccionarios que deben aparecer en la cadena de resultado. Por ejemplo, en el ejemplo
siguiente se usa la cadena numérica estándar "X4" y un especificador de precisión para crear un valor de cadena
que tiene cuatro dígitos hexadecimales.
Para más información sobre las cadenas de formato numérico estándar, vea Standard Numeric Format Strings.
Las cadenas de formato estándar para valores de fecha y hora son alias de las cadenas de formato
personalizado almacenadas por una propiedad DateTimeFormatInfo determinada. Por ejemplo, al llamar al
método ToString de un valor de fecha y hora con el especificador de formato "D" se muestran la fecha y la hora
usando la cadena de formato personalizado que está almacenada en la propiedad
DateTimeFormatInfo.LongDatePattern de la referencia cultural actual. (Para obtener más información sobre las
cadenas de formato personalizado, vea la próxima sección). En el ejemplo siguiente se ilustra esta relación.
using System;
using System.Globalization;
Module Example
Public Sub Main()
Dim date1 As Date = #6/30/2009#
Console.WriteLine("D Format Specifier: {0:D}", date1)
Dim longPattern As String = CultureInfo.CurrentCulture.DateTimeFormat.LongDatePattern
Console.WriteLine("'{0}' custom format string: {1}", _
longPattern, date1.ToString(longPattern))
End Sub
End Module
' The example displays the following output when run on a system whose
' current culture is en-US:
' D Format Specifier: Tuesday, June 30, 2009
' 'dddd, MMMM dd, yyyy' custom format string: Tuesday, June 30, 2009
Para más información sobre las cadenas de formato de fecha y hora estándar, vea Standard Date and Time
Format Strings.
También se pueden emplear las cadenas de formato estándar para definir la representación de cadena de un
objeto definido por la aplicación que genera el método ToString(String) del objeto. Puede definir los
especificadores de formato estándar concretos que su objeto admite y determinar si distinguen entre
mayúsculas y minúsculas o no. Su implementación del método ToString(String) debe aceptar lo siguiente:
Un especificador de formato "G" que representa un formato personalizado o común del objeto. La
sobrecarga sin parámetros del método ToString del objeto debe llamar a su sobrecarga de
ToString(String) y pasarle la cadena de formato estándar "G".
Compatibilidad con un especificador de formato que sea igual a una referencia nula ( Nothing en Visual
Basic). Un especificador de formato que es igual a una referencia nula debe considerarse equivalente al
especificador de formato "G".
Por ejemplo, una clase Temperature puede almacenar internamente la temperatura en grados centígrados y
usar especificadores de formato para representar el valor del objeto Temperature en grados centígrados, grados
Fahrenheit y grados Kelvin. Esto se muestra en el ejemplo siguiente.
using System;
Muchas cadenas de formato estándar para valores de fecha y hora son alias para cadenas de formato
personalizado definidas por propiedades del objeto DateTimeFormatInfo . Las cadenas de formato
personalizado también ofrecen una gran flexibilidad a la hora de proporcionar formatos definidos por la
aplicación para los valores numéricos o de fecha y hora. Puede definir sus propias cadenas de resultado
personalizadas para los valores numéricos y de fecha y hora si combina varios especificadores de formato
personalizados en una única cadena de formato personalizado. En el ejemplo siguiente se define una cadena de
formato personalizado que muestra el día de la semana entre paréntesis después del nombre de mes, el día y el
año.
En el ejemplo siguiente se define una cadena de formato personalizado que muestra un valor de Int64 como un
número de teléfono de Estados Unidos estándar de siete dígitos con el código de área.
using System;
Module Example
Public Sub Main()
Dim number As Long = 8009999999
Dim fmt As String = "000-000-0000"
Console.WriteLine(number.ToString(fmt))
End Sub
End Module
' The example displays the following output:
Aunque las cadenas de formato estándar pueden satisfacer generalmente la mayoría de las necesidades de
formato para los tipos definidos por la aplicación, también puede definir especificadores de formato
personalizados para dar formato a sus tipos.
Cadenas de formato y tipos de .NET
Todos los tipos numéricos (es decir, los tipos Byte, Decimal, Double, Int16, Int32, Int64, SByte, Single, UInt16,
UInt32, UInt64 y BigInteger), así como DateTime, DateTimeOffset, TimeSpan, Guidy todos los tipos de
enumeración, son compatibles con el formato con cadenas de formato. Para obtener información sobre las
cadenas de formato específicas que admite cada tipo, consulte los temas siguientes:
T IT L E DEF IN IC IÓ N
Standard Numeric Format Strings Describe cadenas de formato estándar que crean
representaciones de cadena usadas con frecuencia de valores
numéricos.
Custom Numeric Format Strings Describe cadenas de formato personalizado que crean
formatos específicos de la aplicación para valores numéricos.
Cadenas con formato de fecha y hora estándar Describe cadenas de formato estándar que crean
representaciones de cadena usadas con frecuencia de valores
DateTime y DateTimeOffset.
Cadenas con formato de fecha y hora personalizado Describe cadenas de formato personalizado que crean
formatos específicos de la aplicación para valores DateTime y
DateTimeOffset.
Cadenas de formato TimeSpan estándar Describe cadenas de formato estándar que crean
representaciones de cadena usadas con frecuencia de
intervalos de tiempo.
Cadenas de formato TimeSpan personalizado Describe cadenas de formato personalizado que crean
formatos específicos de la aplicación para intervalos de
tiempo.
Enumeration Format Strings Describe cadenas de formato estándar que se usan para
crear representaciones de cadena de valores de
enumeración.
Imports System.Globalization
La interfaz IFormatProvider incluye un método, GetFormat(Type), que tiene un único parámetro que especifica el
tipo de objeto que proporciona información de formato. Si el método puede proporcionar un objeto de ese tipo,
lo devuelve. De lo contrario, devuelve una referencia nula ( Nothing en Visual Basic).
IFormatProvider.GetFormat es un método de devolución de llamada. Al llamar a una sobrecarga del método
ToString que incluye un parámetro de IFormatProvider , se llama al método GetFormat de ese objeto
IFormatProvider . El método GetFormat devuelve un objeto que proporciona al método formatType la
información de formato necesaria especificada por su parámetro ToString .
Varios métodos de formato o de conversión de cadenas incluyen un parámetro de tipo IFormatProviderpero, en
muchos casos, se omite el valor del parámetro cuando se llama al método. En la tabla siguiente se muestran
algunos métodos de formato que usan el parámetro y el tipo del objeto Type que pasan al método
IFormatProvider.GetFormat .
M ÉTO DO T IP O DE PA RÁ M ET RO FORMATTYPE
String.Format System.ICustomFormatter
StringBuilder.AppendFormat System.ICustomFormatter
NOTE
Los métodos ToString de los tipos numéricos y los tipos de fecha y hora se sobrecargan y solo algunas de las
sobrecargas incluyen un parámetro IFormatProvider . Si un método no tiene un parámetro de tipo IFormatProvider, se
pasa en su lugar el objeto devuelto por la propiedad CultureInfo.CurrentCulture . Por ejemplo, una llamada al método
Int32.ToString() predeterminado resultará finalmente en una llamada similar a esta:
Int32.ToString("G", System.Globalization.CultureInfo.CurrentCulture) .
Imports System.Globalization
Imports System.Threading
Module Example
Public Sub Main()
Dim cultureNames() As String = {"en-US", "fr-FR", "es-MX", "de-DE"}
Dim value As Decimal = 1043.17d
using System;
using System.Globalization;
Imports System.Globalization
Module Example
Public Sub Main()
Dim value As Double = 1043.62957
Dim cultureNames() As String = {"en-US", "en-GB", "ru", "fr"}
Formato que tiene en cuenta las referencias culturales de valores de fecha y hora
De forma predeterminada, el formato de los valores de fecha y hora tiene en cuenta las referencias culturales. Si
no especifica una referencia cultural cuando llama a un método de formato, se utilizan las convenciones de
formato de la referencia cultural del subproceso actual. Esto se muestra en el ejemplo siguiente, que cambia la
referencia cultural del subproceso actual cuatro veces y después llama al método DateTime.ToString(String) . En
cada caso, la cadena resultante refleja las convenciones de formato de la referencia cultural actual. Esto se debe
a que los métodos DateTime.ToString(), DateTime.ToString(String), DateTimeOffset.ToString()y
DateTimeOffset.ToString(String) encapsulan llamadas a los métodos DateTime.ToString(String, IFormatProvider)
y DateTimeOffset.ToString(String, IFormatProvider) .
using System;
using System.Globalization;
using System.Threading;
Module Example
Public Sub Main()
Dim cultureNames() As String = {"en-US", "fr-FR", "es-MX", "de-DE"}
Dim dateToFormat As Date = #5/28/2012 11:30AM#
También se puede dar formato a un valor de fecha y hora para una referencia cultural concreta llamando a una
sobrecarga de DateTime.ToString o DateTimeOffset.ToString que tenga un parámetro provider y pasándole uno
de los elementos siguientes:
Un objeto CultureInfo que representa la referencia cultural cuyas convenciones de formato se van a usar.
Su método CultureInfo.GetFormat devuelve el valor de la propiedad CultureInfo.DateTimeFormat , que es
el objeto DateTimeFormatInfo que proporciona información sobre el formato de una referencia cultural
específica para valores de fecha y hora.
Un objeto DateTimeFormatInfo que define las convenciones de formato de la referencia cultural que se
van a usar. Su método GetFormat devuelve una instancia de sí mismo.
En el siguiente ejemplo se utilizan objetos DateTimeFormatInfo que representan las referencias culturales de
inglés (Estados Unidos) e inglés (Reino Unido), y las referencias culturales neutras de francés y ruso para dar
formato a una fecha.
using System;
using System.Globalization;
Imports System.Globalization
Module Example
Public Sub Main()
Dim dat1 As Date = #5/28/2012 11:30AM#
Dim cultureNames() As String = {"en-US", "en-GB", "ru", "fr"}
Interfaz IFormattable
Normalmente, los tipos que sobrecargan el método ToString con una cadena de formato y un parámetro
IFormatProvider también implementan la interfaz IFormattable . Esta interfaz tiene un solo miembro,
IFormattable.ToString(String, IFormatProvider), que incluye una cadena de formato y un proveedor de formato
como parámetros.
La implementación de la interfaz IFormattable para su clase definida por la aplicación ofrece dos ventajas:
Compatibilidad con la conversión de cadenas por la clase Convert . Las llamadas a los métodos
Convert.ToString(Object) y Convert.ToString(Object, IFormatProvider) llaman automáticamente a su
implementación de IFormattable .
Compatibilidad con formatos compuestos. Si se usa un elemento de formato que incluye una cadena de
formato para dar formato a su tipo personalizado, Common Language Runtime llama automáticamente a
su implementación de IFormattable y le pasa la cadena de formato. Para obtener más información sobre
los formatos compuestos con métodos como String.Format o Console.WriteLine, vea la sección Formatos
compuestos .
En el ejemplo siguiente se define una clase Temperature que implementa la interfaz IFormattable . Admite los
especificadores de formato "C" o "G" para mostrar la temperatura en grados centígrados, el especificador de
formato "F" para mostrar la temperatura en grados Fahrenheit y el especificador de formato "K" para mostrar la
temperatura en grados Kelvin.
using System;
using System.Globalization;
if (provider == null)
provider = NumberFormatInfo.CurrentInfo;
switch (format)
{
// Convert temperature to Fahrenheit and return string.
case "F":
return this.Fahrenheit.ToString("N2", provider) + "°F";
// Convert temperature to Kelvin and return string.
case "K":
return this.Kelvin.ToString("N2", provider) + "K";
// Return temperature in Celsius.
case "C":
case "G":
return this.Celsius.ToString("N2", provider) + "°C";
default:
throw new FormatException(String.Format("The '{0}' format string is not supported.", format));
}
}
}
Imports System.Globalization
En el ejemplo siguiente se crea una instancia de un objeto Temperature . A continuación, llama al método
ToString y usa varias cadenas de formato compuesto para obtener representaciones de cadena diferentes de un
objeto Temperature . Cada una de estas llamadas al método, a su vez, llama a la implementación de
IFormattable de la clase Temperature .
Formatos compuestos
Algunos métodos, como String.Format y StringBuilder.AppendFormat, admiten formatos compuestos. Una
cadena de formato compuesto es un tipo de plantilla que devuelve una sola cadena que incorpora la
representación de cadena de cero, uno o más objetos. Cada objeto se representa en la cadena de formato
compuesto mediante un elemento de formato indizado. El índice del elemento de formato corresponde a la
posición del objeto que representa en la lista de parámetros del método. Los índices son de base cero. Por
ejemplo, en la siguiente llamada al método String.Format , el primer elemento de formato, {0:D} se reemplaza
con la representación de cadena de thatDate ; el segundo elemento de formato, {1} , se reemplaza con la
representación de cadena de item1 y el tercer elemento de formato, {2:C2} , se reemplaza con la
representación de cadena de item1.Value .
Tenga en cuenta que, si están presentes tanto el componente de cadena de alineación como el
componente de cadena de formato, el primero precede al último (por ejemplo, {0,-20:g} ).
Para más información sobre los formatos compuestos, vea Formatos compuestos.
byte[] bytes;
string output = null;
return output.Trim();
}
}
Public Class ByteByByteFormatter : Implements IFormatProvider, ICustomFormatter
Public Function GetFormat(formatType As Type) As Object _
Implements IFormatProvider.GetFormat
If formatType Is GetType(ICustomFormatter) Then
Return Me
Else
Return Nothing
End If
End Function
Return output.Trim()
End Function
End Class
En el ejemplo siguiente se usa la clase ByteByByteFormatter para dar formato a valores enteros. Observe que en
el ejemplo no se llama explícitamente al método ICustomFormatter.Format más de una vez en la segunda
llamada al método String.Format(IFormatProvider, String, Object[]) y que el proveedor NumberFormatInfo
predeterminado se usa en la tercera llamada al método porque el método ByteByByteFormatter.Format no
reconoce la cadena de formato "N0" y devuelve una referencia nula ( Nothing en Visual Basic).
public class Example
{
public static void Main()
{
long value = 3210662321;
byte value1 = 214;
byte value2 = 19;
Temas relacionados
T IT L E DEF IN IC IÓ N
Standard Numeric Format Strings Describe cadenas de formato estándar que crean
representaciones de cadena usadas con frecuencia de valores
numéricos.
Custom Numeric Format Strings Describe cadenas de formato personalizado que crean
formatos específicos de la aplicación para valores numéricos.
Cadenas con formato de fecha y hora estándar Describe cadenas de formato estándar que crean
representaciones de cadena usadas con frecuencia de valores
DateTime .
Custom Date and Time Format Strings Describe cadenas de formato personalizado que crean
formatos específicos de la aplicación para valores DateTime .
Cadenas de formato TimeSpan estándar Describe cadenas de formato estándar que crean
representaciones de cadena usadas con frecuencia de
intervalos de tiempo.
T IT L E DEF IN IC IÓ N
Cadenas de formato TimeSpan personalizado Describe cadenas de formato personalizado que crean
formatos específicos de la aplicación para intervalos de
tiempo.
Enumeration Format Strings Describe cadenas de formato estándar que se usan para
crear representaciones de cadena de valores de
enumeración.
Formatos compuestos Describe cómo incrustar uno o más valores con formato en
una cadena. Posteriormente se puede mostrar la cadena en
la consola o escrita en una secuencia.
Analizar cadenas Describe cómo inicializar objetos en los valores descritos por
representaciones de cadena de dichos objetos. El análisis es
la operación inversa de la aplicación de formato.
Referencia
System.IFormattable
System.IFormatProvider
System.ICustomFormatter
Cadenas con formato numérico estándar
16/09/2020 • 49 minutes to read • Edit Online
Las cadenas de formato numérico estándar se utilizan para dar formato a tipos numéricos comunes. La forma de
una cadena de formato numérico estándar es Axx , donde:
A es un carácter alfabético único denominado especificador de formato. Cualquier cadena de formato
numérico que contenga más de un carácter alfabético, incluido el espacio en blanco, se interpreta como
una cadena de formato numérico personalizado. Para obtener más información, consulte Cadenas con
formato numérico personalizado.
xx es un entero opcional denominado especificador de precisión. El especificador de precisión está
comprendido entre el 0 y el 99 y afecta al número de dígitos del resultado. Observe que el especificador
de precisión controla el número de dígitos en la representación de cadena de un número. No redondea el
número en sí. Para realizar una operación de redondeo, use el método Math.Ceiling, Math.Floor o
Math.Round.
Cuando el especificador de precisión controla el número de dígitos fraccionarios de la cadena de
resultado, esta refleja un número redondeado al resultado representable más cercano al resultado de
precisión infinita. En el caso de que haya dos resultados representables igualmente cercanos:
En .NET Framework y .NET Core (hasta la versión 2.0) , el runtime selecciona el resultado con el
dígito menos significativo más elevado (es decir, usando MidpointRounding.AwayFromZero).
En .NET Core 2.1 y versiones posteriores , el runtime selecciona el resultado con un dígito menos
significativo par (es decir, usando MidpointRounding.ToEven).
NOTE
El especificador de precisión determina el número de dígitos de la cadena de resultado. Para rellenar una cadena de
resultado con espacios iniciales o finales, use la característica formatos compuestos y defina un componente de
alineación en el elemento de formato.
En la tabla siguiente se describen los especificadores de formato numérico estándar y se muestran los
resultados de ejemplo generados por cada especificador de formato. Consulte la sección Notas para obtener
información adicional sobre cómo usar las cadenas con formato numérico estándar y la sección Ejemplo para
ver una ilustración completa de su uso.
ESP EC IF IC A DO R DE
F O RM ATO N O M B RE DESC RIP C IÓ N E JEM P LO S
"D" o "d" Decimal Resultado: dígitos enteros 1234 ("D") -> 1234
con signo negativo
opcional. -1234 ("D6") -> -001234
Especificador de precisión:
número mínimo de dígitos.
Especificador de precisión
predeterminado: número
mínimo de dígitos
necesario.
Más información:
Especificador de formato
decimal ("D").
ESP EC IF IC A DO R DE
F O RM ATO N O M B RE DESC RIP C IÓ N E JEM P LO S
Más información:
Especificador de formato
exponencial ("E").
"F" o "f" Punto fijo Resultado: dígitos integrales 1234.567 ("F", en-US) ->
y decimales con signo 1234.57
negativo opcional.
1234.567 ("F", de-DE) ->
Compatible con: todos los 1234,57
tipos numéricos.
1234 ("F1", en-US) ->
Especificador de precisión: 1234.0
número de dígitos
decimales. 1234 ("F1", de-DE) ->
1234,0
Especificador de precisión
predeterminado: Definido -1234.56 ("F4", en-US) -> -
por 1234.5600
NumberFormatInfo.Number
DecimalDigits. -1234.56 ("F4", de-DE) -> -
1234,5600
Más información:
Especificador de formato de
punto fijo ("F").
"N" o "n" número Resultado: dígitos integrales 1234.567 ("N", en-US) ->
y decimales, separadores de 1,234.57
grupos y un separador
decimal con signo negativo 1234.567 ("N", ru-RU) -> 1
opcional. 234,57
Más información:
Especificador de formato
numérico ("N").
Especificador de precisión
predeterminado: Definido
por
NumberFormatInfo.Percent
DecimalDigits.
Más información:
Especificador de formato de
porcentaje ("P").
ESP EC IF IC A DO R DE
F O RM ATO N O M B RE DESC RIP C IÓ N E JEM P LO S
"R" o "r" Acción de ida y vuelta Resultado: cadena que 123456789.12345678 ("R")
puede aplicar acciones de -> 123456789.12345678
ida y vuelta (round-trip) a
un número idéntico. -1234567890.12345678
("R") -> -
Compatible con: Single, 1234567890.1234567
Double y BigInteger.
Más información:
Especificador de formato de
operación de ida y vuelta
("R").
Más información:
Especificador de formato
hexadecimal ("X").
Una cadena de formato numérico estándar se puede usar para definir el formato de un valor numérico de una
de dos maneras:
Se puede pasar a una sobrecarga del método ToString que tiene un parámetro format . En el ejemplo
siguiente se da formato a un valor numérico como una cadena de divisa en la referencia cultural actual
(en este caso, en-US).
Decimal value = static_cast<Decimal>(123.456);
Console::WriteLine(value.ToString("C2"));
// Displays $123.46
Opcionalmente, puede facilitar un argumento alignment para especificar el ancho del campo numérico y
si su valor está alineado a izquierda o derecha. En el ejemplo siguiente se alinea a la izquierda un valor de
moneda en un campo de 28 caracteres y se alinea a la derecha un valor de moneda en un campo de 14
caracteres.
Console::WriteLine(value.ToString("C3", CultureInfo::CurrentCulture));
Console::WriteLine(value.ToString("C3",
CultureInfo::CreateSpecificCulture("da-DK")));
// The example displays the following output on a system whose
// current culture is English (United States):
// $12,345.68
// $12,345.679
// kr 12.345,679
Console.WriteLine(value.ToString("C3", CultureInfo.CurrentCulture));
Console.WriteLine(value.ToString("C3",
CultureInfo.CreateSpecificCulture("da-DK")));
// The example displays the following output on a system whose
// current culture is English (United States):
// $12,345.68
// $12,345.679
// 12.345,679 kr
Console.WriteLine(value.ToString("C3", CultureInfo.CurrentCulture))
Console.WriteLine(value.ToString("C3", _
CultureInfo.CreateSpecificCulture("da-DK")))
' The example displays the following output on a system whose
' current culture is English (United States):
' $12,345.68
' $12,345.679
' kr 12.345,679
Volver a la tabla
int value;
value = 12345;
Console::WriteLine(value.ToString("D"));
// Displays 12345
Console::WriteLine(value.ToString("D8"));
// Displays 00012345
value = -12345;
Console::WriteLine(value.ToString("D"));
// Displays -12345
Console::WriteLine(value.ToString("D8"));
// Displays -00012345
int value;
value = 12345;
Console.WriteLine(value.ToString("D"));
// Displays 12345
Console.WriteLine(value.ToString("D8"));
// Displays 00012345
value = -12345;
Console.WriteLine(value.ToString("D"));
// Displays -12345
Console.WriteLine(value.ToString("D8"));
// Displays -00012345
value = 12345
Console.WriteLine(value.ToString("D"))
' Displays 12345
Console.WriteLine(value.ToString("D8"))
' Displays 00012345
value = -12345
Console.WriteLine(value.ToString("D"))
' Displays -12345
Console.WriteLine(value.ToString("D8"))
' Displays -00012345
Volver a la tabla
Console::WriteLine(value.ToString("E10", CultureInfo::InvariantCulture));
// Displays 1.2345678900E+004
Console::WriteLine(value.ToString("e4", CultureInfo::InvariantCulture));
// Displays 1.2346e+004
Console::WriteLine(value.ToString("E",
CultureInfo::CreateSpecificCulture("fr-FR")));
// Displays 1,234568E+004
Console.WriteLine(value.ToString("E10", CultureInfo.InvariantCulture));
// Displays 1.2345678900E+004
Console.WriteLine(value.ToString("e4", CultureInfo.InvariantCulture));
// Displays 1.2346e+004
Console.WriteLine(value.ToString("E",
CultureInfo.CreateSpecificCulture("fr-FR")));
// Displays 1,234568E+004
Dim value As Double = 12345.6789
Console.WriteLine(value.ToString("E", CultureInfo.InvariantCulture))
' Displays 1.234568E+004
Console.WriteLine(value.ToString("E10", CultureInfo.InvariantCulture))
' Displays 1.2345678900E+004
Console.WriteLine(value.ToString("e4", CultureInfo.InvariantCulture))
' Displays 1.2346e+004
Console.WriteLine(value.ToString("E", _
CultureInfo.CreateSpecificCulture("fr-FR")))
' Displays 1,234568E+004
Volver a la tabla
En el ejemplo siguiente se da formato a un valor Double e Int32 con el especificador de formato de punto fijo:
int integerNumber;
integerNumber = 17843;
Console::WriteLine(integerNumber.ToString("F",
CultureInfo::InvariantCulture));
// Displays 17843.00
integerNumber = -29541;
Console::WriteLine(integerNumber.ToString("F3",
CultureInfo::InvariantCulture));
// Displays -29541.000
double doubleNumber;
doubleNumber = 18934.1879;
Console::WriteLine(doubleNumber.ToString("F", CultureInfo::InvariantCulture));
// Displays 18934.19
Console::WriteLine(doubleNumber.ToString("F0", CultureInfo::InvariantCulture));
// Displays 18934
doubleNumber = -1898300.1987;
Console::WriteLine(doubleNumber.ToString("F1", CultureInfo::InvariantCulture));
// Displays -1898300.2
Console::WriteLine(doubleNumber.ToString("F3",
CultureInfo::CreateSpecificCulture("es-ES")));
// Displays -1898300,199
int integerNumber;
integerNumber = 17843;
Console.WriteLine(integerNumber.ToString("F",
CultureInfo.InvariantCulture));
// Displays 17843.00
integerNumber = -29541;
Console.WriteLine(integerNumber.ToString("F3",
CultureInfo.InvariantCulture));
// Displays -29541.000
double doubleNumber;
doubleNumber = 18934.1879;
Console.WriteLine(doubleNumber.ToString("F", CultureInfo.InvariantCulture));
// Displays 18934.19
Console.WriteLine(doubleNumber.ToString("F0", CultureInfo.InvariantCulture));
// Displays 18934
doubleNumber = -1898300.1987;
Console.WriteLine(doubleNumber.ToString("F1", CultureInfo.InvariantCulture));
// Displays -1898300.2
Console.WriteLine(doubleNumber.ToString("F3",
CultureInfo.CreateSpecificCulture("es-ES")));
// Displays -1898300,199
Dim integerNumber As Integer
integerNumber = 17843
Console.WriteLine(integerNumber.ToString("F", CultureInfo.InvariantCulture))
' Displays 17843.00
integerNumber = -29541
Console.WriteLine(integerNumber.ToString("F3", CultureInfo.InvariantCulture))
' Displays -29541.000
Console.WriteLine(doubleNumber.ToString("F0", CultureInfo.InvariantCulture))
' Displays 18934
doubleNumber = -1898300.1987
Console.WriteLine(doubleNumber.ToString("F1", CultureInfo.InvariantCulture))
' Displays -1898300.2
Console.WriteLine(doubleNumber.ToString("F3", _
CultureInfo.CreateSpecificCulture("es-ES")))
' Displays -1898300,199
Volver a la tabla
Int64 19 dígitos
UInt64 20 dígitos
Single 7 dígitos
Double 15 dígitos
Decimal 29 dígitos
La notación de punto fijo se utiliza si el exponente que resultaría de la expresión del número en notación
científica es mayor que -5 y menor que el especificador de precisión, de lo contrario se utiliza la notación
científica. El resultado contiene un separador decimal si es necesario y se omiten los ceros finales después de ese
separador. Si el especificador de precisión está presente y el número de dígitos significativos del resultado
supera la precisión especificada, los dígitos finales sobrantes se quitan mediante redondeo.
Sin embargo, si el número es Decimal y se omite el especificador de precisión, siempre se usa la notación de
punto fijo y se conservan los ceros finales.
Si se usa la notación científica, el exponente del resultado lleva el prefijo "E" si el especificador de formato es "G",
o "e" si el especificador de formato es "g". El exponente contiene un mínimo de dos dígitos. Esto difiere del
formato para la notación científica que genera el especificador de formato exponencial, que incluye un mínimo
de tres dígitos en el exponente.
Tenga en cuenta que, cuando se usa con un valor Double, el especificador de formato "G17" garantiza que el
valor Double original realice un recorrido de ida y vuelta correctamente. Esto se debe a que Double es un
número de punto flotante de doble precisión compatible con IEEE 754-2008 ( binary64 ) que ofrece un máximo
de 17 dígitos de precisión significativos. Se recomienda su uso en lugar del especificador de formato "R", ya que,
en algunos casos, "R" no logra realizar un recorrido de ida y vuelta correcto por los valores de números de
punto flotante de doble precisión. Esto se ilustra en el siguiente ejemplo.
Module Example
Public Sub Main()
Dim original As Double = 0.84551240822557006
Dim rSpecifier = original.ToString("R")
Dim g17Specifier = original.ToString("G17")
Cuando se usa con un valor Single, el especificador de formato "G9" garantiza que el valor Single original realice
un recorrido de ida y vuelta correctamente. Esto se debe a que Single es un número de punto flotante de
precisión sencilla compatible con IEEE 754-2008 ( binary32 ) que ofrece hasta nueve dígitos de precisión
significativos. Por motivos de rendimiento, se recomienda su uso en lugar del especificador de formato "R".
La información de formato del objeto NumberFormatInfo actual afecta a la cadena de resultado. En la tabla
siguiente se enumeran las propiedades de NumberFormatInfo que controlan el formato de la cadena de
resultado.
P RO P IEDA D DE N UM B ERF O RM AT IN F O DESC RIP C IÓ N
En el ejemplo siguiente se da formato a valores de punto flotante ordenados con el especificador de formato
general:
double number;
number = 12345.6789;
Console::WriteLine(number.ToString("G", CultureInfo::InvariantCulture));
// Displays 12345.6789
Console::WriteLine(number.ToString("G",
CultureInfo::CreateSpecificCulture("fr-FR")));
// Displays 12345,6789
Console::WriteLine(number.ToString("G7", CultureInfo::InvariantCulture));
// Displays 12345.68
number = .0000023;
Console::WriteLine(number.ToString("G", CultureInfo::InvariantCulture));
// Displays 2.3E-06
Console::WriteLine(number.ToString("G",
CultureInfo::CreateSpecificCulture("fr-FR")));
// Displays 2,3E-06
number = .0023;
Console::WriteLine(number.ToString("G", CultureInfo::InvariantCulture));
// Displays 0.0023
number = 1234;
Console::WriteLine(number.ToString("G2", CultureInfo::InvariantCulture));
// Displays 1.2E+03
number = Math::PI;
Console::WriteLine(number.ToString("G5", CultureInfo::InvariantCulture));
// Displays 3.1416
double number;
number = 12345.6789;
Console.WriteLine(number.ToString("G", CultureInfo.InvariantCulture));
// Displays 12345.6789
Console.WriteLine(number.ToString("G",
CultureInfo.CreateSpecificCulture("fr-FR")));
// Displays 12345,6789
Console.WriteLine(number.ToString("G7", CultureInfo.InvariantCulture));
// Displays 12345.68
number = .0000023;
Console.WriteLine(number.ToString("G", CultureInfo.InvariantCulture));
// Displays 2.3E-06
Console.WriteLine(number.ToString("G",
CultureInfo.CreateSpecificCulture("fr-FR")));
// Displays 2,3E-06
number = .0023;
Console.WriteLine(number.ToString("G", CultureInfo.InvariantCulture));
// Displays 0.0023
number = 1234;
Console.WriteLine(number.ToString("G2", CultureInfo.InvariantCulture));
// Displays 1.2E+03
number = Math.PI;
Console.WriteLine(number.ToString("G5", CultureInfo.InvariantCulture));
// Displays 3.1416
number = 12345.6789
Console.WriteLine(number.ToString("G", CultureInfo.InvariantCulture))
' Displays 12345.6789
Console.WriteLine(number.ToString("G", _
CultureInfo.CreateSpecificCulture("fr-FR")))
' Displays 12345,6789
Console.WriteLine(number.ToString("G7", CultureInfo.InvariantCulture))
' Displays 12345.68
number = .0000023
Console.WriteLine(number.ToString("G", CultureInfo.InvariantCulture))
' Displays 2.3E-06
Console.WriteLine(number.ToString("G", _
CultureInfo.CreateSpecificCulture("fr-FR")))
' Displays 2,3E-06
number = .0023
Console.WriteLine(number.ToString("G", CultureInfo.InvariantCulture))
' Displays 0.0023
number = 1234
Console.WriteLine(number.ToString("G2", CultureInfo.InvariantCulture))
' Displays 1.2E+03
number = Math.Pi
Console.WriteLine(number.ToString("G5", CultureInfo.InvariantCulture))
' Displays 3.1416
Volver a la tabla
Especificador de formato numérico ("N")
El especificador de formato numérico ("N") convierte un número en una cadena con el formato "-
d,ddd,ddd.ddd…", donde "-" indica el símbolo de número negativo, si es necesario; "d", representa cada dígito (0-
9); "," es el separador de grupo; y "." es el símbolo de punto decimal. El especificador de precisión indica el
número deseado de dígitos después del separador decimal. Si se omite el especificador de precisión, el número
de posiciones decimales está definido por la propiedad NumberFormatInfo.NumberDecimalDigits actual.
La información de formato del objeto NumberFormatInfo actual afecta a la cadena de resultado. En la tabla
siguiente se enumeran las propiedades de NumberFormatInfo que controlan el formato de la cadena de
resultado.
En el ejemplo siguiente se da formato a valores de punto flotante ordenados con el especificador de formato
numérico:
Volver a la tabla
En el ejemplo siguiente se da formato a valores de punto flotante con el especificador de formato de porcentaje:
double number = .2468013;
Console::WriteLine(number.ToString("P", CultureInfo::InvariantCulture));
// Displays 24.68 %
Console::WriteLine(number.ToString("P",
CultureInfo::CreateSpecificCulture("hr-HR")));
// Displays 24,68%
Console::WriteLine(number.ToString("P1", CultureInfo::InvariantCulture));
// Displays 24.7 %
Volver a la tabla
#using <System.Numerics.dll>
void main()
{
BigInteger value = BigInteger::Pow(Int64::MaxValue, 2);
Console::WriteLine(value.ToString("R"));
}
// The example displays the following output:
// 85070591730234615847396907784232501249
using System;
using System.Numerics;
Imports System.Numerics
Module Example
Public Sub Main()
Dim value = BigInteger.Pow(Int64.MaxValue, 2)
Console.WriteLine(value.ToString("R"))
End Sub
End Module
' The example displays the following output:
' 85070591730234615847396907784232501249
IMPORTANT
En algunos casos, los valores Double con el formato de cadena numérica estándar "R" no realizan correctamente la
operación de ida y vuelta si se compilan usando los modificadores /platform:x64 o /platform:anycpu y se ejecutan
en sistemas de 64 bits. Para más información, consulte el siguiente párrafo.
Para solucionar el problema de los valores Double con formato de cadena numérico estándar “R” que no hacen
correctamente el viaje de ida y vuelta si se compilan con conmutadores /platform:x64 o /platform:anycpu y se
ejecutan en sistemas de 64 bits, puede dar formato a los valores Double usando la cadena de formato numérico
estándar “G17”. En el ejemplo siguiente se usa la cadena de formato "R" con un valor Double que no realiza
correctamente la operación de ida y vuelta, y usa también la cadena de formato "G17" para realizar
correctamente la operación de ida y vuelta del valor original:
Console.WriteLine("Attempting to round-trip a Double with 'R':");
double initialValue = 0.6822871999174;
string valueString = initialValue.ToString("R",
CultureInfo.InvariantCulture);
double roundTripped = double.Parse(valueString,
CultureInfo.InvariantCulture);
Console.WriteLine("{0:R} = {1:R}: {2}\n",
initialValue, roundTripped, initialValue.Equals(roundTripped));
Imports System.Globalization
Module Example
Public Sub Main()
Console.WriteLine("Attempting to round-trip a Double with 'R':")
Dim initialValue As Double = 0.6822871999174
Dim valueString As String = initialValue.ToString("R",
CultureInfo.InvariantCulture)
Dim roundTripped As Double = Double.Parse(valueString,
CultureInfo.InvariantCulture)
Console.WriteLine("{0:R} = {1:R}: {2}",
initialValue, roundTripped, initialValue.Equals(roundTripped))
Console.WriteLine()
Volver a la tabla
int value;
value = 0x2045e;
Console::WriteLine(value.ToString("x"));
// Displays 2045e
Console::WriteLine(value.ToString("X"));
// Displays 2045E
Console::WriteLine(value.ToString("X8"));
// Displays 0002045E
value = 123456789;
Console::WriteLine(value.ToString("X"));
// Displays 75BCD15
Console::WriteLine(value.ToString("X2"));
// Displays 75BCD15
int value;
value = 0x2045e;
Console.WriteLine(value.ToString("x"));
// Displays 2045e
Console.WriteLine(value.ToString("X"));
// Displays 2045E
Console.WriteLine(value.ToString("X8"));
// Displays 0002045E
value = 123456789;
Console.WriteLine(value.ToString("X"));
// Displays 75BCD15
Console.WriteLine(value.ToString("X2"));
// Displays 75BCD15
value = &h2045e
Console.WriteLine(value.ToString("x"))
' Displays 2045e
Console.WriteLine(value.ToString("X"))
' Displays 2045E
Console.WriteLine(value.ToString("X8"))
' Displays 0002045E
value = 123456789
Console.WriteLine(value.ToString("X"))
' Displays 75BCD15
Console.WriteLine(value.ToString("X2"))
' Displays 75BCD15
Volver a la tabla
Notas
Configuración del Panel de control
Los valores de configuración del elemento Configuración regional y de idioma del Panel de control influyen
en la cadena de resultado generada por una operación de formato. Esas configuraciones se usan para inicializar
el objeto NumberFormatInfo asociado a la referencia cultural del subproceso actual, que proporciona valores
que se usan para controlar el formato. Los equipos que usan configuraciones diferentes generarán cadenas de
resultado distintas.
Asimismo, si se utiliza el constructor CultureInfo(String) para crear instancias de un nuevo objeto CultureInfo
que representa la misma referencia cultural que la referencia cultural del sistema actual, cualquier
personalización establecida por el elemento Configuración regional y de idioma del Panel de control se
aplicará al nuevo objeto CultureInfo. Puede usar el constructor CultureInfo(String, Boolean) para crear un objeto
CultureInfo que no refleje las personalizaciones de un sistema.
Propiedades NumberFormatInfo
El formato se ve influenciado por las propiedades del objeto NumberFormatInfo actual, proporcionado
implícitamente por la referencia cultural del subproceso actual o explícitamente por el parámetro
IFormatProvider del método que invoca el formato. Especifique un objeto NumberFormatInfo o CultureInfo para
dicho parámetro.
NOTE
Para obtener información sobre la personalización de patrones o cadenas que se usan para dar formato a valores
numéricos, vea el tema de la clase NumberFormatInfo.
Ejemplo
NOTE
Algunos de los ejemplos de C# de este artículo se ejecutan en el ejecutor de código en línea y área de juegos de Try.NET.
Haga clic en el botón Ejecutar para ejecutar un ejemplo en una ventana interactiva. Una vez que se ejecuta el código,
puede modificar y ejecutar el código modificado si vuelve a hacer clic en Ejecutar . El código modificado se ejecuta en la
ventana interactiva o, si se produce un error en la compilación, en la ventana interactiva se muestran todos los mensajes
de error del compilador de C#.
En el ejemplo siguiente se da formato a un valor numérico integral y de punto flotante mediante la referencia
cultural en-US y todos los especificadores de formato numérico estándar. En este ejemplo se usan dos tipos
numéricos concretos (Double y Int32), pero se obtendrían resultados similares con cualquiera de los demás
tipos base numéricos (Byte, SByte, Int16, Int32, Int64, UInt16, UInt32, UInt64, BigInteger, Decimal y Single).
// Display string representations of numbers for en-us culture
CultureInfo ci = new CultureInfo("en-us");
Imports System.Globalization
Imports System.Threading
Module NumericFormats
Public Sub Main()
' Display string representations of numbers for en-us culture
Dim ci As New CultureInfo("en-us")
Vea también
NumberFormatInfo
Cadenas con formato numérico personalizado
Aplicación de formato a tipos
Cómo: Rellenar un número con ceros a la izquierda
Formatos compuestos
Ejemplo: Utilidad de formato WinForms de .NET Core (C#)
Ejemplo: Utilidad de formato WinForms de .NET Core (Visual Basic)
Cadenas con formato numérico personalizado
16/09/2020 • 38 minutes to read • Edit Online
Puede crear una cadena de formato numérico personalizado, formada por uno o varios especificadores
numéricos personalizados, para definir cómo debe darse formato a los datos numéricos. Una cadena de formato
numérico personalizado es cualquier cadena que no sea una cadena de formato numérico estándar.
Algunas sobrecargas del método ToString de todos los tipos numéricos admiten las cadenas de formato
numérico personalizado. Por ejemplo, se puede proporcionar una cadena de formato numérico a los métodos
ToString(String) y ToString(String, IFormatProvider) del tipo Int32 . La característica de formato compuesto de
.NET, que utilizan algunos métodos Write y WriteLine de las clases Console y StreamWriter, el método
String.Format y el método StringBuilder.AppendFormat, admite también cadenas de formato numérico
personalizado. La característica interpolación de cadenas admite también cadenas de formato numérico
personalizado.
TIP
Puede descargar la Utilidad de formato , que es una aplicación de .NET Core Windows Forms que permite aplicar
cadenas de formato a valores numéricos o de fecha y hora, y que muestra la cadena de resultado. El código fuente está
disponible para C# y Visual Basic.
En la tabla siguiente se describen los especificadores de formato numérico personalizado y se muestran las
salidas de ejemplo generadas por cada especificador de formato. Vea la sección Notas para obtener información
adicional sobre cómo usar las cadenas de formato numérico personalizado y la sección Ejemplo para ver una
ilustración completa de su uso.
ESP EC IF IC A DO R DE
F O RM ATO N O M B RE DESC RIP C IÓ N E JEM P LO S
"0" Marcador de posición cero Reemplaza el cero con el 1234.5678 ("00000") ->
dígito correspondiente si 01235
hay alguno presente; de lo
contrario, el cero aparece en 0.45678 ("0.00", en-US) ->
la cadena de resultado. 0.46
Más información:
Especificador personalizado
"#".
"." Separador decimal Determina la ubicación del 0.45678 ("0.00", en-US) ->
separador decimal en la 0.46
cadena de resultado.
0.45678 ("0.00", fr-FR) ->
Más información: El 0,46
especificador personalizado
".".
Más información:
Especificadores
personalizados "E" y "e".
'cadena' Delimitador de cadena Indica que los caracteres 68 ("# ' grados'") -> 68
literal que encierra se deben grados
"string" copiar en la cadena de
resultado sin modificar. 68 ("# ' grados'") -> 68
grados
Más información: Literales
de carácter.
ESP EC IF IC A DO R DE
F O RM ATO N O M B RE DESC RIP C IÓ N E JEM P LO S
Otros Todos los demás caracteres El carácter se copia en la 68 ("# °") -> 68 °
cadena de resultado sin
modificar.
En las secciones siguientes se proporciona información detallada sobre cada uno de los especificadores de
formato numérico personalizado.
NOTE
Algunos de los ejemplos de C# de este artículo se ejecutan en el ejecutor de código en línea y área de juegos de Try.NET.
Haga clic en el botón Ejecutar para ejecutar un ejemplo en una ventana interactiva. Una vez que se ejecuta el código,
puede modificar y ejecutar el código modificado si vuelve a hacer clic en Ejecutar . El código modificado se ejecuta en la
ventana interactiva o, si se produce un error en la compilación, en la ventana interactiva se muestran todos los mensajes
de error del compilador de C#.
value = 123;
Console::WriteLine(value.ToString("00000"));
Console::WriteLine(String::Format("{0:00000}", value));
// Displays 00123
value = 1.2;
Console::WriteLine(value.ToString("0.00", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:0.00}", value));
// Displays 1.20
Console::WriteLine(value.ToString("00.00", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:00.00}", value));
// Displays 01.20
value = .56;
Console::WriteLine(value.ToString("0.0", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:0.0}", value));
// Displays 0.6
value = 1234567890;
Console::WriteLine(value.ToString("0,0", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:0,0}", value));
// Displays 1,234,567,890
value = 1234567890.123456;
Console::WriteLine(value.ToString("0,0.0", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:0,0.0}", value));
// Displays 1,234,567,890.1
value = 1234.567890;
Console::WriteLine(value.ToString("0,0.00", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:0,0.00}", value));
// Displays 1,234.57
double value;
value = 123;
Console.WriteLine(value.ToString("00000"));
Console.WriteLine(String.Format("{0:00000}", value));
// Displays 00123
value = 1.2;
Console.WriteLine(value.ToString("0.00", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.00}", value));
// Displays 1.20
Console.WriteLine(value.ToString("00.00", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:00.00}", value));
// Displays 01.20
value = .56;
Console.WriteLine(value.ToString("0.0", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.0}", value));
// Displays 0.6
value = 1234567890;
Console.WriteLine(value.ToString("0,0", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0,0}", value));
// Displays 1,234,567,890
value = 1234567890.123456;
Console.WriteLine(value.ToString("0,0.0", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0,0.0}", value));
// Displays 1,234,567,890.1
value = 1234.567890;
Console.WriteLine(value.ToString("0,0.00", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0,0.00}", value));
// Displays 1,234.57
Dim value As Double
value = 123
Console.WriteLine(value.ToString("00000"))
Console.WriteLine(String.Format("{0:00000}", value))
' Displays 00123
value = 1.2
Console.Writeline(value.ToString("0.00", CultureInfo.InvariantCulture))
Console.Writeline(String.Format(CultureInfo.InvariantCulture,
"{0:0.00}", value))
' Displays 1.20
Console.WriteLine(value.ToString("00.00", CultureInfo.InvariantCulture))
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:00.00}", value))
' Displays 01.20
Dim daDK As CultureInfo = CultureInfo.CreateSpecificCulture("da-DK")
Console.WriteLine(value.ToString("00.00", daDK))
Console.WriteLine(String.Format(daDK, "{0:00.00}", value))
' Displays 01,20
value = .56
Console.WriteLine(value.ToString("0.0", CultureInfo.InvariantCulture))
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.0}", value))
' Displays 0.6
value = 1234567890
Console.WriteLine(value.ToString("0,0", CultureInfo.InvariantCulture))
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0,0}", value))
' Displays 1,234,567,890
Dim elGR As CultureInfo = CultureInfo.CreateSpecificCulture("el-GR")
Console.WriteLine(value.ToString("0,0", elGR))
Console.WriteLine(String.Format(elGR, "{0:0,0}", value))
' Displays 1.234.567.890
value = 1234567890.123456
Console.WriteLine(value.ToString("0,0.0", CultureInfo.InvariantCulture))
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0,0.0}", value))
' Displays 1,234,567,890.1
value = 1234.567890
Console.WriteLine(value.ToString("0,0.00", CultureInfo.InvariantCulture))
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0,0.00}", value))
' Displays 1,234.57
Volver a la tabla
double value;
value = 1.2;
Console::WriteLine(value.ToString("#.##", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:#.##}", value));
// Displays 1.2
value = 123;
Console::WriteLine(value.ToString("#####"));
Console::WriteLine(String::Format("{0:#####}", value));
// Displays 123
value = 123456;
Console::WriteLine(value.ToString("[##-##-##]"));
Console::WriteLine(String::Format("{0:[##-##-##]}", value));
// Displays [12-34-56]
value = 1234567890;
Console::WriteLine(value.ToString("#"));
Console::WriteLine(String::Format("{0:#}", value));
// Displays 1234567890
Console::WriteLine(value.ToString("(###) ###-####"));
Console::WriteLine(String::Format("{0:(###) ###-####}", value));
// Displays (123) 456-7890
double value;
value = 1.2;
Console.WriteLine(value.ToString("#.##", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#.##}", value));
// Displays 1.2
value = 123;
Console.WriteLine(value.ToString("#####"));
Console.WriteLine(String.Format("{0:#####}", value));
// Displays 123
value = 123456;
Console.WriteLine(value.ToString("[##-##-##]"));
Console.WriteLine(String.Format("{0:[##-##-##]}", value));
// Displays [12-34-56]
value = 1234567890;
Console.WriteLine(value.ToString("#"));
Console.WriteLine(String.Format("{0:#}", value));
// Displays 1234567890
Console.WriteLine(value.ToString("(###) ###-####"));
Console.WriteLine(String.Format("{0:(###) ###-####}", value));
// Displays (123) 456-7890
Dim value As Double
value = 1.2
Console.WriteLine(value.ToString("#.##", CultureInfo.InvariantCulture))
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#.##}", value))
' Displays 1.2
value = 123
Console.WriteLine(value.ToString("#####"))
Console.WriteLine(String.Format("{0:#####}", value))
' Displays 123
value = 123456
Console.WriteLine(value.ToString("[##-##-##]"))
Console.WriteLine(String.Format("{0:[##-##-##]}", value))
' Displays [12-34-56]
value = 1234567890
Console.WriteLine(value.ToString("#"))
Console.WriteLine(String.Format("{0:#}", value))
' Displays 1234567890
Console.WriteLine(value.ToString("(###) ###-####"))
Console.WriteLine(String.Format("{0:(###) ###-####}", value))
' Displays (123) 456-7890
Para devolver una cadena de resultado en la que los dígitos que faltan o los ceros iniciales se reemplazan por
espacios, use la característica de formato compuesto y especifique un ancho de campo, como se muestra en el
ejemplo siguiente.
void main()
{
Double value = .324;
Console::WriteLine("The value is: '{0,5:#.###}'", value);
}
// The example displays the following output if the current culture
// is en-US:
// The value is: ' .324'
using System;
Volver a la tabla
double value;
value = 1.2;
Console::WriteLine(value.ToString("0.00", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:0.00}", value));
// Displays 1.20
Console::WriteLine(value.ToString("00.00", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:00.00}", value));
// Displays 01.20
Console::WriteLine(value.ToString("00.00",
CultureInfo::CreateSpecificCulture("da-DK")));
Console::WriteLine(String::Format(CultureInfo::CreateSpecificCulture("da-DK"),
"{0:00.00}", value));
// Displays 01,20
value = .086;
Console::WriteLine(value.ToString("#0.##%", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:#0.##%}", value));
// Displays 8.6%
value = 86000;
Console::WriteLine(value.ToString("0.###E+0", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:0.###E+0}", value));
// Displays 8.6E+4
double value;
value = 1.2;
Console.WriteLine(value.ToString("0.00", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.00}", value));
// Displays 1.20
Console.WriteLine(value.ToString("00.00", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:00.00}", value));
// Displays 01.20
Console.WriteLine(value.ToString("00.00",
CultureInfo.CreateSpecificCulture("da-DK")));
Console.WriteLine(String.Format(CultureInfo.CreateSpecificCulture("da-DK"),
"{0:00.00}", value));
// Displays 01,20
value = .086;
Console.WriteLine(value.ToString("#0.##%", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#0.##%}", value));
// Displays 8.6%
value = 86000;
Console.WriteLine(value.ToString("0.###E+0", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.###E+0}", value));
// Displays 8.6E+4
value = 1.2
Console.Writeline(value.ToString("0.00", CultureInfo.InvariantCulture))
Console.Writeline(String.Format(CultureInfo.InvariantCulture,
"{0:0.00}", value))
' Displays 1.20
Console.WriteLine(value.ToString("00.00", CultureInfo.InvariantCulture))
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:00.00}", value))
' Displays 01.20
Console.WriteLine(value.ToString("00.00", _
CultureInfo.CreateSpecificCulture("da-DK")))
Console.WriteLine(String.Format(CultureInfo.CreateSpecificCulture("da-DK"),
"{0:00.00}", value))
' Displays 01,20
value = .086
Console.WriteLine(value.ToString("#0.##%", CultureInfo.InvariantCulture))
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#0.##%}", value))
' Displays 8.6%
value = 86000
Console.WriteLine(value.ToString("0.###E+0", CultureInfo.InvariantCulture))
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.###E+0}", value))
' Displays 8.6E+4
Volver a la tabla
Especificador personalizado ","
El carácter "," actúa como separador de grupos y como especificador de escala numérica.
Separador de grupos: si se especifican una o varias comas dos marcadores de posición de dígitos (0 o #)
que dan formato a los dígitos enteros de un número, se insertará un carácter separador de grupos entre
cada grupo de números en la parte entera de la salida.
Las propiedades NumberGroupSeparator y NumberGroupSizes del objeto NumberFormatInfo actual
determinan el carácter utilizado como separador de grupos de números y el tamaño de cada grupo de
números. Por ejemplo, si se utiliza la cadena "#,#" y la referencia cultural de todos los idiomas para dar
formato al número 1000, el resultado será "1,000".
Especificador de escala numérica: si se especifican una o varias comas inmediatamente a la izquierda del
signo decimal explícito o implícito, el número al que se va a dar formato se divide por 1000 por cada
coma. Por ejemplo, si se utiliza la cadena "0,," para dar formato al número 100 millones, el resultado será
"100".
Puede usar especificadores de separador de grupos y de escala numérica en la misma cadena de formato. Por
ejemplo, si se utiliza la cadena "#,0,," y la referencia cultural de todos los idiomas para dar formato al número
mil millones, el resultado será "1,000".
En el ejemplo siguiente se muestra el uso de la coma como separador de grupos.
Console::WriteLine(value.ToString("#,##0,,", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:#,##0,,}", value));
// Displays 1,235
Console.WriteLine(value.ToString("#,##0,,", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#,##0,,}", value));
// Displays 1,235
Console.WriteLine(value.ToString("#,##0,,", CultureInfo.InvariantCulture))
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#,##0,,}", value))
' Displays 1,235
Console::WriteLine(value.ToString("#,,,", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:#,,,}", value));
// Displays 1
Console::WriteLine(value.ToString("#,##0,,", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:#,##0,,}", value));
// Displays 1,235
Console.WriteLine(value.ToString("#,,,", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#,,,}", value));
// Displays 1
Console.WriteLine(value.ToString("#,##0,,", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#,##0,,}", value));
// Displays 1,235
Console.WriteLine(value.ToString("#,,,", CultureInfo.InvariantCulture))
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#,,,}", value))
' Displays 1
Console.WriteLine(value.ToString("#,##0,,", CultureInfo.InvariantCulture))
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#,##0,,}", value))
' Displays 1,235
Volver a la tabla
Volver a la tabla
Volver a la tabla
Especificadores personalizados "E" y "e"
Si alguna de las cadenas "E", "E+", "E-", "e", "e+", o "e-" está presente en la cadena de formato y va seguida
inmediatamente de al menos un cero, se da formato al número mediante notación científica con una 'E' o una 'e'
insertadas entre el número y el exponente. El número de ceros que hay a continuación del indicador de notación
científica determina el número mínimo de dígitos para el exponente. Los formatos 'E+' y 'e+' indican que un
signo más o un signo menos debe preceder siempre al exponente. Los formatos 'E', 'E-', 'e' o 'e-' indican que un
carácter de signo debe preceder solo a exponentes negativos.
En el ejemplo siguiente se da formato a varios valores numéricos utilizando los especificadores de notación
científica.
Console::WriteLine(value.ToString("0.###E+000", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:0.###E+000}", value));
// Displays 8.6E+004
Console::WriteLine(value.ToString("0.###E-000", CultureInfo::InvariantCulture));
Console::WriteLine(String::Format(CultureInfo::InvariantCulture,
"{0:0.###E-000}", value));
// Displays 8.6E004
Console.WriteLine(value.ToString("0.###E+000", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.###E+000}", value));
// Displays 8.6E+004
Console.WriteLine(value.ToString("0.###E-000", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.###E-000}", value));
// Displays 8.6E004
Console.WriteLine(value.ToString("0.###E+000", CultureInfo.InvariantCulture))
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.###E+000}", value))
' Displays 8.6E+004
Console.WriteLine(value.ToString("0.###E-000", CultureInfo.InvariantCulture))
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.###E-000}", value))
' Displays 8.6E004
Volver a la tabla
NOTE
Algunos compiladores, como los compiladores de C# y C++, también pueden interpretar un único carácter de barra
diagonal inversa como un carácter de escape. Para asegurarse de que una cadena se interpreta correctamente al darle
formato, puede usar el carácter literal de cadena textual (el carácter @) antes de la cadena en C# o puede agregar otro
carácter de barra diagonal inversa delante de cada barra diagonal inversa en C# y C++. En el siguiente ejemplo de C# se
muestran ambos enfoques.
En el ejemplo siguiente se usa el carácter de escape para evitar que la operación de formato interprete los
caracteres "#", "0" y "\" como caracteres de escape o especificadores de formato. En el ejemplo de C# se usa una
barra diagonal inversa adicional para asegurarse de que una barra diagonal inversa se interprete como un
carácter literal.
Volver a la tabla
Los separadores de sección omiten cualquier formato preexistente asociado a un número al dar formato al valor
final. Por ejemplo, los valores negativos se muestran siempre con signo menos cuando se utilizan separadores
de sección. Si se desea que el valor con formato final tenga un signo menos, debe incluir explícitamente el signo
menos como parte del especificador de formato personalizado.
En el ejemplo siguiente se usa el especificador de formato ";" para aplicar un formato diferente a los números
positivos, negativos y cero.
Console::WriteLine(posValue.ToString(fmt2));
Console::WriteLine(String::Format("{0:" + fmt2 + "}", posValue));
// Displays 1234
Console::WriteLine(negValue.ToString(fmt2));
Console::WriteLine(String::Format("{0:" + fmt2 + "}", negValue));
// Displays (1234)
Console::WriteLine(zeroValue.ToString(fmt3));
Console::WriteLine(String::Format("{0:" + fmt3 + "}", zeroValue));
// Displays **Zero**
Console.WriteLine(posValue.ToString(fmt2));
Console.WriteLine(String.Format("{0:" + fmt2 + "}", posValue));
// Displays 1234
Console.WriteLine(negValue.ToString(fmt2));
Console.WriteLine(String.Format("{0:" + fmt2 + "}", negValue));
// Displays (1234)
Console.WriteLine(zeroValue.ToString(fmt3));
Console.WriteLine(String.Format("{0:" + fmt3 + "}", zeroValue));
// Displays **Zero**
Dim posValue As Double = 1234
Dim negValue As Double = -1234
Dim zeroValue As Double = 0
Console.WriteLine(posValue.ToString(fmt2))
Console.WriteLine(String.Format("{0:" + fmt2 + "}", posValue))
' Displays 1234
Console.WriteLine(negValue.ToString(fmt2))
Console.WriteLine(String.Format("{0:" + fmt2 + "}", negValue))
' Displays (1234)
Console.WriteLine(zeroValue.ToString(fmt3))
Console.WriteLine(String.Format("{0:" + fmt3 + "}", zeroValue))
' Displays **Zero**
Volver a la tabla
Literales de carácter
Los especificadores de formato que aparecen en una cadena de formato numérico personalizado siempre se
interpretan como caracteres de formato y no como caracteres literales. Se incluyen los caracteres siguientes:
0
#
%
‰
'
\
.
,
E o e, según su posición en la cadena de formato.
Todos los demás caracteres se interpretan siempre como literales de carácter y, en una operación de formato, se
incluyen en la cadena de resultado sin modificar. En una operación de análisis, deben coincidir exactamente con
los caracteres de la cadena de entrada; la comparación distingue entre mayúsculas y minúsculas.
En el ejemplo siguiente se muestra un uso habitual de las unidades de carácter literal (en este caso, los millares):
double n = 123.8;
Console.WriteLine($"{n:#,##0.0K}");
// The example displays the following output:
// 123.8K
Hay dos formas de indicar que los caracteres se han de interpretar como caracteres literales y no como
caracteres de formato, para que se puedan incluir en una cadena de resultado o analizarse correctamente en una
cadena de entrada:
Mediante el escape de un carácter de formato. Para obtener más información, vea Carácter de escape "\".
Mediante la inclusión de toda la cadena literal entre comillas o apóstrofes.
En el ejemplo siguiente se usan los dos enfoques para incluir caracteres reservados en una cadena de formato
numérico personalizada.
double n = 9.3;
Console.WriteLine($@"{n:##.0\%}");
Console.WriteLine($@"{n:\'##\'}");
Console.WriteLine($@"{n:\\##\\}");
Console.WriteLine();
Console.WriteLine($"{n:##.0'%'}");
Console.WriteLine($@"{n:'\'##'\'}");
// The example displays the following output:
// 9.3%
// '9'
// \9\
//
// 9.3%
// \9\
Notas
Infinitos de punto flotante y NaN
Independientemente de la cadena de formato, si el valor de un tipo de punto flotante Single o Double es infinito
positivo, infinito negativo o NaN (Not a Number, no es un número), la cadena con formato será el valor de la
propiedad PositiveInfinitySymbol, NegativeInfinitySymbolo NaNSymbol respectiva especificada por el objeto
NumberFormatInfo aplicable actualmente.
Configuración del Panel de control
Los valores de configuración del elemento Configuración regional y de idioma del Panel de control influyen
en la cadena de resultado generada por una operación de formato. Estos valores de configuración se utilizan
para inicializar el objeto NumberFormatInfo asociado a la referencia cultural del subproceso actual, y la
referencia cultural del subproceso actual proporciona valores que se utilizan para controlar el formato. Los
equipos que usan configuraciones diferentes generarán cadenas de resultado distintas.
Asimismo, si se usa el constructor CultureInfo(String) para crear instancias de un nuevo objeto CultureInfo que
representa la misma referencia cultural que la referencia cultural del sistema actual, cualquier personalización
establecida por el elemento Configuración regional y de idioma del Panel de control se aplicará al nuevo
objeto CultureInfo . Puede usar el constructor CultureInfo(String, Boolean) para crear un objeto CultureInfo que
no refleje las personalizaciones de un sistema.
Cadenas de formato de punto fijo y redondeo
Para las cadenas de formato de punto fijo (es decir, las cadenas de formato que no contienen caracteres de
formato de notación científica), los números se redondean hasta tantos decimales como marcadores de posición
de dígitos haya a la derecha del separador decimal. Si la cadena de formato no contiene ningún separador
decimal, el número se redondea al entero más próximo. Si el número tiene más dígitos que marcadores de
posición de dígitos a la izquierda del separador decimal, los dígitos adicionales se copian en la cadena de
resultado justo antes del primer marcador de posición de dígitos.
Volver a la tabla
Ejemplo
En el siguiente ejemplo se muestran dos cadenas de formato numérico personalizado. En ambos casos, el
marcador de posición de dígitos ( # ) muestra los datos numéricos, y todos los demás caracteres se copian en la
cadena de resultado.
Volver a la tabla
Vea también
System.Globalization.NumberFormatInfo
Aplicación de formato a tipos
Cadenas con formato numérico estándar
Cómo: Rellenar un número con ceros a la izquierda
Ejemplo: Utilidad de formato WinForms de .NET Core (C#)
Ejemplo: Utilidad de formato WinForms de .NET Core (Visual Basic)
Cadenas con formato de fecha y hora estándar
16/09/2020 • 54 minutes to read • Edit Online
Una cadena de formato de fecha y hora estándar usa un único especificador de formato para definir la
representación de texto de un valor de fecha y hora. Cualquier cadena con formato de fecha y hora que contenga
más de un carácter, incluido un espacio en blanco, se interpreta como una cadena con formato de fecha y hora
personalizado; para obtener más información, consulte Cadenas con formato de fecha y hora personalizado. Una
cadena de formato estándar o personalizado se puede usar de dos maneras:
Para definir la cadena resultante una operación de formato.
Para definir la representación de texto de un valor de fecha y hora que se puede convertir en un valor
DateTime o DateTimeOffset mediante una operación de análisis.
TIP
Puede descargar la Utilidad de formato , que es una aplicación de .NET Core Windows Forms que permite aplicar
cadenas de formato a valores numéricos o de fecha y hora, y que muestra la cadena de resultado. El código fuente está
disponible para C# y Visual Basic.
Las cadenas con formato de fecha y hora estándar se pueden utilizar tanto con valores DateTime como con
valores DateTimeOffset.
NOTE
Algunos de los ejemplos de C# de este artículo se ejecutan en el ejecutor de código en línea y área de juegos de Try.NET.
Haga clic en el botón Ejecutar para ejecutar un ejemplo en una ventana interactiva. Una vez que se ejecuta el código,
puede modificar y ejecutar el código modificado si vuelve a hacer clic en Ejecutar . El código modificado se ejecuta en la
ventana interactiva o, si se produce un error en la compilación, en la ventana interactiva se muestran todos los mensajes
de error del compilador de C#.
La zona horaria local del ejecutor de código en línea de Try.NET y del área de juegos es la hora universal coordinada o UTC.
Esto puede afectar al comportamiento y la salida de ejemplos que ilustran los tipos DateTime, DateTimeOffset y
TimeZoneInfo y sus miembros.
En la tabla siguiente se describen los especificadores de formato de fecha y hora estándar. A menos que se
indique lo contrario, un determinado especificador de formato de fecha y hora estándar genera una
representación de cadena idéntica independientemente de que se use con un valor DateTime o DateTimeOffset.
Consulte la sección Notas para obtener información adicional sobre cómo usar cadenas de formato de fecha y
hora estándar.
2009-06-15T13:45:30
(DateTimeKind.Utc) --> 2009-06-
15T13:45:30.0000000Z
2009-06-15T13:45:30
(DateTimeKind.Unspecified) --> 2009-
06-15T13:45:30.0000000
Valores de DateTimeOffset:
2009-06-15T13:45:30-07:00 -->
2009-06-15T13:45:30.0000000-07:00
"u" Patrón de fecha y hora universal que se Con un valor DateTime de: 2009-06-
puede ordenar. 15T13:45:30 -> 2009-06-15
13:45:30Z
Más información: El especificador de
formato universal que se puede Con un valor DateTimeOffset de:
ordenar ("u"). 2009-06-15T13:45:30 -> 2009-06-15
20:45:30Z
ESP EC IF IC A DO R DE F O RM ATO DESC RIP C IÓ N E JEM P LO S
Puede pasar un objeto CultureInfo que represente la referencia cultural cuyo formato se va a usar a un
método que tenga un parámetro IFormatProvider. En el ejemplo siguiente se muestra una fecha con el
formato de fecha abreviado de la referencia cultural pt-BR.
// Display using pt-BR culture's short date format
DateTime thisDate = new DateTime(2008, 3, 15);
CultureInfo culture = new CultureInfo("pt-BR");
Console.WriteLine(thisDate.ToString("d", culture)); // Displays 15/3/2008
Puede pasar un objeto DateTimeFormatInfo que proporcione información sobre el formato a un método
que tenga un parámetro IFormatProvider. En el ejemplo siguiente se muestra una fecha con el formato de
fecha abreviado de un objeto DateTimeFormatInfo en la referencia cultural hr-HR.
NOTE
Para obtener información sobre la personalización de patrones o cadenas usadas para dar formato a valores de fecha y
hora, vea el tema sobre la clase NumberFormatInfo.
En algunos casos, la cadena con formato estándar actúa como la abreviatura correspondiente de una cadena con
formato personalizado más larga que es invariable. Hay cuatro cadenas de formato estándar que pertenecen a
esta categoría: "O" (u "o"), "R" (o "r"), "s" y "u". Estas cadenas se corresponden con las cadenas de formato
personalizado definidas en la referencia cultural de todos los idiomas. Generan representaciones de cadena de
valores de fecha y hora que están pensados para que sean idénticos en todas las referencias culturales. En la
tabla siguiente se proporciona información sobre estas cuatro cadenas de formato de fecha y hora estándar.
SE DEF IN E EN L A P RO P IEDA D
DAT ET IM EF O RM AT IN F O. IN VA RIA N T IN F C A DEN A C O N F O RM ATO
C A DEN A C O N F O RM ATO ESTÁ N DA R O P ERSO N A L IZ A DO
Las cadenas de formato estándar también se pueden usar en operaciones de análisis con los métodos
DateTime.ParseExact o DateTimeOffset.ParseExact, que necesitan que una cadena de entrada se ajuste
exactamente a un patrón determinado para que la operación de análisis se realice correctamente. Muchas
cadenas de formato estándar se asignan a varias cadenas de formato personalizado, por lo que un valor de fecha
y hora se pueden representar en diversos formatos y la operación de análisis todavía se realizará correctamente.
Puede determinar la cadena o las cadenas con formato personalizado correspondientes a una cadena con
formato estándar llamando al método DateTimeFormatInfo.GetAllDateTimePatterns(Char). En el ejemplo
siguiente se muestran las cadenas con formato personalizado que se asignan a la cadena de formato estándar
"d" (patrón de fecha corta).
using System;
using System.Globalization;
Imports System.Globalization
Module Example
Public Sub Main()
Console.WriteLine("'d' standard format string:")
For Each customString In DateTimeFormatInfo.CurrentInfo.GetAllDateTimePatterns("d"c)
Console.WriteLine(" {0}", customString)
Next
End Sub
End Module
' The example displays the following output:
' 'd' standard format string:
' M/d/yyyy
' M/d/yy
' MM/dd/yy
' MM/dd/yyyy
' yy/MM/dd
' yyyy-MM-dd
' dd-MMM-yy
En las próximas secciones se describen los especificadores de formato estándar para los valores DateTime y
DateTimeOffset.
En el ejemplo siguiente se usa el especificador de formato "d" para mostrar un valor de fecha y hora.
Volver a la tabla
En el ejemplo siguiente se usa el especificador de formato "D" para mostrar un valor de fecha y hora.
DateTime date1 = new DateTime(2008, 4, 10);
Console.WriteLine(date1.ToString("D",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays Thursday, April 10, 2008
Console.WriteLine(date1.ToString("D",
CultureInfo.CreateSpecificCulture("pt-BR")));
// Displays quinta-feira, 10 de abril de 2008
Console.WriteLine(date1.ToString("D",
CultureInfo.CreateSpecificCulture("es-MX")));
// Displays jueves, 10 de abril de 2008
Volver a la tabla
En el ejemplo siguiente se usa el especificador de formato "f" para mostrar un valor de fecha y hora.
Volver a la tabla
Volver a la tabla
En el ejemplo siguiente se usa el especificador de formato "g" para mostrar un valor de fecha y hora.
DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);
Console.WriteLine(date1.ToString("g",
DateTimeFormatInfo.InvariantInfo));
// Displays 04/10/2008 06:30
Console.WriteLine(date1.ToString("g",
CultureInfo.CreateSpecificCulture("en-us")));
// Displays 4/10/2008 6:30 AM
Console.WriteLine(date1.ToString("g",
CultureInfo.CreateSpecificCulture("fr-BE")));
// Displays 10/04/2008 6:30
Volver a la tabla
Volver a la tabla
En el ejemplo siguiente se usa el especificador de formato "m" para mostrar un valor de fecha y hora.
Volver a la tabla
Module Example
Public Sub Main()
Dim dat As New Date(2009, 6, 15, 13, 45, 30,
DateTimeKind.Unspecified)
Console.WriteLine("{0} ({1}) --> {0:O}", dat, dat.Kind)
En el ejemplo siguiente se usa el especificador de formato "o" para crear una cadena con formato y, a
continuación, se restaura el valor de fecha y hora original llamando a un método Parse de fecha y hora.
// Round-trip DateTime values.
DateTime originalDate, newDate;
string dateString;
// Round-trip a local time.
originalDate = DateTime.SpecifyKind(new DateTime(2008, 4, 10, 6, 30, 0), DateTimeKind.Local);
dateString = originalDate.ToString("o");
newDate = DateTime.Parse(dateString, null, DateTimeStyles.RoundtripKind);
Console.WriteLine("Round-tripped {0} {1} to {2} {3}.", originalDate, originalDate.Kind,
newDate, newDate.Kind);
// Round-trip a UTC time.
originalDate = DateTime.SpecifyKind(new DateTime(2008, 4, 12, 9, 30, 0), DateTimeKind.Utc);
dateString = originalDate.ToString("o");
newDate = DateTime.Parse(dateString, null, DateTimeStyles.RoundtripKind);
Console.WriteLine("Round-tripped {0} {1} to {2} {3}.", originalDate, originalDate.Kind,
newDate, newDate.Kind);
// Round-trip time in an unspecified time zone.
originalDate = DateTime.SpecifyKind(new DateTime(2008, 4, 13, 12, 30, 0), DateTimeKind.Unspecified);
dateString = originalDate.ToString("o");
newDate = DateTime.Parse(dateString, null, DateTimeStyles.RoundtripKind);
Console.WriteLine("Round-tripped {0} {1} to {2} {3}.", originalDate, originalDate.Kind,
newDate, newDate.Kind);
Aunque la norma RFC 1123 expresa una hora según la hora universal coordinada (hora UTC), la operación de
formato no modifica el valor del objeto DateTime al que se está dando formato. Por consiguiente, debe convertir
el valor DateTime en una hora UTC llamando al método DateTime.ToUniversalTime antes de realizar la operación
de formato. En cambio, los valores DateTimeOffset realizan esta conversión automáticamente; no es necesario
llamar al método DateTimeOffset.ToUniversalTime antes de la operación de formato.
En el ejemplo siguiente se utiliza el especificador de formato "r" para mostrar un DateTime y un
valor DateTimeOffset en un sistema de la zona horaria del Pacífico de EE. UU.
Volver a la tabla
Volver a la tabla
En el ejemplo siguiente se usa el especificador de formato "t" para mostrar un valor de fecha y hora.
DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);
Console.WriteLine(date1.ToString("t",
CultureInfo.CreateSpecificCulture("en-us")));
// Displays 6:30 AM
Console.WriteLine(date1.ToString("t",
CultureInfo.CreateSpecificCulture("es-ES")));
// Displays 6:30
Volver a la tabla
En el ejemplo siguiente se usa el especificador de formato "T" para mostrar un valor de fecha y hora.
Volver a la tabla
Volver a la tabla
El especificador de formato "U" no es compatible con el tipo DateTimeOffset y provoca una excepción
FormatException si se usa para dar formato a un valor DateTimeOffset.
En el ejemplo siguiente se usa el especificador de formato "U" para mostrar un valor de fecha y hora.
Volver a la tabla
En el ejemplo siguiente se usa el especificador de formato "y" para mostrar un valor de fecha y hora.
DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);
Console.WriteLine(date1.ToString("Y",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays April, 2008
Console.WriteLine(date1.ToString("y",
CultureInfo.CreateSpecificCulture("af-ZA")));
// Displays April 2008
Volver a la tabla
Notas
Configuración del Panel de control
Los valores de configuración del elemento Configuración regional y de idioma del Panel de control influyen
en la cadena de resultado generada por una operación de formato. Estas configuraciones se utilizan para
inicializar el objeto DateTimeFormatInfo asociado a la referencia cultural del subproceso actual, que proporciona
valores que se utilizan para controlar el formato. Los equipos que usan configuraciones diferentes generarán
cadenas de resultado distintas.
Asimismo, si se usa el constructor CultureInfo(String) para crear instancias de un nuevo objeto CultureInfo que
representa la misma referencia cultural que la referencia cultural del sistema actual, cualquier personalización
establecida por el elemento Configuración regional y de idioma del Panel de control se aplicará al nuevo
objeto CultureInfo . Puede usar el constructor CultureInfo(String, Boolean) para crear un objeto CultureInfo que
no refleje las personalizaciones de un sistema.
Propiedades de DateTimeFormatInfo
El formato se ve influenciado por las propiedades del objeto DateTimeFormatInfo actual, proporcionado
implícitamente por la referencia cultural del subproceso actual o explícitamente por el parámetro
IFormatProvider del método que invoca el formato. En el parámetro IFormatProvider, la aplicación debe
especificar un objeto CultureInfo, que representa una referencia cultural, o un objeto DateTimeFormatInfo, que
representa las convenciones de formato de fecha y hora de una determinada referencia cultural. Muchos de los
especificadores de formato de fecha y hora estándar son alias de patrones de formato definidos en las
propiedades del objeto DateTimeFormatInfo actual. La aplicación puede modificar el resultado generado por
algunos especificadores de formato de fecha y hora estándar al cambiar los patrones de formato de fecha y hora
correspondientes de la propiedad DateTimeFormatInfo apropiada.
Vea también
System.DateTime
System.DateTimeOffset
Aplicación de formato a tipos
Cadenas con formato de fecha y hora personalizado
Ejemplo: Utilidad de formato WinForms de .NET Core (C#)
Ejemplo: Utilidad de formato WinForms de .NET Core (Visual Basic)
Cadenas con formato de fecha y hora
personalizado
16/09/2020 • 96 minutes to read • Edit Online
Una cadena con formato de fecha y hora define la representación de texto de un valor DateTime o
DateTimeOffset que es el resultado de una operación de formato. También puede definir la representación de un
valor de fecha y hora que se necesite en una operación de análisis para convertir correctamente la cadena en
una fecha y hora. Una cadena de formato personalizado consta de uno o varios especificadores de formato de
fecha y hora personalizado. Una cadena que no sea una cadena con formato de fecha y hora estándar se
interpreta como una cadena con formato de fecha y hora personalizado.
TIP
Puede descargar la Utilidad de formato , que es una aplicación de .NET Core Windows Forms que permite aplicar
cadenas de formato a valores numéricos o de fecha y hora, y que muestra la cadena de resultado. El código fuente está
disponible para C# y Visual Basic.
Las cadenas con formato de fecha y hora personalizado se pueden utilizar tanto con valores DateTime como con
valores DateTimeOffset.
NOTE
Algunos de los ejemplos de C# de este artículo se ejecutan en el ejecutor de código en línea y área de juegos de Try.NET.
Haga clic en el botón Ejecutar para ejecutar un ejemplo en una ventana interactiva. Una vez que se ejecuta el código,
puede modificar y ejecutar el código modificado si vuelve a hacer clic en Ejecutar . El código modificado se ejecuta en la
ventana interactiva o, si se produce un error en la compilación, en la ventana interactiva se muestran todos los mensajes
de error del compilador de C#.
La zona horaria local del ejecutor de código en línea de Try.NET y del área de juegos es la hora universal coordinada o UTC.
Esto puede afectar al comportamiento y la salida de ejemplos que ilustran los tipos DateTime, DateTimeOffset y
TimeZoneInfo y sus miembros.
En operaciones de formato, las cadenas de formato de fecha y hora personalizado se pueden usar con el método
ToString de una instancia de fecha y hora o con un método que admita formato compuesto. En el ejemplo
siguiente se muestran ambos usos.
En las operaciones de análisis, las cadenas de formato de fecha y hora personalizado se pueden usar con los
métodos DateTime.ParseExact, DateTime.TryParseExact, DateTimeOffset.ParseExact y
DateTimeOffset.TryParseExact. Estos métodos necesitan que una cadena de entrada se ajuste exactamente a un
modelo determinado para que la operación de análisis se realice correctamente. En el ejemplo siguiente se
muestra una llamada al método DateTimeOffset.ParseExact(String, String, IFormatProvider) para analizar una
fecha que debe incluir un día, un mes y un año de dos dígitos.
using System;
using System.Globalization;
Module Example
Public Sub Main()
Dim dateValues() As String = {"30-12-2011", "12-30-2011",
"30-12-11", "12-30-11"}
Dim pattern As String = "MM-dd-yy"
Dim parsedDate As Date
En la tabla siguiente se describen los especificadores de formato de fecha y hora personalizados, y se muestra la
cadena de resultado producida por cada especificador de formato. De forma predeterminada, las cadenas de
resultado reflejan las convenciones de formato de la referencia cultural en-us. Si un especificador de formato
determinado genera una cadena de resultado localizada, el ejemplo también indica la referencia cultural a la que
se aplica dicha cadena. Para más información sobre cómo usar cadenas de formato de fecha y hora
personalizado, vea la sección Notas.
2009-06-15T01:45:30-07:00 --> -
07:00
2009-06-15T08:45:30+00:00 -->
+00:00
2009-06-15T13:45:30 -> 9
2019-06-15T13:45:30 -> 19
2019-06-15T13:45:30 -> 19
En las secciones siguientes se proporciona información adicional sobre cada especificador de formato de fecha y
hora personalizado. A menos que se indique lo contrario, cada especificador genera una representación de
cadena idéntica independientemente de que se use con un valor DateTime o DateTimeOffset.
Console.WriteLine(date1.ToString("d, M",
CultureInfo.InvariantCulture));
// Displays 29, 8
Console.WriteLine(date1.ToString("d MMMM",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays 29 August
Console.WriteLine(date1.ToString("d MMMM",
CultureInfo.CreateSpecificCulture("es-MX")));
// Displays 29 agosto
Console.WriteLine(date1.ToString("d, M", _
CultureInfo.InvariantCulture))
' Displays 29, 8
Console.WriteLine(date1.ToString("d MMMM", _
CultureInfo.CreateSpecificCulture("en-US")))
' Displays 29 August
Console.WriteLine(date1.ToString("d MMMM", _
CultureInfo.CreateSpecificCulture("es-MX")))
' Displays 29 agosto
Volver a la tabla
Especificador de formato personalizado "dd"
La cadena de formato personalizado "dd" representa el día del mes como un número de 01 a 31. Un día con un
solo dígito tiene un formato con un cero inicial.
En el ejemplo siguiente se incluye el especificador de formato personalizado "dd" en una cadena de formato
personalizado.
DateTime date1 = new DateTime(2008, 1, 2, 6, 30, 15);
Console.WriteLine(date1.ToString("dd, MM",
CultureInfo.InvariantCulture));
// 02, 01
Console.WriteLine(date1.ToString("dd, MM", _
CultureInfo.InvariantCulture))
' 02, 01
Volver a la tabla
Especificador de formato personalizado "ddd"
El especificador de formato personalizado "ddd" representa el nombre abreviado del día de la semana. El
nombre abreviado adaptado del día de la semana se recupera de la propiedad
DateTimeFormatInfo.AbbreviatedDayNames de la referencia cultural actual o especificada.
En el ejemplo siguiente se incluye el especificador de formato personalizado "ddd" en una cadena de formato
personalizado.
Console.WriteLine(date1.ToString("ddd d MMM",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays Fri 29 Aug
Console.WriteLine(date1.ToString("ddd d MMM",
CultureInfo.CreateSpecificCulture("fr-FR")));
// Displays ven. 29 août
Console.WriteLine(date1.ToString("ddd d MMM", _
CultureInfo.CreateSpecificCulture("en-US")))
' Displays Fri 29 Aug
Console.WriteLine(date1.ToString("ddd d MMM", _
CultureInfo.CreateSpecificCulture("fr-FR")))
' Displays ven. 29 août
Volver a la tabla
Especificador de formato personalizado "dddd"
El especificador de formato personalizado "dddd" (más cualquier número de especificadores "d" adicionales)
representa el nombre completo del día de la semana. El nombre adaptado del día de la semana se recupera de la
propiedad DateTimeFormatInfo.DayNames de la referencia cultural actual o especificada.
En el ejemplo siguiente se incluye el especificador de formato personalizado "dddd" en una cadena de formato
personalizado.
DateTime date1 = new DateTime(2008, 8, 29, 19, 27, 15);
Console.WriteLine(date1.ToString("dddd dd MMMM",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays Friday 29 August
Console.WriteLine(date1.ToString("dddd dd MMMM",
CultureInfo.CreateSpecificCulture("it-IT")));
// Displays venerdì 29 agosto
Console.WriteLine(date1.ToString("dddd dd MMMM", _
CultureInfo.CreateSpecificCulture("en-US")))
' Displays Friday 29 August
Console.WriteLine(date1.ToString("dddd dd MMMM", _
CultureInfo.CreateSpecificCulture("it-IT")))
' Displays venerdì 29 agosto
Volver a la tabla
Console.WriteLine(date1.ToString("hh:mm:ss.f", ci));
// Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci));
// Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci));
// Displays 07:27:15.018
Dim date1 As New Date(2008, 8, 29, 19, 27, 15, 018)
Dim ci As CultureInfo = CultureInfo.InvariantCulture
Console.WriteLine(date1.ToString("hh:mm:ss.f", ci))
' Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci))
' Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci))
' Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci))
' Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci))
' Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci))
' Displays 07:27:15.018
Volver a la tabla
Especificador de formato personalizado "ff"
El especificador de formato personalizado "ff" representa los dos dígitos más significativos de la fracción de
segundos; es decir, representa las centésimas de segundo de un valor de fecha y hora.
En el ejemplo siguiente se incluye el especificador de formato personalizado "ff" en una cadena de formato
personalizado.
Console.WriteLine(date1.ToString("hh:mm:ss.f", ci));
// Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci));
// Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.f", ci))
' Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci))
' Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci))
' Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci))
' Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci))
' Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci))
' Displays 07:27:15.018
Volver a la tabla
Especificador de formato personalizado "fff"
El especificador de formato personalizado "fff" representa los tres dígitos más significativos de la fracción de
segundos; es decir, representa los milisegundos de un valor de fecha y hora.
En el ejemplo siguiente se incluye el especificador de formato personalizado "fff" en una cadena de formato
personalizado.
Console.WriteLine(date1.ToString("hh:mm:ss.f", ci));
// Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci));
// Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.f", ci))
' Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci))
' Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci))
' Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci))
' Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci))
' Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci))
' Displays 07:27:15.018
Volver a la tabla
Especificador de formato personalizado "ffff"
El especificador de formato personalizado "ffff" representa los cuatro dígitos más significativos de la fracción de
segundos; es decir, representa las diezmilésimas de segundo de un valor de fecha y hora.
Si bien se puede mostrar el componente correspondiente a las diezmilésimas de segundo de un valor de hora,
es muy posible que ese valor no sea significativo. La precisión de los valores de fecha y hora depende de la
resolución del reloj del sistema. En los sistemas operativos Windows NT 3.5 (y versiones posteriores) y Windows
Vista, la resolución del reloj es aproximadamente de 10 a 15 milisegundos.
Volver a la tabla
Especificador de formato personalizado "fffff"
El especificador de formato personalizado "fffff" representa los cinco dígitos más significativos de la fracción de
segundo; es decir, representa las cienmilésimas de segundo de un valor de fecha y hora.
Si bien se puede mostrar el componente correspondiente a las cienmilésimas de segundo de un valor de hora,
es muy posible que ese valor no sea significativo. La precisión de los valores de fecha y hora depende de la
resolución del reloj del sistema. En los sistemas operativos Windows NT 3.5 (y versiones posteriores) y Windows
Vista, la resolución del reloj es aproximadamente de 10 a 15 milisegundos.
Volver a la tabla
Especificador de formato personalizado "ffffff"
El especificador de formato personalizado "ffffff" representa los seis dígitos más significativos de la fracción de
segundos; es decir, representa las millonésimas de segundo de un valor de fecha y hora.
Si bien se puede mostrar el componente correspondiente a las millonésimas de segundo de un valor de hora, es
muy posible que ese valor no sea significativo. La precisión de los valores de fecha y hora depende de la
resolución del reloj del sistema. En los sistemas operativos Windows NT 3.5 (y versiones posteriores) y Windows
Vista, la resolución del reloj es aproximadamente de 10 a 15 milisegundos.
Volver a la tabla
Especificador de formato personalizado "fffffff"
El especificador de formato personalizado "fffffff" representa los siete dígitos más significativos de la fracción de
segundos; es decir, representa las diezmillonésimas de segundo de un valor de fecha y hora.
Si bien se puede mostrar el componente correspondiente a las diezmillonésimas de segundo de un valor de
hora, es muy posible que ese valor no sea significativo. La precisión de los valores de fecha y hora depende de la
resolución del reloj del sistema. En los sistemas operativos Windows NT 3.5 (y versiones posteriores) y Windows
Vista, la resolución del reloj es aproximadamente de 10 a 15 milisegundos.
Volver a la tabla
Console.WriteLine(date1.ToString("hh:mm:ss.f", ci));
// Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci));
// Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci));
// Displays 07:27:15.018
Dim date1 As New Date(2008, 8, 29, 19, 27, 15, 018)
Dim ci As CultureInfo = CultureInfo.InvariantCulture
Console.WriteLine(date1.ToString("hh:mm:ss.f", ci))
' Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci))
' Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci))
' Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci))
' Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci))
' Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci))
' Displays 07:27:15.018
Volver a la tabla
Especificador de formato personalizado "FF"
El especificador de formato personalizado "FF" representa los dos dígitos más significativos de la fracción de
segundos; es decir, representa las centésimas de segundo de un valor de fecha y hora. Sin embargo, no se
muestran los ceros finales ni los dígitos de dos ceros.
En el ejemplo siguiente se incluye el especificador de formato personalizado "FF" en una cadena de formato
personalizado.
Console.WriteLine(date1.ToString("hh:mm:ss.f", ci));
// Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci));
// Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.f", ci))
' Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci))
' Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci))
' Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci))
' Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci))
' Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci))
' Displays 07:27:15.018
Volver a la tabla
Especificador de formato personalizado "FFF"
El especificador de formato personalizado "FFF" representa los tres dígitos más significativos de la fracción de
segundos; es decir, representa los milisegundos de un valor de fecha y hora. Sin embargo, no se muestran los
ceros finales ni los dígitos de tres ceros.
En el ejemplo siguiente se incluye el especificador de formato personalizado "FFF" en una cadena de formato
personalizado.
Console.WriteLine(date1.ToString("hh:mm:ss.f", ci));
// Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci));
// Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.f", ci))
' Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci))
' Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci))
' Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci))
' Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci))
' Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci))
' Displays 07:27:15.018
Volver a la tabla
Especificador de formato personalizado "FFFF"
El especificador de formato personalizado "FFFF" representa los cuatro dígitos más significativos de la fracción
de segundos; es decir, representa las diezmilésimas de segundo de un valor de fecha y hora. Sin embargo, no se
muestran los ceros finales ni los dígitos de cuatro ceros.
Si bien se puede mostrar el componente correspondiente a las diezmilésimas de segundo de un valor de hora,
es muy posible que ese valor no sea significativo. La precisión de los valores de fecha y hora depende de la
resolución del reloj del sistema. En los sistemas operativos Windows NT 3.5 (y versiones posteriores) y Windows
Vista, la resolución del reloj es aproximadamente de 10 a 15 milisegundos.
Volver a la tabla
Especificador de formato personalizado "FFFFF"
El especificador de formato personalizado "FFFFF" representa los cinco dígitos más significativos de la fracción
de segundos; es decir, representa las cienmilésimas de segundo de un valor de fecha y hora. Sin embargo, no se
muestran los ceros finales ni los dígitos de cinco ceros.
Si bien se puede mostrar el componente correspondiente a las cienmilésimas de segundo de un valor de hora,
es muy posible que ese valor no sea significativo. La precisión de los valores de fecha y hora depende de la
resolución del reloj del sistema. En los sistemas operativos Windows NT 3.5 (y versiones posteriores) y Windows
Vista, la resolución del reloj es aproximadamente de 10 a 15 milisegundos.
Volver a la tabla
Especificador de formato personalizado "FFFFFF"
El especificador de formato personalizado "FFFFFF" representa los seis dígitos más significativos de la fracción
de segundos; es decir, representa las millonésimas de segundo de un valor de fecha y hora. Sin embargo, no se
muestran los ceros finales ni los dígitos de seis ceros.
Si bien se puede mostrar el componente correspondiente a las millonésimas de segundo de un valor de hora, es
muy posible que ese valor no sea significativo. La precisión de los valores de fecha y hora depende de la
resolución del reloj del sistema. En los sistemas operativos Windows NT 3.5 (y versiones posteriores) y Windows
Vista, la resolución del reloj es aproximadamente de 10 a 15 milisegundos.
Volver a la tabla
Especificador de formato personalizado "FFFFFFF"
El especificador de formato personalizado "FFFFFFF" representa los siete dígitos más significativos de la fracción
de segundos; es decir, representa las diezmillonésimas de segundo de un valor de fecha y hora. Sin embargo, no
se muestran los ceros finales ni los dígitos de siete ceros.
Si bien se puede mostrar el componente correspondiente a las diezmillonésimas de segundo de un valor de
hora, es muy posible que ese valor no sea significativo. La precisión de los valores de fecha y hora depende de la
resolución del reloj del sistema. En los sistemas operativos Windows NT 3.5 (y versiones posteriores) y Windows
Vista, la resolución del reloj es aproximadamente de 10 a 15 milisegundos.
Volver a la tabla
Console.WriteLine(date1.ToString("MM/dd/yyyy g",
CultureInfo.InvariantCulture));
// Displays 08/04/0070 A.D.
Console.WriteLine(date1.ToString("MM/dd/yyyy g",
CultureInfo.CreateSpecificCulture("fr-FR")));
// Displays 08/04/0070 ap. J.-C.
Dim date1 As Date = #08/04/0070#
Console.WriteLine(date1.ToString("MM/dd/yyyy g", _
CultureInfo.InvariantCulture))
' Displays 08/04/0070 A.D.
Console.WriteLine(date1.ToString("MM/dd/yyyy g", _
CultureInfo.CreateSpecificCulture("fr-FR")))
' Displays 08/04/0070 ap. J.-C.
Volver a la tabla
DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.InvariantCulture));
// Displays 6:9:1 P
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.CreateSpecificCulture("el-GR")));
// Displays 6:9:1 µ
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.InvariantCulture));
// Displays 6:9:1.5 P
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.CreateSpecificCulture("el-GR")));
// Displays 6:9:1.5 µ
Dim date1 As Date
date1 = #6:09:01PM#
Console.WriteLine(date1.ToString("h:m:s.F t", _
CultureInfo.InvariantCulture))
' Displays 6:9:1 P
Console.WriteLine(date1.ToString("h:m:s.F t", _
CultureInfo.CreateSpecificCulture("el-GR")))
' Displays 6:9:1 µ
date1 = New Date(2008, 1, 1, 18, 9, 1, 500)
Console.WriteLine(date1.ToString("h:m:s.F t", _
CultureInfo.InvariantCulture))
' Displays 6:9:1.5 P
Console.WriteLine(date1.ToString("h:m:s.F t", _
CultureInfo.CreateSpecificCulture("el-GR")))
' Displays 6:9:1.5 µ
Volver a la tabla
Especificador de formato personalizado "hh"
El especificador de formato personalizado "hh" (más cualquier número de especificadores "h" adicionales)
representa la hora como un número del 01 al 12; es decir, la hora se representa como en un reloj de 12 horas
que cuenta las horas enteras desde medianoche o mediodía. Una hora determinada después de la medianoche
no se distingue de la misma hora después del mediodía. No se redondea la hora y las horas con un solo dígito
tienen un cero inicial. Por ejemplo, dada una hora de 5:43 de la mañana o de la tarde, este especificador de
formato muestra "05".
En el ejemplo siguiente se incluye el especificador de formato personalizado "hh" en una cadena de formato
personalizado.
DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01 PM
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01 du.
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01.50 PM
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01.50 du.
Volver a la tabla
Especificador de formato personalizado "HH"
El especificador de formato personalizado "HH" (más cualquier número de especificadores "H" adicionales)
representa la hora como un número del 00 al 23; es decir, la hora se representa como en un reloj de 24 horas de
base cero que cuenta las horas desde medianoche. Una hora con un solo dígito tiene un formato con un cero
inicial.
En el ejemplo siguiente se incluye el especificador de formato personalizado "HH" en una cadena de formato
personalizado.
Volver a la tabla
Console.WriteLine(DateTime.Now.ToString("%K"));
// Displays -07:00
Console.WriteLine(DateTime.UtcNow.ToString("%K"));
// Displays Z
Console.WriteLine("'{0}'",
DateTime.SpecifyKind(DateTime.Now,
DateTimeKind.Unspecified).ToString("%K"));
// Displays ''
Console.WriteLine(DateTimeOffset.Now.ToString("%K"));
// Displays -07:00
Console.WriteLine(DateTimeOffset.UtcNow.ToString("%K"));
// Displays +00:00
Console.WriteLine(new DateTimeOffset(2008, 5, 1, 6, 30, 0,
new TimeSpan(5, 0, 0)).ToString("%K"));
// Displays +05:00
Console.WriteLine(Date.Now.ToString("%K"))
' Displays -07:00
Console.WriteLine(Date.UtcNow.ToString("%K"))
' Displays Z
Console.WriteLine("'{0}'", _
Date.SpecifyKind(Date.Now, _
DateTimeKind.Unspecified). _
ToString("%K"))
' Displays ''
Console.WriteLine(DateTimeOffset.Now.ToString("%K"))
' Displays -07:00
Console.WriteLine(DateTimeOffset.UtcNow.ToString("%K"))
' Displays +00:00
Console.WriteLine(New DateTimeOffset(2008, 5, 1, 6, 30, 0, _
New TimeSpan(5, 0, 0)). _
ToString("%K"))
' Displays +05:00
Volver a la tabla
Especificador de formato de minuto "m"
Especificador de formato personalizado "m"
El especificador de formato personalizado "m" representa el minuto como un número de 0 a 59. El minuto
representa los minutos enteros que han transcurrido desde la última hora. Un minuto con un solo dígito tiene
un formato sin un cero inicial.
Si el especificador de formato "m" se usa sin otros especificadores de formato personalizado, se interpretará
como el especificador de formato de fecha y hora estándar "m". Para más información sobre cómo usar un
especificador de formato único, vea Usar especificadores de formato personalizado únicos más adelante en este
artículo.
En el ejemplo siguiente se incluye el especificador de formato personalizado "m" en una cadena de formato
personalizado.
DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.InvariantCulture));
// Displays 6:9:1 P
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.CreateSpecificCulture("el-GR")));
// Displays 6:9:1 µ
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.InvariantCulture));
// Displays 6:9:1.5 P
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.CreateSpecificCulture("el-GR")));
// Displays 6:9:1.5 µ
Volver a la tabla
Especificador de formato personalizado "mm"
El especificador de formato personalizado "mm" (más cualquier número de especificadores "m" adicionales)
representa el minuto como un número de 00 a 59. El minuto representa los minutos enteros que han
transcurrido desde la última hora. Un minuto con un solo dígito tiene un formato con un cero inicial.
En el ejemplo siguiente se incluye el especificador de formato personalizado "mm" en una cadena de formato
personalizado.
DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01 PM
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01 du.
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01.50 PM
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01.50 du.
Volver a la tabla
Volver a la tabla
Especificador de formato personalizado "MM"
El especificador de formato personalizado "MM" representa el mes como un número del 01 al 12 (o del 1 al 13
para los calendarios con 13 meses). Un mes con un solo dígito tiene un formato con un cero inicial.
En el ejemplo siguiente se incluye el especificador de formato personalizado "MM" en una cadena de formato
personalizado.
Console.WriteLine(date1.ToString("dd, MM",
CultureInfo.InvariantCulture));
// 02, 01
Console.WriteLine(date1.ToString("dd, MM", _
CultureInfo.InvariantCulture))
' 02, 01
Volver a la tabla
Especificador de formato personalizado "MMM"
El especificador de formato personalizado "MMM" representa el nombre abreviado del mes. El nombre
abreviado adaptado del mes se recupera de la propiedad DateTimeFormatInfo.AbbreviatedMonthNames de la
referencia cultural actual o especificada.
En el ejemplo siguiente se incluye el especificador de formato personalizado "MMM" en una cadena de formato
personalizado.
Console.WriteLine(date1.ToString("ddd d MMM",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays Fri 29 Aug
Console.WriteLine(date1.ToString("ddd d MMM",
CultureInfo.CreateSpecificCulture("fr-FR")));
// Displays ven. 29 août
Dim date1 As Date = #08/29/2008 7:27:15PM#
Console.WriteLine(date1.ToString("ddd d MMM", _
CultureInfo.CreateSpecificCulture("en-US")))
' Displays Fri 29 Aug
Console.WriteLine(date1.ToString("ddd d MMM", _
CultureInfo.CreateSpecificCulture("fr-FR")))
' Displays ven. 29 août
Volver a la tabla
Especificador de formato personalizado "MMMM"
El especificador de formato personalizado "MMMM" representa el nombre completo del mes. El nombre
adaptado del mes se recupera de la propiedad DateTimeFormatInfo.MonthNames de la referencia cultural actual
o especificada.
En el ejemplo siguiente se incluye el especificador de formato personalizado "MMMM" en una cadena de
formato personalizado.
Console.WriteLine(date1.ToString("dddd dd MMMM",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays Friday 29 August
Console.WriteLine(date1.ToString("dddd dd MMMM",
CultureInfo.CreateSpecificCulture("it-IT")));
// Displays venerdì 29 agosto
Console.WriteLine(date1.ToString("dddd dd MMMM", _
CultureInfo.CreateSpecificCulture("en-US")))
' Displays Friday 29 August
Console.WriteLine(date1.ToString("dddd dd MMMM", _
CultureInfo.CreateSpecificCulture("it-IT")))
' Displays venerdì 29 agosto
Volver a la tabla
Volver a la tabla
Especificador de formato personalizado "ss"
El especificador de formato personalizado "ss" (más cualquier número de especificadores "s" adicionales)
representa los segundos como un número de 00 a 59. El resultado representa los segundos enteros que han
transcurrido desde el último minuto. Un segundo con un solo dígito tiene un formato con un cero inicial.
En el ejemplo siguiente se incluye el especificador de formato personalizado "ss" en una cadena de formato
personalizado.
DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01 PM
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01 du.
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01.50 PM
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01.50 du.
Dim date1 As Date
date1 = #6:09:01PM#
Console.WriteLine(date1.ToString("hh:mm:ss tt", _
CultureInfo.InvariantCulture))
' Displays 06:09:01 PM
Console.WriteLine(date1.ToString("hh:mm:ss tt", _
CultureInfo.CreateSpecificCulture("hu-HU")))
' Displays 06:09:01 du.
date1 = New Date(2008, 1, 1, 18, 9, 1, 500)
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt", _
CultureInfo.InvariantCulture))
' Displays 06:09:01.50 PM
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt", _
CultureInfo.CreateSpecificCulture("hu-HU")))
' Displays 06:09:01.50 du.
Volver a la tabla
DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.InvariantCulture));
// Displays 6:9:1 P
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.CreateSpecificCulture("el-GR")));
// Displays 6:9:1 µ
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.InvariantCulture));
// Displays 6:9:1.5 P
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.CreateSpecificCulture("el-GR")));
// Displays 6:9:1.5 µ
Dim date1 As Date
date1 = #6:09:01PM#
Console.WriteLine(date1.ToString("h:m:s.F t", _
CultureInfo.InvariantCulture))
' Displays 6:9:1 P
Console.WriteLine(date1.ToString("h:m:s.F t", _
CultureInfo.CreateSpecificCulture("el-GR")))
' Displays 6:9:1 µ
date1 = New Date(2008, 1, 1, 18, 9, 1, 500)
Console.WriteLine(date1.ToString("h:m:s.F t", _
CultureInfo.InvariantCulture))
' Displays 6:9:1.5 P
Console.WriteLine(date1.ToString("h:m:s.F t", _
CultureInfo.CreateSpecificCulture("el-GR")))
' Displays 6:9:1.5 µ
Volver a la tabla
Especificador de formato personalizado "tt"
El especificador de formato personalizado "tt" (más cualquier número de especificadores "t" adicionales)
representa designador AM/PM completo. El designador adaptado adecuado se recupera de la propiedad
DateTimeFormatInfo.AMDesignator o DateTimeFormatInfo.PMDesignator de la referencia cultural actual o
especificada. El designador AM se usa para todas las horas de 0:00:00 (medianoche) a 11:59:59.999. El
designador PM se usa para todas las horas de 12:00:00 (mediodía) a 23:59:59.999.
Asegúrese de usar el especificador "tt" para aquellos idiomas en los que sea necesario mantener la distinción
entre a. m. y p. m. Un ejemplo es el japonés, en el que los designadores de a.m. y p.m. se diferencian en el
segundo carácter en vez de en el primero.
En el ejemplo siguiente se incluye el especificador de formato personalizado "tt" en una cadena de formato
personalizado.
DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01 PM
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01 du.
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01.50 PM
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01.50 du.
Dim date1 As Date
date1 = #6:09:01PM#
Console.WriteLine(date1.ToString("hh:mm:ss tt", _
CultureInfo.InvariantCulture))
' Displays 06:09:01 PM
Console.WriteLine(date1.ToString("hh:mm:ss tt", _
CultureInfo.CreateSpecificCulture("hu-HU")))
' Displays 06:09:01 du.
date1 = New Date(2008, 1, 1, 18, 9, 1, 500)
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt", _
CultureInfo.InvariantCulture))
' Displays 06:09:01.50 PM
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt", _
CultureInfo.CreateSpecificCulture("hu-HU")))
' Displays 06:09:01.50 du.
Volver a la tabla
Volver a la tabla
Especificador de formato personalizado "yy"
El especificador de formato personalizado "yy" representa el año como un número de dos dígitos. Si el año tiene
más de dos dígitos, en el resultado sólo aparecen los dos dígitos de orden inferior. Si el año de dos dígitos tiene
menos de dos dígitos significativos, el número se rellenará con ceros iniciales hasta obtener dos dígitos.
En una operación de análisis, un año de dos dígitos que se analiza mediante el especificador de formato
personalizado “yy” se interpreta basándose en la propiedad de Calendar.TwoDigitYearMax del calendario actual
del proveedor de formato. En el ejemplo siguiente se analiza la representación en forma de cadena de una fecha
con un año de dos dígitos utilizando el calendario gregoriano predeterminado de la referencia cultural actual (en
este caso, en-US). Modifica el objeto CultureInfo de la referencia cultural actual para utilizar un objeto
GregorianCalendar cuya propiedad TwoDigitYearMax se ha modificado.
using System;
using System.Globalization;
using System.Threading;
cal.TwoDigitYearMax = 2099;
CultureInfo culture = (CultureInfo) CultureInfo.CurrentCulture.Clone();
culture.DateTimeFormat.Calendar = cal;
Thread.CurrentThread.CurrentCulture = culture;
Module Example
Public Sub Main()
Dim fmt As String = "dd-MMM-yy"
Dim value As String = "24-Jan-49"
cal.TwoDigitYearMax = 2099
Dim culture As CultureInfo = CType(CultureInfo.CurrentCulture.Clone(), CultureInfo)
culture.DateTimeFormat.Calendar = cal
Thread.CurrentThread.CurrentCulture = culture
En el ejemplo siguiente se incluye el especificador de formato personalizado "yy" en una cadena de formato
personalizado.
Volver a la tabla
Especificador de formato personalizado "yyy"
El especificador de formato personalizado "yyy" representa el año con un mínimo de tres dígitos. Si el año tiene
más de tres dígitos significativos, se incluyen en la cadena de resultado. Si el año tiene menos de tres dígitos, el
número se rellenará con ceros iniciales hasta obtener tres dígitos.
NOTE
Para el calendario budista tailandés, que puede tener años de cinco dígitos, este especificador de formato muestra todos
los dígitos significativos.
En el ejemplo siguiente se incluye el especificador de formato personalizado "yyy" en una cadena de formato
personalizado.
Volver a la tabla
Especificador de formato personalizado "yyyy"
El especificador de formato personalizado "Yyyy" representa el año con un mínimo de cuatro dígitos. Si el año
tiene más de cuatro dígitos significativos, se incluyen en la cadena resultante. Si el año tiene menos de cuatro
dígitos, el número se completa con ceros iniciales hasta obtener cuatro dígitos.
NOTE
En el calendario budista tailandés, que puede tener años de cinco dígitos, este especificador de formato muestra un
mínimo de cuatro dígitos.
En el ejemplo siguiente se incluye el especificador de formato personalizado "yyyy" en una cadena de formato
personalizado.
Volver a la tabla
Especificador de formato personalizado "yyyyy"
El especificador de formato personalizado "yyyyy" (más cualquier número de especificadores "y" adicionales)
representa el año con un mínimo de cinco dígitos. Si el año tiene más de cinco dígitos significativos, se incluyen
en la cadena resultante. Si el año tiene menos de cinco dígitos, el número se rellenará con ceros iniciales hasta
obtener cinco dígitos.
Si hay especificadores "y" adicionales, el número se rellenará con tantos ceros iniciales como sean necesarios
para obtener el número de especificadores "y".
En el ejemplo siguiente se incluye el especificador de formato personalizado "yyyyy" en una cadena de formato
personalizado.
Volver a la tabla
Volver a la tabla
Especificador de formato personalizado "zz"
Con valores DateTime, el especificador de formato personalizado "zz" representa el desfase con signo de la zona
horaria del sistema operativo local respecto a la hora UTC, medido en horas. No refleja el valor de la propiedad
DateTime.Kind de una instancia. Por esta razón, no se recomienda usar el especificador de formato "zz" con
valores DateTime.
Con valores DateTimeOffset, este especificador de formato representa el desfase en horas del valor
DateTimeOffset con respecto a la hora UTC.
La diferencia horaria se muestra siempre con un signo inicial. Un signo más (+) indica las horas de adelanto y un
signo menos (-) indica las horas de retraso con respecto a la hora UTC. Un desfase con un solo dígito tiene un
formato con un cero inicial.
En el ejemplo siguiente se incluye el especificador de formato personalizado "zz" en una cadena de formato
personalizado.
Volver a la tabla
Especificador de formato personalizado "zzz"
Con valores DateTime, el especificador de formato personalizado "zzz" representa el desfase con signo de la
zona horaria del sistema operativo local respecto a la hora UTC, medido en horas y minutos. No refleja el valor
de la propiedad DateTime.Kind de una instancia. Por esta razón, no se recomienda usar el especificador de
formato "zzz" con valores DateTime.
Con valores DateTimeOffset, este especificador de formato representa el desfase en horas y minutos del valor
DateTimeOffset con respecto a la hora UTC.
La diferencia horaria se muestra siempre con un signo inicial. Un signo más (+) indica las horas de adelanto y un
signo menos (-) indica las horas de retraso con respecto a la hora UTC. Un desfase con un solo dígito tiene un
formato con un cero inicial.
En el ejemplo siguiente se incluye el especificador de formato personalizado "zzz" en una cadena de formato
personalizado.
Volver a la tabla
NOTE
Para cambiar el separador de hora en una determinada cadena de fecha y hora, especifique el carácter separador en un
delimitador de cadena literal. Por ejemplo, la cadena de formato personalizado hh'_'dd'_'ss genera una cadena en que
"_" (guion bajo) siempre se utiliza como separador de hora. Para cambiar el separador de hora en todas las fechas de una
referencia cultural, cambie el valor de la propiedad DateTimeFormatInfo.TimeSeparator de la referencia cultural actual, o
cree una instancia de un objeto DateTimeFormatInfo, asigne el carácter a su propiedad TimeSeparator y llame a una
sobrecarga del método de formato que incluya un parámetro IFormatProvider.
Si el especificador de formato ":" se usa sin otros especificadores de formato personalizado, se interpretará
como un especificador de formato de fecha y hora estándar y producirá una excepción FormatException. Para
más información sobre cómo usar un especificador de formato único, vea Usar especificadores de formato
personalizado únicos más adelante en este artículo.
Volver a la tabla
Especificador de formato personalizado "/"
El especificador de formato personalizado "/" representa el separador de fecha, que se usa para diferenciar años,
meses y días. El separador de fecha adaptado adecuado se recupera de la propiedad
DateTimeFormatInfo.DateSeparator de la referencia cultural actual o especificada.
NOTE
Para cambiar el separador de fecha en una determinada cadena de fecha y hora, especifique el carácter separador en un
delimitador de cadena literal. Por ejemplo, la cadena de formato personalizado mm'/'dd'/'yyyy genera una cadena en
que "/" siempre se utiliza como separador de fecha. Para cambiar el separador de fecha en todas las fechas de una
referencia cultural, cambie el valor de la propiedad DateTimeFormatInfo.DateSeparator de la referencia cultural actual, o
cree una instancia de un objeto DateTimeFormatInfo, asigne el carácter a su propiedad DateSeparator y llame a una
sobrecarga del método de formato que incluya un parámetro IFormatProvider.
Si el especificador de formato "/" se usa sin otros especificadores de formato personalizado, se interpretará
como un especificador de formato de fecha y hora estándar y producirá una excepción FormatException. Para
más información sobre cómo usar un especificador de formato único, vea Usar especificadores de formato
personalizado únicos más adelante en este artículo.
Volver a la tabla
Literales de carácter
Los siguientes caracteres de una cadena de formato de fecha y hora personalizada están reservados y siempre
se interpretan como caracteres de formato o, en el caso de " , ' , / y \ , como caracteres especiales.
F H K M d
f g h m s
t y z % :
/ " ' \
Todos los demás caracteres se interpretan siempre como literales de carácter y, en una operación de formato, se
incluyen en la cadena de resultado sin modificar. En una operación de análisis, deben coincidir exactamente con
los caracteres de la cadena de entrada; la comparación distingue entre mayúsculas y minúsculas.
En el ejemplo siguiente se incluyen los caracteres literales "PST" (para hora estándar del Pacífico) y "PDT" (para
horario de verano del Pacífico) para representar la zona horaria local en una cadena de formato. Tenga en cuenta
que la cadena se incluye en la cadena de resultado y que una cadena que incluye la cadena de zona horaria local
también se analiza correctamente.
using System;
using System.Globalization;
// Parse a string.
String value = "25 Dec 2016 12:00 pm PST";
DateTime newDate;
if (DateTime.TryParseExact(value, formats, null,
DateTimeStyles.None, out newDate))
Console.WriteLine(newDate);
else
Console.WriteLine("Unable to parse '{0}'", value);
}
}
// The example displays the following output:
// 18 Aug 2016 04:50 PM PDT
// 12/25/2016 12:00:00 PM
Imports System.Globalization
Module Example
Public Sub Main()
Dim formats() As String = {"dd MMM yyyy hh:mm tt PST",
"dd MMM yyyy hh:mm tt PDT"}
Dim dat As New Date(2016, 8, 18, 16, 50, 0)
' Display the result string.
Console.WriteLine(dat.ToString(formats(1)))
Hay dos formas de indicar que los caracteres se han de interpretar como caracteres literales y no como
caracteres de reserva, para que se puedan incluir en una cadena de resultado o analizarse correctamente en una
cadena de entrada:
Al incluir un escape con cada carácter reservado. Para obtener más información, consulte Usar el carácter de
escape.
En el ejemplo siguiente se incluyen los caracteres literales "pst" (para hora estándar del Pacífico) para
representar la zona horaria local en una cadena de formato. Como "s" y "t" son cadenas de formato
personalizado, ambos caracteres deben incluir un escape para interpretarse como literales de carácter.
using System;
using System.Globalization;
// Parse a string.
String value = "25 Dec 2016 12:00 pm pst";
DateTime newDate;
if (DateTime.TryParseExact(value, format, null,
DateTimeStyles.None, out newDate))
Console.WriteLine(newDate);
else
Console.WriteLine("Unable to parse '{0}'", value);
}
}
// The example displays the following output:
// 18 Aug 2016 04:50 PM PDT
// 12/25/2016 12:00:00 PM
Imports System.Globalization
Module Example
Public Sub Main()
Dim fmt As String = "dd MMM yyyy hh:mm tt p\s\t"
Dim dat As New Date(2016, 8, 18, 16, 50, 0)
' Display the result string.
Console.WriteLine(dat.ToString(fmt))
Al incluir toda la cadena literal entre comillas o apóstrofes. El siguiente ejemplo es igual al anterior, excepto
que "pst" se incluye entre comillas para indicar que toda la cadena delimitada debe interpretarse como
literales de carácter.
using System;
using System.Globalization;
// Parse a string.
String value = "25 Dec 2016 12:00 pm pst";
DateTime newDate;
if (DateTime.TryParseExact(value, format, null,
DateTimeStyles.None, out newDate))
Console.WriteLine(newDate);
else
Console.WriteLine("Unable to parse '{0}'", value);
}
}
// The example displays the following output:
// 18 Aug 2016 04:50 PM PDT
// 12/25/2016 12:00:00 PM
Imports System.Globalization
Module Example
Public Sub Main()
Dim fmt As String = "dd MMM yyyy hh:mm tt ""pst"""
Dim dat As New Date(2016, 8, 18, 16, 50, 0)
' Display the result string.
Console.WriteLine(dat.ToString(fmt))
Notas
Usar especificadores de formato personalizado únicos
Una cadena con formato de fecha y hora personalizado se compone de dos o más caracteres. Los métodos de
formato de fecha y hora interpretan cualquier cadena de un único carácter como una cadena de formato de
fecha y hora estándar. Si no reconocen el carácter como un especificador de formato válido, producen una
excepción FormatException. Por ejemplo, una cadena de formato que solo se compone del especificador "h" se
interpreta como una cadena de formato de fecha y hora estándar. Sin embargo, en este caso concreto, se
produce una excepción porque no existe ningún especificador de formato de fecha y hora estándar "h".
Para usar cualquiera de los especificadores de formato de fecha y hora personalizado como el único
especificador en una cadena de formato (es decir, usar el especificador de formato personalizado "d", "f", "F", "g",
"h", "H", "K", "m", "M", "s", "t", "y", "z", ":" o "/"), incluya un espacio delante o detrás del especificador, o incluya un
especificador de formato de porcentaje ("%") delante del único especificador de fecha y hora personalizado.
Por ejemplo, " %h" se interpreta como una cadena de formato de fecha y hora personalizado que muestra la
hora representada por el valor de fecha y hora actual. También puede usar la cadena de formato " h" o "h ",
aunque esto incluye un espacio en la cadena de resultado junto con la hora. En el ejemplo siguiente se muestran
estas tres cadenas de formato.
Console.WriteLine("'{0:%h}'", dat1);
Console.WriteLine("'{0: h}'", dat1);
Console.WriteLine("'{0:h }'", dat1);
// The example displays the following output:
// '1'
// ' 1'
// '1 '
Console.WriteLine("'{0:%h}'", dat1)
Console.WriteLine("'{0: h}'", dat1)
Console.WriteLine("'{0:h }'", dat1)
' The example displays the following output:
' '1'
' ' 1'
' '1 '
NOTE
Algunos compiladores, como los compiladores de C# y C++, también pueden interpretar un único carácter de barra
diagonal inversa como un carácter de escape. Para asegurarse de que una cadena se interpreta correctamente al darle
formato, puede usar el carácter literal de cadena textual (el carácter @) antes de la cadena en C# o puede agregar otro
carácter de barra diagonal inversa delante de cada barra diagonal inversa en C# y C++. En el siguiente ejemplo de C# se
muestran ambos enfoques.
En el ejemplo siguiente se usa el carácter de escape para evitar que la operación de formato interprete los
caracteres "h" y "m" como especificadores de formato.
DateTime date = new DateTime(2009, 06, 15, 13, 45, 30, 90);
string fmt1 = "h \\h m \\m";
string fmt2 = @"h \h m \m";
Vea también
System.DateTime
System.IFormatProvider
Aplicar formato a tipos
Cadenas con formato de fecha y hora estándar
Ejemplo: utilidad de formato WinForms de .NET Core (C#)
Ejemplo: utilidad de formato WinForms de .NET Core (Visual Basic)
Cadenas de formato TimeSpan estándar
16/09/2020 • 15 minutes to read • Edit Online
Una cadena de formato estándar TimeSpan usa un único especificador de formato para definir la representación
de texto de un valor TimeSpan resultante de una operación de formato. Cualquier cadena de formato que
contenga más de un carácter, incluido el espacio en blanco, se interpreta como una cadena de formato TimeSpan
personalizado. Para más información, consulte Cadenas de formato TimeSpan personalizadas.
Las representaciones de cadena de los valores TimeSpan se generan mediante llamadas a las sobrecargas del
método TimeSpan.ToString, y también mediante métodos que admiten formatos compuestos, como String.Format.
Para obtener más información, consulte Aplicar formato a tipos y Formatos compuestos. En el siguiente ejemplo
se muestra el uso de cadenas de formato estándar en operaciones de formato.
using System;
Module Example
Public Sub Main()
Dim duration As New TimeSpan(1, 12, 23, 62)
Dim output As String = "Time of Travel: " + duration.ToString("c")
Console.WriteLine(output)
Module Example
Public Sub Main()
Dim value As String = "1.03:14:56.1667"
Dim interval As TimeSpan
Try
interval = TimeSpan.ParseExact(value, "c", Nothing)
Console.WriteLine("Converted '{0}' to {1}", value, interval)
Catch e As FormatException
Console.WriteLine("{0}: Bad Format", value)
Catch e As OverflowException
Console.WriteLine("{0}: Out of Range", value)
End Try
ESP EC IF IC A DO R DE
F O RM ATO N O M B RE DESC RIP C IÓ N E JEM P LO S
ESP EC IF IC A DO R DE
F O RM ATO N O M B RE DESC RIP C IÓ N E JEM P LO S
Más información:
Especificador de formato
constante ("c").
"g" Formato corto general Este especificador solo New TimeSpan(1, 3, 16,
genera lo necesario. Tiene en 50, 500)
cuenta la referencia cultural -> 1:3:16:50.5 (en-US)
y su forma es
[-] New TimeSpan(1, 3, 16,
[d':']h':'mm':'ss[.FFFFFFF] 50, 500)
. -> 1:3:16:50,5 (fr-FR)
"G" Formato general largo Este especificador siempre New TimeSpan(18, 30, 0)
genera días y siete dígitos -> 0:18:30:00.0000000 (en-
fraccionarios. Tiene en US)
cuenta la referencia cultural
y su forma es New TimeSpan(18, 30, 0)
[- -> 0:18:30:00,0000000 (fr-
]d':'hh':'mm':'ss.fffffff
FR)
.
Más información:
Especificador de formato
largo general ("G").
EL EM EN TO DESC RIP C IÓ N
A diferencia de los especificadores de formato de "g" y "G", el especificador de formato "c" no tiene en cuenta la
referencia cultural. Produce la representación de cadena de un valor TimeSpan que es invariable y común a todas
las versiones anteriores de .NET previas a .NET Framework 4. "c" es la cadena de formato TimeSpan
predeterminado; el método TimeSpan.ToString() da formato a un valor de intervalo de tiempo mediante la cadena
de formato "c".
NOTE
TimeSpan también admite las cadenas de formato estándar "t" y "T", cuyo comportamiento es idéntico al de la cadena de
formato estándar "c".
En el ejemplo siguiente se crea una instancia de dos objetos TimeSpan, que se usan para realizar operaciones
aritméticas y se muestra el resultado. En cada caso, se utiliza un formato compuesto para mostrar el valor
TimeSpan mediante el especificador de formato "c".
using System;
EL EM EN TO DESC RIP C IÓ N
Al igual que el especificador de formato "G", el especificador de formato "g" se localiza. Su separador de fracciones
de segundo se basa en la referencia cultural actual o en la propiedad NumberDecimalSeparator de una referencia
cultural especificada.
En el ejemplo siguiente se crea una instancia de dos objetos TimeSpan, que se usan para realizar operaciones
aritméticas y se muestra el resultado. En cada caso, se utiliza un formato compuesto para mostrar el valor
TimeSpan mediante el especificador de formato "g". Además, se da formato al valor TimeSpan mediante las
convenciones de formato de la referencia cultural del sistema actual (que, en este caso, es inglés - Estados Unidos
o en-US) y la referencia cultural de Francia de francés (fr-FR).
using System;
using System.Globalization;
Imports System.Globalization
Module Example
Public Sub Main()
Dim interval1, interval2 As TimeSpan
interval1 = New TimeSpan(7, 45, 16)
interval2 = New TimeSpan(18, 12, 38)
EL EM EN TO DESC RIP C IÓ N
Al igual que el especificador de formato "G", el especificador de formato "g" se localiza. Su separador de fracciones
de segundo se basa en la referencia cultural actual o en la propiedad NumberDecimalSeparator de una referencia
cultural especificada.
En el ejemplo siguiente se crea una instancia de dos objetos TimeSpan, que se usan para realizar operaciones
aritméticas y se muestra el resultado. En cada caso, se utiliza un formato compuesto para mostrar el valor
TimeSpan mediante el especificador de formato "G". Además, se da formato al valor TimeSpan mediante las
convenciones de formato de la referencia cultural del sistema actual (que, en este caso, es inglés - Estados Unidos
o en-US) y la referencia cultural de Francia de francés (fr-FR).
using System;
using System.Globalization;
Imports System.Globalization
Module Example
Public Sub Main()
Dim interval1, interval2 As TimeSpan
interval1 = New TimeSpan(7, 45, 16)
interval2 = New TimeSpan(18, 12, 38)
Vea también
Aplicación de formato a tipos
Cadenas de formato TimeSpan personalizado
Analizar cadenas
Cadenas de formato TimeSpan personalizado
16/09/2020 • 64 minutes to read • Edit Online
Una cadena de formato TimeSpan define la representación de cadena de un valor TimeSpan generado por una
operación de formato. Una cadena de formato personalizado consta de uno o varios especificadores de formato
TimeSpan personalizado, además de un número de caracteres literales. Cualquier cadena que no sea una cadena
de formato TimeSpan estándar se interpreta como una cadena de formato TimeSpan personalizado.
IMPORTANT
Los especificadores de formato TimeSpan personalizado no incluyen símbolos de separador de marcadores de posición,
como los símbolos que separan los días de las horas, las horas de los minutos o los segundos de las fracciones de segundo.
Estos símbolos deben incluirse en la cadena de formato personalizado como literales de cadena. Por ejemplo,
"dd\.hh\:mm" define un punto (.) como separador entre los días y las horas, y un signo de dos puntos (:) como separador
entre las horas y los minutos.
Los especificadores de formato TimeSpan personalizado tampoco incluyen un símbolo de signo que permita distinguir entre
los intervalos de tiempo negativos y positivos. Para incluir un símbolo de signo, es necesario construir una cadena de
formato utilizando lógica condicional. En la sección Otros caracteres se incluye un ejemplo.
Las representaciones de cadena de los valores TimeSpan se generan mediante llamadas a las sobrecargas del
método TimeSpan.ToString, y también mediante métodos que admiten formatos compuestos, como
String.Format. Para obtener más información, consulte Aplicar formato a tipos y Formatos compuestos. En el
siguiente ejemplo, se muestra el uso de cadenas de formato personalizado en operaciones de formato.
using System;
using System;
value = "6";
if (TimeSpan.TryParseExact(value, "%d", null, out interval))
Console.WriteLine("{0} --> {1}", value, interval.ToString("c"));
else
Console.WriteLine("Unable to parse '{0}'", value);
value = "16:32.05";
if (TimeSpan.TryParseExact(value, @"mm\:ss\.ff", null, out interval))
Console.WriteLine("{0} --> {1}", value, interval.ToString("c"));
else
Console.WriteLine("Unable to parse '{0}'", value);
value= "12.035";
if (TimeSpan.TryParseExact(value, "ss\\.fff", null, out interval))
Console.WriteLine("{0} --> {1}", value, interval.ToString("c"));
else
Console.WriteLine("Unable to parse '{0}'", value);
}
}
// The example displays the following output:
// 6 --> 6.00:00:00
// 16:32.05 --> 00:16:32.0500000
// 12.035 --> 00:00:12.0350000
Module Example
Public Sub Main()
Dim value As String = Nothing
Dim interval As TimeSpan
value = "6"
If TimeSpan.TryParseExact(value, "%d", Nothing, interval) Then
Console.WriteLine("{0} --> {1}", value, interval.ToString("c"))
Else
Console.WriteLine("Unable to parse '{0}'", value)
End If
value = "16:32.05"
If TimeSpan.TryParseExact(value, "mm\:ss\.ff", Nothing, interval) Then
Console.WriteLine("{0} --> {1}", value, interval.ToString("c"))
Else
Console.WriteLine("Unable to parse '{0}'", value)
End If
value = "12.035"
If TimeSpan.TryParseExact(value, "ss\.fff", Nothing, interval) Then
Console.WriteLine("{0} --> {1}", value, interval.ToString("c"))
Else
Console.WriteLine("Unable to parse '{0}'", value)
End If
End Sub
End Module
' The example displays the following output:
' 6 --> 6.00:00:00
' 16:32.05 --> 00:16:32.0500000
' 12.035 --> 00:00:12.0350000
"d", "%d" Número de días completos de un new TimeSpan(6, 14, 32, 17, 685):
intervalo de tiempo.
%d --> "6"
Más información: Especificador de
formato personalizado "d". d\.hh\:mm --> "6.14:32"
"dd"-"dddddddd" Número de días completos de un new TimeSpan(6, 14, 32, 17, 685):
intervalo de tiempo, que se completa
con tantos ceros iniciales como sean ddd --> "006"
necesarios.
dd\.hh\:mm --> "06.14:32"
Más información: Especificadores de
formato personalizado "dd"-
"dddddddd".
"h", "%h" Número de horas completas de un new TimeSpan(6, 14, 32, 17, 685):
intervalo de tiempo que no se cuentan
como parte de los días. Las horas con %h --> "14"
un solo dígito no se escriben con un
cero a la izquierda. hh\:mm --> "14:32"
Más información: Especificador de
formato personalizado "h".
ESP EC IF IC A DO R DE F O RM ATO DESC RIP C IÓ N E JEM P LO
"hh" Número de horas completas de un new TimeSpan(6, 14, 32, 17, 685):
intervalo de tiempo que no se cuentan
como parte de los días. Las horas con hh --> "14"
un solo dígito se escriben con un cero a
la izquierda. new TimeSpan(6, 8, 32, 17, 685):
"m", "%m" Número de minutos completos de un new TimeSpan(6, 14, 8, 17, 685):
intervalo de tiempo que no se incluyen
como parte de las horas o los días. Los %m --> "8"
minutos con un solo dígito no se
escriben con un cero a la izquierda. h\:m --> "14:8"
Más información: Especificador de
formato personalizado "m".
ss\.FFF : 03.1
ESP EC IF IC A DO R DE F O RM ATO DESC RIP C IÓ N E JEM P LO
ss\.FFFFF : 03.1
ss\.FFFFFF : 03.1
Cualquier otro carácter Cualquier otro carácter sin escape se new TimeSpan(14, 32, 17):
interpreta como especificador de
formato personalizado. hh\:mm\:ss --> "14:32:17"
Volver a la tabla
Volver a la tabla
Especificador de formato personalizado "h"
El especificador de formato personalizado "h" presenta el valor de la propiedad TimeSpan.Hours, que representa
el número de horas completas de un intervalo de tiempo que no se cuentan como parte del componente de días.
Devuelve un valor de cadena de un dígito si el valor de la propiedad TimeSpan.Hours es de 0 a 9; devuelve un
valor de cadena de dos dígitos si el valor de la propiedad TimeSpan.Hours es de 10 a 23.
Si el especificador de formato personalizado "h" se utiliza solo, especifique "%h" de modo que no se interprete
por error como una cadena de formato estándar. Esto se muestra en el ejemplo siguiente.
Normalmente, en las operaciones de análisis, una cadena de entrada que incluye solamente un número se
interpreta como número de días. Se puede utilizar el especificador de formato personalizado "%h"" para que la
cadena numérica se interprete como número de horas. Esto se muestra en el ejemplo siguiente.
Volver a la tabla
Volver a la tabla
Normalmente, en las operaciones de análisis, una cadena de entrada que incluye solamente un número se
interpreta como número de días. Se puede utilizar el especificador de formato personalizado "%m"" para que la
cadena numérica se interprete como número de minutos. Esto se muestra en el ejemplo siguiente.
Volver a la tabla
Volver a la tabla
TimeSpan ts = TimeSpan.FromSeconds(12.465);
Console.WriteLine(ts.ToString("%s"));
// The example displays the following output:
// 12
Normalmente, en las operaciones de análisis, una cadena de entrada que incluye solamente un número se
interpreta como número de días. Se puede utilizar el especificador de formato personalizado "%s"" para que la
cadena numérica se interprete como número de segundos. Esto se muestra en el ejemplo siguiente.
Volver a la tabla
Volver a la tabla
For ctr = 1 To 7
fmt = New String("f"c, ctr)
If fmt.Length = 1 Then fmt = "%" + fmt
Console.WriteLine("{0,10}: {1:" + fmt + "}", fmt, ts)
Next
Console.WriteLine()
For ctr = 1 To 7
fmt = New String("f"c, ctr)
Console.WriteLine("{0,10}: {1:s\." + fmt + "}", "s\." + fmt, ts)
Next
' The example displays the following output:
' %f: 8
' ff: 87
' fff: 876
' ffff: 8765
' fffff: 87654
' ffffff: 876543
' fffffff: 8765432
'
' s\.f: 29.8
' s\.ff: 29.87
' s\.fff: 29.876
' s\.ffff: 29.8765
' s\.fffff: 29.87654
' s\.ffffff: 29.876543
' s\.fffffff: 29.8765432
Volver a la tabla
For ctr = 1 To 7
fmt = New String("f"c, ctr)
If fmt.Length = 1 Then fmt = "%" + fmt
Console.WriteLine("{0,10}: {1:" + fmt + "}", fmt, ts)
Next
Console.WriteLine()
For ctr = 1 To 7
fmt = New String("f"c, ctr)
Console.WriteLine("{0,10}: {1:s\." + fmt + "}", "s\." + fmt, ts)
Next
' The example displays the following output:
' %f: 8
' ff: 87
' fff: 876
' ffff: 8765
' fffff: 87654
' ffffff: 876543
' fffffff: 8765432
'
' s\.f: 29.8
' s\.ff: 29.87
' s\.fff: 29.876
' s\.ffff: 29.8765
' s\.fffff: 29.87654
' s\.ffffff: 29.876543
' s\.fffffff: 29.8765432
Volver a la tabla
For ctr = 1 To 7
fmt = New String("f"c, ctr)
If fmt.Length = 1 Then fmt = "%" + fmt
Console.WriteLine("{0,10}: {1:" + fmt + "}", fmt, ts)
Next
Console.WriteLine()
For ctr = 1 To 7
fmt = New String("f"c, ctr)
Console.WriteLine("{0,10}: {1:s\." + fmt + "}", "s\." + fmt, ts)
Next
' The example displays the following output:
' %f: 8
' ff: 87
' fff: 876
' ffff: 8765
' fffff: 87654
' ffffff: 876543
' fffffff: 8765432
'
' s\.f: 29.8
' s\.ff: 29.87
' s\.fff: 29.876
' s\.ffff: 29.8765
' s\.fffff: 29.87654
' s\.ffffff: 29.876543
' s\.fffffff: 29.8765432
Volver a la tabla
For ctr = 1 To 7
fmt = New String("f"c, ctr)
If fmt.Length = 1 Then fmt = "%" + fmt
Console.WriteLine("{0,10}: {1:" + fmt + "}", fmt, ts)
Next
Console.WriteLine()
For ctr = 1 To 7
fmt = New String("f"c, ctr)
Console.WriteLine("{0,10}: {1:s\." + fmt + "}", "s\." + fmt, ts)
Next
' The example displays the following output:
' %f: 8
' ff: 87
' fff: 876
' ffff: 8765
' fffff: 87654
' ffffff: 876543
' fffffff: 8765432
'
' s\.f: 29.8
' s\.ff: 29.87
' s\.fff: 29.876
' s\.ffff: 29.8765
' s\.fffff: 29.87654
' s\.ffffff: 29.876543
' s\.fffffff: 29.8765432
Volver a la tabla
For ctr = 1 To 7
fmt = New String("f"c, ctr)
If fmt.Length = 1 Then fmt = "%" + fmt
Console.WriteLine("{0,10}: {1:" + fmt + "}", fmt, ts)
Next
Console.WriteLine()
For ctr = 1 To 7
fmt = New String("f"c, ctr)
Console.WriteLine("{0,10}: {1:s\." + fmt + "}", "s\." + fmt, ts)
Next
' The example displays the following output:
' %f: 8
' ff: 87
' fff: 876
' ffff: 8765
' fffff: 87654
' ffffff: 876543
' fffffff: 8765432
'
' s\.f: 29.8
' s\.ff: 29.87
' s\.fff: 29.876
' s\.ffff: 29.8765
' s\.fffff: 29.87654
' s\.ffffff: 29.876543
' s\.fffffff: 29.8765432
Volver a la tabla
For ctr = 1 To 7
fmt = New String("f"c, ctr)
If fmt.Length = 1 Then fmt = "%" + fmt
Console.WriteLine("{0,10}: {1:" + fmt + "}", fmt, ts)
Next
Console.WriteLine()
For ctr = 1 To 7
fmt = New String("f"c, ctr)
Console.WriteLine("{0,10}: {1:s\." + fmt + "}", "s\." + fmt, ts)
Next
' The example displays the following output:
' %f: 8
' ff: 87
' fff: 876
' ffff: 8765
' fffff: 87654
' ffffff: 876543
' fffffff: 8765432
'
' s\.f: 29.8
' s\.ff: 29.87
' s\.fff: 29.876
' s\.ffff: 29.8765
' s\.fffff: 29.87654
' s\.ffffff: 29.876543
' s\.fffffff: 29.8765432
Volver a la tabla
For ctr = 1 To 7
fmt = New String("f"c, ctr)
If fmt.Length = 1 Then fmt = "%" + fmt
Console.WriteLine("{0,10}: {1:" + fmt + "}", fmt, ts)
Next
Console.WriteLine()
For ctr = 1 To 7
fmt = New String("f"c, ctr)
Console.WriteLine("{0,10}: {1:s\." + fmt + "}", "s\." + fmt, ts)
Next
' The example displays the following output:
' %f: 8
' ff: 87
' fff: 876
' ffff: 8765
' fffff: 87654
' ffffff: 876543
' fffffff: 8765432
'
' s\.f: 29.8
' s\.ff: 29.87
' s\.fff: 29.876
' s\.ffff: 29.8765
' s\.fffff: 29.87654
' s\.ffffff: 29.876543
' s\.fffffff: 29.8765432
Volver a la tabla
Console.WriteLine("Formatting:");
TimeSpan ts1 = TimeSpan.Parse("0:0:3.669");
Console.WriteLine("{0} ('%F') --> {0:%F}", ts1);
Console.WriteLine("Parsing:");
string[] inputs = { "0:0:03.", "0:0:03.1", "0:0:03.12" };
string fmt = @"h\:m\:ss\.F";
TimeSpan ts3;
Console.WriteLine("Parsing:")
Dim inputs() As String = {"0:0:03.", "0:0:03.1", "0:0:03.12"}
Dim fmt As String = "h\:m\:ss\.F"
Dim ts3 As TimeSpan
Volver a la tabla
Console.WriteLine("Parsing:");
string[] inputs = { "0:0:03.", "0:0:03.1", "0:0:03.127" };
string fmt = @"h\:m\:ss\.FF";
TimeSpan ts3;
Console.WriteLine("Formatting:")
Dim ts1 As TimeSpan = TimeSpan.Parse("0:0:3.697")
Console.WriteLine("{0} ('FF') --> {0:FF}", ts1)
Console.WriteLine("Parsing:")
Dim inputs() As String = {"0:0:03.", "0:0:03.1", "0:0:03.127"}
Dim fmt As String = "h\:m\:ss\.FF"
Dim ts3 As TimeSpan
Volver a la tabla
Especificador de formato personalizado "FFF"
El especificador de formato personalizado "FFF" (tres caracteres "F") presenta las milésimas de segundo de un
intervalo de tiempo. En una operación de formato, se truncan los dígitos fraccionarios restantes. Si hay ceros
fraccionarios finales, estos no se incluyen en la cadena de resultado. En una operación de análisis que llama al
método TimeSpan.ParseExact o TimeSpan.TryParseExact, la presencia de las décimas, centésimas y milésimas de
segundo es opcional.
En el siguiente ejemplo, se utiliza el especificador de formato personalizado "FFF" para mostrar las milésimas de
segundo de un valor TimeSpan. También se utiliza este especificador de formato personalizado en una operación
de análisis.
Console.WriteLine("Formatting:");
TimeSpan ts1 = TimeSpan.Parse("0:0:3.6974");
Console.WriteLine("{0} ('FFF') --> {0:FFF}", ts1);
Console.WriteLine("Parsing:");
string[] inputs = { "0:0:03.", "0:0:03.12", "0:0:03.1279" };
string fmt = @"h\:m\:ss\.FFF";
TimeSpan ts3;
Console.WriteLine("Parsing:")
Dim inputs() As String = {"0:0:03.", "0:0:03.12", "0:0:03.1279"}
Dim fmt As String = "h\:m\:ss\.FFF"
Dim ts3 As TimeSpan
Volver a la tabla
Console.WriteLine("Parsing:");
string[] inputs = { "0:0:03.", "0:0:03.12", "0:0:03.12795" };
string fmt = @"h\:m\:ss\.FFFF";
TimeSpan ts3;
Console.WriteLine("Formatting:")
Dim ts1 As TimeSpan = TimeSpan.Parse("0:0:3.69749")
Console.WriteLine("{0} ('FFFF') --> {0:FFFF}", ts1)
Console.WriteLine("Parsing:")
Dim inputs() As String = {"0:0:03.", "0:0:03.12", "0:0:03.12795"}
Dim fmt As String = "h\:m\:ss\.FFFF"
Dim ts3 As TimeSpan
Volver a la tabla
Especificador de formato personalizado "FFFFF"
El especificador de formato personalizado "FFFFF" (cinco caracteres "F") presenta las cienmilésimas de segundo
de un intervalo de tiempo. En una operación de formato, se truncan los dígitos fraccionarios restantes. Si hay
ceros fraccionarios finales, estos no se incluyen en la cadena de resultado. En una operación de análisis que llama
al método TimeSpan.ParseExact o TimeSpan.TryParseExact, la presencia de las décimas, centésimas, milésimas,
diezmilésimas y cienmilésimas de segundo es opcional.
En el siguiente ejemplo, se utiliza el especificador de formato personalizado "FFFFF" para mostrar las
cienmilésimas de segundo de un valor TimeSpan. También se utiliza este especificador de formato personalizado
en una operación de análisis.
Console.WriteLine("Formatting:");
TimeSpan ts1 = TimeSpan.Parse("0:0:3.697497");
Console.WriteLine("{0} ('FFFFF') --> {0:FFFFF}", ts1);
Console.WriteLine("Parsing:");
string[] inputs = { "0:0:03.", "0:0:03.12", "0:0:03.127956" };
string fmt = @"h\:m\:ss\.FFFFF";
TimeSpan ts3;
Console.WriteLine("Parsing:")
Dim inputs() As String = {"0:0:03.", "0:0:03.12", "0:0:03.127956"}
Dim fmt As String = "h\:m\:ss\.FFFFF"
Dim ts3 As TimeSpan
Volver a la tabla
Console.WriteLine("Parsing:");
string[] inputs = { "0:0:03.", "0:0:03.12", "0:0:03.1279569" };
string fmt = @"h\:m\:ss\.FFFFFF";
TimeSpan ts3;
Console.WriteLine("Formatting:")
Dim ts1 As TimeSpan = TimeSpan.Parse("0:0:3.6974974")
Console.WriteLine("{0} ('FFFFFF') --> {0:FFFFFF}", ts1)
Console.WriteLine("Parsing:")
Dim inputs() As String = {"0:0:03.", "0:0:03.12", "0:0:03.1279569"}
Dim fmt As String = "h\:m\:ss\.FFFFFF"
Dim ts3 As TimeSpan
Volver a la tabla
Especificador de formato personalizado "FFFFFFF"
El especificador de formato personalizado "FFFFFFF" (siete caracteres "F") presenta las diezmillonésimas de
segundo (o fracciones de paso) de un intervalo de tiempo. Si hay ceros fraccionarios finales, estos no se incluyen
en la cadena de resultado. En una operación de análisis que llama al método TimeSpan.ParseExact or
TimeSpan.TryParseExact, la presencia de los siete dígitos fraccionarios en la cadena de entrada es opcional.
En el siguiente ejemplo, se utiliza el especificador de formato personalizado "FFFFFFF" para mostrar las fracciones
de segundo de un valor TimeSpan. También se utiliza este especificador de formato personalizado en una
operación de análisis.
Console.WriteLine("Formatting:");
TimeSpan ts1 = TimeSpan.Parse("0:0:3.6974974");
Console.WriteLine("{0} ('FFFFFFF') --> {0:FFFFFFF}", ts1);
Console.WriteLine("Parsing:");
string[] inputs = { "0:0:03.", "0:0:03.12", "0:0:03.1279569" };
string fmt = @"h\:m\:ss\.FFFFFFF";
TimeSpan ts3;
Console.WriteLine("Parsing:")
Dim inputs() As String = {"0:0:03.", "0:0:03.12", "0:0:03.1279569"}
Dim fmt As String = "h\:m\:ss\.FFFFFFF"
Dim ts3 As TimeSpan
Volver a la tabla
Otros caracteres
Cualquier otro carácter sin escape de una cadena de formato, incluido el carácter de espacio en blanco, se
interpreta como especificador de formato personalizado. En la mayoría de los casos, la presencia de cualquier
otro carácter sin escape da lugar a una excepción FormatException.
Para incluir un carácter literal en una cadena de formato, se puede proceder de dos formas:
Se puede escribirlo entre comillas sencillas (delimitador de cadena literal).
Se puede anteponer una barra diagonal inversa ("\"), que se interpreta como un carácter de escape. En C#,
esto significa que la cadena de formato debe ser @-quoted o que el carácter literal debe ir precedido de
una barra diagonal inversa adicional.
En algunos casos, puede que sea necesario usar lógica condicional para incluir un carácter literal de escape
en una cadena de formato. En el ejemplo siguiente se usa lógica condicional para incluir un símbolo de
signo para los intervalos de tiempo negativos.
using System;
Console.WriteLine(result.ToString(fmt));
Console.WriteLine("Interval: {0:" + fmt + "}", result);
}
}
// The example displays output like the following:
// -1291.10:54
// Interval: -1291.10:54
Module Example
Public Sub Main()
Dim result As TimeSpan = New DateTime(2010, 01, 01) - Date.Now
Dim fmt As String = If(result < TimeSpan.Zero, "\-", "") + "dd\.hh\:mm"
Console.WriteLine(result.ToString(fmt))
Console.WriteLine("Interval: {0:" + fmt + "}", result)
End Sub
End Module
' The example displays output like the following:
' -1291.10:54
' Interval: -1291.10:54
.NET no define ninguna gramática para los separadores en los intervalos de tiempo. Esto significa que los
separadores entre los días y las horas, las horas y los minutos, los minutos y los segundos, y los segundos y las
fracciones de segundo deben tratarse todos como literales de carácter en una cadena de formato.
En el siguiente ejemplo, se utilizan el carácter de escape y la comilla simple para definir una cadena de formato
personalizado que incluye la palabra "minutes" en la cadena de salida.
Volver a la tabla
Vea también
Aplicación de formato a tipos
Cadenas de formato TimeSpan estándar
Cadenas de formato de enumeración
16/09/2020 • 5 minutes to read • Edit Online
Puede usar el método Enum.ToString para crear un objeto de cadena que represente el valor de cadena, numérico
o hexadecimal del miembro de una enumeración. Este método toma una de las cadenas de formato de
enumeración para especificar el valor que quiere que se devuelva.
En las secciones siguientes se enumeran las cadenas de formato de enumeración y los valores que devuelven.
Estos especificadores de formato no distinguen mayúsculas de minúsculas.
Gog
Si es posible, muestra la entrada de enumeración como valor de cadena y, si no, muestra el valor entero de la
instancia actual. Si la enumeración se define con el conjunto de atributos Flags , los valores de cadena de cada
entrada válida se concatenan, separados por comas. Si no se establece el atributo Flags , se muestra un valor no
válido como entrada numérica. En el siguiente ejemplo se muestra el uso del especificador de formato G.
Fof
Si es posible, muestra la entrada de enumeración como valor de cadena. Si el valor se puede mostrar por
completo como suma de las entradas de la enumeración (aunque el atributo Flags no esté presente), los valores
de cadena de cada entrada válida se concatenan, separados por comas. Si las entradas de enumeración no
pueden determinar completamente el valor, a este se le da formato como valor entero. En el siguiente ejemplo se
muestra el uso del especificador de formato F.
Dod
Muestra la entrada de enumeración como valor entero en la representación más corta posible. En el siguiente
ejemplo se muestra el uso del especificador de formato D.
Console.WriteLine(ConsoleColor.Cyan.ToString("D")); // Displays 11
FileAttributes attributes = FileAttributes.Hidden |
FileAttributes.Archive;
Console.WriteLine(attributes.ToString("D")); // Displays 34
Xox
Muestra la entrada de enumeración como valor hexadecimal. El valor se representa con ceros a la izquierda,
según sea necesario, para garantizar que la cadena de resultados tenga dos caracteres para cada byte en el tipo
numérico que subyace en el tipo de enumeración. En el siguiente ejemplo se muestra el uso del especificador de
formato X. En el ejemplo, el tipo subyacente tanto de ConsoleColor como de FileAttributes es Int32, o un entero
de 32 bits (o 4 bytes), que produce una cadena de resultados de ocho caracteres.
Ejemplo
En el ejemplo siguiente se define una enumeración denominada Colors que consta de tres entradas: Red , Blue
y Green .
Una vez definida la enumeración, se puede declarar una instancia de la siguiente manera.
Luego se puede usar el método Color.ToString(System.String) para mostrar el valor de enumeración de maneras
diferentes, según el especificador de formato pasado.
Console.WriteLine("The value of myColor is {0}.",
myColor.ToString("G"));
Console.WriteLine("The value of myColor is {0}.",
myColor.ToString("F"));
Console.WriteLine("The value of myColor is {0}.",
myColor.ToString("D"));
Console.WriteLine("The value of myColor is 0x{0}.",
myColor.ToString("X"));
// The example displays the following output to the console:
// The value of myColor is Green.
// The value of myColor is Green.
// The value of myColor is 3.
// The value of myColor is 0x00000003.
Vea también
Aplicación de formato a tipos
Formatos compuestos
16/09/2020 • 24 minutes to read • Edit Online
La característica de formato compuesto de .NET toma una lista de objetos y una cadena de formato
compuesto como entrada. Una cadena de formato compuesto está formada por texto fijo combinado con
marcadores de posición indizados, que reciben el nombre de elementos de formato, y que se corresponden
con los objetos de la lista. La operación de formato genera una cadena de resultado compuesta por el texto
fijo original combinado con la representación de cadena de los objetos de la lista.
IMPORTANT
En lugar de usar cadenas de formato compuesto, puede usar cadenas interpoladas si el idioma y la versión de idioma
que está usando son compatibles con ellos. Una cadena interpolada es una cadena que contiene expresiones
interpoladas. Cada expresión interpolada se resuelve con el valor de la expresión y se incluye en la cadena de resultado
cuando se asigna la cadena. Para obtener más información, vea Interpolación de cadenas (Referencia de C#) y Cadenas
interpoladas (referencia de Visual Basic).
El texto fijo es " Name = " y " , hours = ". Los elementos de formato son " {0} ", cuyo índice es 0, que
corresponde al objeto name , y " {1:hh} ", cuyo índice es 1, que corresponde al objeto DateTime.Now .
string primes;
primes = String.Format("Prime numbers less than 10: {0}, {1}, {2}, {3}",
2, 3, 5, 7 );
Console.WriteLine(primes);
// The example displays the following output:
// Prime numbers less than 10: 2, 3, 5, 7
Los elementos de formato múltiple se pueden referir al mismo elemento de la lista de objetos mediante la
especificación del mismo especificador de parámetro. Por ejemplo, se puede dar formato al mismo valor
numérico en formato hexadecimal, científico y de número mediante la especificación de una cadena de
formato compuesto como esta: "0x{0:X} {0:E} {0:N}", como se muestra en el ejemplo siguiente.
Cada elemento de formato puede hacer referencia a cualquier objeto de la lista. Por ejemplo, si existen tres
objetos, se puede dar formato al segundo, primero y tercer objeto mediante la especificación de una cadena
de formato compuesto como esta: "{1} {0} {2}". Un objeto al que no hace referencia ningún elemento de
formato se omite. Se produce una excepción de tipo FormatException en tiempo de ejecución si un
especificador de parámetro designa un elemento fuera de los límites de la lista de objetos.
Alignment (Componente )
El componente opcional alignment es un entero con signo que indica el ancho de campo con formato
preferido. Si el valor de alignment es menor que la longitud de la cadena con formato, se omite alignment y
se usa la longitud de la cadena con formato como el ancho de campo. Los datos con formato del campo
están alineados a la derecha si alignment es positivo y a la izquierda si alignment es negativo. Si hace falta
relleno, se utiliza un espacio en blanco. Si se especifica alignment, es necesaria la coma.
El siguiente ejemplo define dos matrices, que contiene los nombres de empleados y otra contiene las horas
que han trabajado en un período de dos semanas. La cadena de formato compuesto alinea a la izquierda los
nombres en un campo de 20 caracteres y alinea a la derecha las horas en un campo de 5 caracteres. Tenga en
cuenta que la cadena de formato estándar "N1" también se usa para dar formato a las horas con un dígito
fraccionario.
using System;
Tipos de fecha y hora (DateTime, DateTimeOffset) Cadenas con formato de fecha y hora estándar
Tipos numéricos (BigInteger, Byte, Decimal, Double, Int16, Cadenas con formato numérico estándar
Int32, Int64, SByte, Single, UInt16, UInt32, UInt64)
Cadenas con formato numérico personalizado
Guid Guid.ToString(String)
T IP O O C AT EGO RÍA DE T IP O VEA
Llaves de escape
Las llaves de apertura y de cierre se interpretan como el inicio y el final de un elemento de formato. Por lo
tanto, debe utilizar una secuencia de escape para que se muestre una llave de apertura o de cierre literal.
Especifique dos llaves de apertura ("{{") en el texto fijo para que se muestre una llave de apertura ("{"), o dos
llaves de cierre ("}}") para que se muestre una llave de cierre ("}"). Las llaves de un elemento de formato se
interpretan secuencialmente, en el orden en que se encuentran. No se admite la interpretación de llaves
anidadas.
El modo de interpretar las llaves de escape puede dar lugar a resultados inesperados. Tomemos como
ejemplo el elemento de formato "{{{0:D}}}", cuyo propósito es mostrar una llave de apertura, un valor
numérico con formato de número decimal y una llave de cierre; pero que, en la práctica, se interpreta de la
siguiente forma:
1. Las dos primeras llaves de apertura ("{{") son llaves de escape y dan lugar a en una llave de apertura.
2. Los tres caracteres siguientes ("{0:") se interpretan como el inicio de un elemento de formato.
3. El siguiente carácter ("D") se interpretaría como el especificador de formato numérico estándar
decimal, pero las dos llaves de escape siguientes ("}}") dan lugar a una única llave. Como la cadena
resultante ("D}") no es un especificador de formato numérico estándar, se interpreta como una cadena
de formato personalizado que significa que debe mostrarse la cadena literal "D}".
4. La última llave ("}") se interpreta como el final del elemento de formato.
5. Como resultado final, se muestra la cadena literal "{D}". No se muestra el valor numérico al que se
debía dar formato.
Una forma de escribir código e impedir que las llaves de escape y los elementos de formato se
malinterpreten consiste en dar formato a las llaves y elementos de formato por separado. Es decir, en la
primera operación de formato mostrar una llave de apertura literal, en la siguiente operación mostrar el
resultado del elemento de formato y, por último, en la operación final mostrar una llave de cierre literal. En el
ejemplo siguiente se muestra este enfoque.
Orden de procesamiento
Si la llamada al método de formato compuesto incluye un argumento IFormatProvider cuyo valor no es
null , el runtime llama al método IFormatProvider.GetFormat para solicitar una implementación de
ICustomFormatter. Si el método es capaz de devolver una implementación de ICustomFormatter, se
almacena en caché el tiempo que dure la llamada de método de formato compuesto.
Cada valor de la lista de parámetros que se corresponda con un elemento de formato se convierte en una
cadena del siguiente modo:
1. Si el valor al que se va a dar formato es null , se devuelve una cadena vacía String.Empty.
2. Si hay disponible una implementación de ICustomFormatter, el runtime llama al método Format. Pasa
al método el valor formatString del elemento de formato, si hay alguno, o null si no lo hay, junto con
la implementación de IFormatProvider. Si la llamada al método ICustomFormatter.Format devuelve
null , la ejecución avanza al siguiente paso; en caso contrario, se devuelve el resultado de la llamada
a ICustomFormatter.Format.
3. Si el valor implementa la interfaz IFormattable, se llama al método ToString(String, IFormatProvider)
de esta. Se pasa al método el valor formatString, si hubiera uno presente en el elemento de formato, o
null si no lo hubiera. El argumento IFormatProvider se determina de la siguiente forma:
Ejemplos de código
En el ejemplo siguiente se muestra una cadena creada mediante formato compuesto y otra creada mediante
el método ToString de un objeto. Los dos tipos de formato producen resultados equivalentes.
Si tomamos como día actual un jueves del mes de mayo, el valor de ambas cadenas del ejemplo anterior
será Thursday May para la referencia cultural Inglés (Estados Unidos).
Console.WriteLine expone la misma funcionalidad que String.Format. La única diferencia que existe entre
estos dos métodos es que String.Format devuelve el resultado como una cadena, mientras que
Console.WriteLine escribe el resultado en el flujo de salida asociado al objeto Console. En el ejemplo
siguiente se usa el método Console.WriteLine para dar formato al valor de MyInt como un valor de divisa.
En el ejemplo siguiente se muestra el uso de la alineación en la aplicación de formato. Los argumentos a los
que se da formato se colocan entre caracteres verticales (|) para resaltar la alineación resultante.
string myFName = "Fred";
string myLName = "Opals";
int myInt = 100;
string FormatFName = String.Format("First Name = |{0,10}|", myFName);
string FormatLName = String.Format("Last Name = |{0,10}|", myLName);
string FormatPrice = String.Format("Price = |{0,10:C}|", myInt);
Console.WriteLine(FormatFName);
Console.WriteLine(FormatLName);
Console.WriteLine(FormatPrice);
Console.WriteLine();
Vea también
WriteLine
String.Format
Interpolación de cadenas en C#
Cadenas interpoladas (referencia de Visual Basic)
Aplicación de formato a tipos
Cadenas con formato numérico estándar
Cadenas con formato numérico personalizado
Cadenas con formato de fecha y hora estándar
Cadenas con formato de fecha y hora personalizado
Cadenas de formato TimeSpan estándar
Cadenas de formato TimeSpan personalizado
Cadenas de formato de enumeración
Procedimiento para rellenar un número con ceros a
la izquierda
16/09/2020 • 10 minutes to read • Edit Online
Si quiere agregar ceros a la izquierda de un entero, puede hacerlo mediante la cadena de formato numérico
estándar "D" con un especificador de precisión. Para agregar ceros a la izquierda tanto de enteros como de
números de punto flotante, use una cadena de formato numérico personalizada. En este artículo se explica cómo
usar ambos métodos para rellenar un número con ceros a la izquierda.
4. Agregue el número de ceros a la izquierda que desea incluir en la cadena con formato a la longitud de la
cadena numérica sin rellenar. Al agregar el número de ceros iniciales se define la longitud total de la cadena
rellenada.
5. Llame al método ToString(String) del valor entero y pase la cadena "Dn" para cadenas decimales y "Xn"
para cadenas hexadecimales, donde n representa la longitud total de la cadena rellenada. También puede
usar la cadena de formato "Dn" o "Xn" en un método que admita formato compuesto.
En el ejemplo siguiente se rellena un valor entero con cinco ceros a la izquierda.
int value = 160934;
int decimalLength = value.ToString("D").Length + 5;
int hexLength = value.ToString("X").Length + 5;
Console.WriteLine(value.ToString("D" + decimalLength.ToString()));
Console.WriteLine(value.ToString("X" + hexLength.ToString()));
// The example displays the following output:
// 00000160934
// 00000274A6
if (dblValue.ToString().Contains(decSeparator))
{
int digits = dblValue.ToString().IndexOf(decSeparator);
fmt = new String('0', 5) + new String('#', digits) + ".##";
}
else
{
fmt = new String('0', dblValue.ToString().Length);
}
formatString = "{0,20:" + fmt + "}";
Console.WriteLine(dblValue.ToString(fmt));
Console.WriteLine(formatString, dblValue);
}
// The example displays the following output:
// 000009034521202.93
// 000009034521202.93
// 9034521202
// 9034521202
Dim dblValues() As Double = {9034521202.93217412, 9034521202}
For Each dblValue As Double In dblValues
Dim decSeparator As String = System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator
Dim fmt, formatString As String
If dblValue.ToString.Contains(decSeparator) Then
Dim digits As Integer = dblValue.ToString().IndexOf(decSeparator)
fmt = New String("0"c, 5) + New String("#"c, digits) + ".##"
Else
fmt = New String("0"c, dblValue.ToString.Length)
End If
formatString = "{0,20:" + fmt + "}"
Console.WriteLine(dblValue.ToString(fmt))
Console.WriteLine(formatString, dblValue)
Next
' The example displays the following output:
' 000009034521202.93
' 000009034521202.93
' 9034521202
' 9034521202
Vea también
Cadenas con formato numérico personalizado
Cadenas con formato numérico estándar
Formatos compuestos
Procedimiento para extraer el día de la semana de
una fecha concreta
16/09/2020 • 12 minutes to read • Edit Online
.NET Framework permite determinar fácilmente el número de día de la semana correspondiente a una fecha
concreta, así como mostrar el nombre localizado del día de la semana para una fecha en particular. Las propiedades
DayOfWeek o DayOfWeek proporcionan un valor enumerado que indica el día de la semana correspondiente a una
fecha concreta. Por el contrario, la recuperación del nombre del día de la semana es una operación de formato que
puede realizarse con una llamada a un método de formato como, por ejemplo, un método ToString de un valor de
fecha y hora o un método String.Format. En este tema se muestra cómo realizar estas operaciones de formato.
Para obtener un número que indique el día de la semana a partir de una fecha específica
1. Cuando trabaje con la representación de cadena de una fecha, conviértala en un valor DateTime o
DateTimeOffset mediante el método estático DateTime.Parse o DateTimeOffset.Parse.
2. Use las propiedades DateTime.DayOfWeek o DateTimeOffset.DayOfWeek para recuperar un valor
DayOfWeek que indique el día de la semana.
3. En caso necesario, convierta (en C# o en Visual Basic) el valor DayOfWeek en un entero.
En el ejemplo siguiente se muestra un entero que representa el día de la semana de una fecha concreta.
using System;
Module Example
Public Sub Main()
Dim dateValue As Date = #6/11/2008#
Console.WriteLine(dateValue.DayOfWeek)
End Sub
End Module
' The example displays the following output:
' 3
Para obtener el nombre abreviado del día de la semana a partir de una fecha específica
1. Cuando trabaje con la representación de cadena de una fecha, conviértala en un valor DateTime o
DateTimeOffset mediante el método estático DateTime.Parse o DateTimeOffset.Parse.
2. Puede obtener el nombre abreviado de un día de la semana de la referencia cultural actual o de una
referencia cultural específica:
a. Para obtener el nombre abreviado del día de la semana de la referencia cultural actual, llame al
método de instancia DateTime.ToString(String) o DateTimeOffset.ToString(String) del valor de fecha y
hora, y pase la cadena "ddd" como parámetro format . En el ejemplo siguiente se muestra la llamada
al método ToString(String).
using System;
Module Example
Public Sub Main()
Dim dateValue As Date = #6/11/2008#
Console.WriteLine(dateValue.ToString("ddd"))
End Sub
End Module
' The example displays the following output:
' Wed
b. Para obtener el nombre abreviado del día de la semana de una referencia cultural específica, llame al
método de instancia DateTime.ToString(String, IFormatProvider) o DateTimeOffset.ToString(String,
IFormatProvider) del valor de fecha y hora. Pase la cadena "ddd" como parámetro format . Pase un
objeto CultureInfo o DateTimeFormatInfo que represente la referencia cultural cuyo nombre del día
de la semana desee recuperar como parámetro provider . En el código siguiente se muestra una
llamada al método ToString(String, IFormatProvider) con un objeto CultureInfo que representa la
referencia cultural fr-FR.
using System;
using System.Globalization;
Imports System.Globalization
Module Example
Public Sub Main()
Dim dateValue As Date = #6/11/2008#
Console.WriteLine(dateValue.ToString("ddd",
New CultureInfo("fr-FR")))
End Sub
End Module
' The example displays the following output:
' mer.
Para obtener el nombre completo del día de la semana a partir de una fecha específica
1. Cuando trabaje con la representación de cadena de una fecha, conviértala en un valor DateTime o
DateTimeOffset mediante el método estático DateTime.Parse o DateTimeOffset.Parse.
2. Puede obtener el nombre completo de un día de la semana de la referencia cultural actual o de una
referencia cultural específica:
a. Para obtener el nombre del día de la semana de la referencia cultural actual, llame al método de
instancia DateTime.ToString(String) o DateTimeOffset.ToString(String) del valor de fecha y hora, y pase
la cadena "dddd" como parámetro format . En el ejemplo siguiente se muestra la llamada al método
ToString(String).
using System;
Module Example
Public Sub Main()
Dim dateValue As Date = #6/11/2008#
Console.WriteLine(dateValue.ToString("dddd"))
End Sub
End Module
' The example displays the following output:
' Wednesday
b. Para obtener el nombre del día de la semana de una referencia cultural específica, llame al método de
instancia DateTime.ToString(String, IFormatProvider) o DateTimeOffset.ToString(String,
IFormatProvider) del valor de fecha y hora. Pase la cadena "dddd" como parámetro format . Pase un
objeto CultureInfo o DateTimeFormatInfo que represente la referencia cultural cuyo nombre del día
de la semana desee recuperar como parámetro provider . En el código siguiente se muestra una
llamada al método ToString(String, IFormatProvider) con un objeto CultureInfo que representa la
referencia cultural es-ES.
using System;
using System.Globalization;
Module Example
Public Sub Main()
Dim dateValue As Date = #6/11/2008#
Console.WriteLine(dateValue.ToString("dddd", _
New CultureInfo("es-ES")))
End Sub
End Module
' The example displays the following output:
' miércoles.
Ejemplo
En el ejemplo se muestran llamadas a las propiedades DateTime.DayOfWeek y DateTimeOffset.DayOfWeek y los
métodos DateTime.ToString y DateTimeOffset.ToString para recuperar el número que representa el día de la
semana, el nombre abreviado del día de la semana y el nombre completo del día de la semana para una fecha en
particular.
using System;
using System.Globalization;
try
{
DateTimeFormatInfo dateTimeFormats;
// Convert date representation to a date value
dateValue = DateTime.Parse(dateString, CultureInfo.InvariantCulture);
dateOffsetValue = new DateTimeOffset(dateValue,
TimeZoneInfo.Local.GetUtcOffset(dateValue));
Module Example
Public Sub Main()
Dim dateString As String = "6/11/2007"
Dim dateValue As Date
Dim dateOffsetValue As DateTimeOffset
Try
Dim dateTimeFormats As DateTimeFormatInfo
' Convert date representation to a date value
dateValue = Date.Parse(dateString, CultureInfo.InvariantCulture)
dateOffsetValue = New DateTimeOffset(dateValue, _
TimeZoneInfo.Local.GetUtcOffset(dateValue))
' Convert date representation to a number indicating the day of week
Console.WriteLine(dateValue.DayOfWeek)
Console.WriteLine(dateOffsetValue.DayOfWeek)
Imports System.Globalization
Imports System.Threading
Module Example
Public Sub Main()
Dim dateValue As Date = #6/11/2008#
También se puede usar el valor devuelto por la propiedad DateTime.DayOfWeek para recuperar el nombre del día
de la semana de una fecha en particular. Para ello, solo se necesita hacer una llamada al método ToString en el valor
DayOfWeek devuelto por la propiedad. Sin embargo, esta técnica no produce el nombre localizado de un día de la
semana para la referencia cultural actual, tal como se muestra en el ejemplo siguiente.
using System;
using System.Globalization;
using System.Threading;
Imports System.Globalization
Imports System.Threading
Module Example
Public Sub Main()
' Change current culture to fr-FR
Dim originalCulture As CultureInfo = Thread.CurrentThread.CurrentCulture
Thread.CurrentThread.CurrentCulture = New CultureInfo("fr-FR")
Vea también
Cadenas con formato de fecha y hora estándar
Cadenas con formato de fecha y hora personalizado
Procedimiento para definir y usar proveedores de
formato numérico personalizado
16/09/2020 • 12 minutes to read • Edit Online
.NET Framework ofrece un amplio control sobre la representación de cadena de valores numéricos. Admite las
siguientes características para personalizar el formato de los valores numéricos:
Cadenas con formato numérico estándar, que proporcionan un conjunto predefinido de formatos para
convertir números en su representación de cadena. Se pueden usar con cualquier método de formato
numérico, como Decimal.ToString(String), que tiene un parámetro format . Para obtener detalles, vea
Cadenas con formato numérico estándar.
Cadenas con formato numérico personalizado, que proporcionan un conjunto de símbolos que pueden
combinarse para definir especificadores de formato numérico personalizado. Se pueden usar también con
cualquier método de formato numérico, como Decimal.ToString(String), que tiene un parámetro format .
Para obtener detalles, consulte Cadenas con formato numérico personalizado.
Objetos personalizados CultureInfo o NumberFormatInfo, que definen los símbolos y los modelos de
formato que se usan para mostrar las representaciones de cadena de valores numéricos. Se pueden usar con
cualquier método de formato numérico, como ToString, que tiene un parámetro provider . Normalmente, el
parámetro provider se usa para especificar el formato específico de la referencia cultural.
En algunos casos (por ejemplo, cuando una aplicación debe mostrar un número de cuenta con formato, un número
de identificación o un código postal) estas tres técnicas no resultan apropiadas. .NET Framework también permite
definir un objeto de formato que no es ni un objeto CultureInfo ni NumberFormatInfo para determinar cómo se
aplica formato a un valor numérico. En este tema se proporcionan instrucciones paso a paso para implementar este
tipo de objeto y se ofrece un ejemplo que da formato a números de teléfono.
Para definir un proveedor de formato personalizado
1. Defina una clase que implementa las interfaces IFormatProvider y ICustomFormatter.
2. Implemente el método IFormatProvider.GetFormat. GetFormat es un método de devolución de llamada que
el método de formato (como el método String.Format(IFormatProvider, String, Object[])) invoca para
recuperar el objeto realmente responsable del formato personalizado. Una implementación típica de
GetFormat hace lo siguiente:
a. Determina si el objeto Type pasado como un parámetro de método representa a una interfaz
ICustomFormatter.
b. Si el parámetro representa a la interfaz ICustomFormatter, GetFormat devuelve un objeto que
implementa la interfaz ICustomFormatter, que es responsable de proporcionar el formato
personalizado. Normalmente, el objeto de formato personalizado se devuelve a sí mismo.
c. Si el parámetro no representa la interfaz ICustomFormatter, GetFormat devuelve null .
3. Implemente el método Format. Este método es invocado por el método String.Format(IFormatProvider,
String, Object[]) y es responsable de devolver la representación de cadena de un número. La implementación
del método normalmente implica lo siguiente:
a. Opcionalmente, asegúrese de que el método se haya diseñado para proporcionar servicios de
formato al examinar el parámetro provider . En el caso de los objetos de formato que implementan
IFormatProvider y ICustomFormatter, esto implica probar la igualdad del parámetro provider y el
objeto de formato actual.
b. Determine si el objeto de formato debe admitir especificadores de formato personalizado. (Por
ejemplo, un especificador de formato "N" podría indicar que debe generarse un número de teléfono
de los Estados Unidos en formato NANP, mientras que una "I" podría indicar la salida en el formato de
la recomendación E.123 de ITU-T). Si se usan especificadores de formato, el método debe controlar el
especificador de formato específico. Se pasa al método en el parámetro format . Si no hay ningún
especificador, el valor del parámetro format es String.Empty.
c. Recupere el valor numérico pasado al método como parámetro arg . Realice todas las
manipulaciones necesarias para convertirlo en su representación de cadena.
d. Devuelva la representación de cadena del parámetro arg .
Para usar un objeto de formato numérico personalizado
1. Cree una nueva instancia de la clase de formato personalizado.
2. Llame al método de formato String.Format(IFormatProvider, String, Object[]) y pásele el objeto de formato
personalizado, el especificador de formato (o String.Empty si no se usa ninguno) y el valor numérico al que
se va a dar formato.
Ejemplo
En el ejemplo siguiente se define un proveedor de formato numérico personalizado denominado
TelephoneFormatter que convierte un número que representa un número de teléfono de los Estados Unidos en su
formato NANP o E.123. El método controla dos especificadores de formato, "N" (que genera el formato NANP) e "I"
(que genera el formato E.123 internacional).
using System;
using System.Globalization;
if (format == "N")
{
if (numericString.Length <= 4)
return numericString;
else if (numericString.Length == 7)
return numericString.Substring(0, 3) + "-" + numericString.Substring(3, 4);
else if (numericString.Length == 10)
return "(" + numericString.Substring(0, 3) + ") " +
numericString.Substring(3, 3) + "-" + numericString.Substring(6);
else
else
throw new FormatException(
string.Format("'{0}' cannot be used to format {1}.",
format, arg.ToString()));
}
else if (format == "I")
{
if (numericString.Length < 10)
throw new FormatException(string.Format("{0} does not have 10 digits.", arg.ToString()));
else
numericString = "+1 " + numericString.Substring(0, 3) + " " + numericString.Substring(3, 3) + " " +
numericString.Substring(6);
}
else
{
throw new FormatException(string.Format("The {0} format specifier is invalid.", format));
}
return numericString;
}
}
Pero también permite que se produzca la conversión si no hay ningún especificador de formato. En el ejemplo
siguiente se muestra una llamada al método así.
if (arg is IFormattable)
s = ((IFormattable)arg).ToString(format, formatProvider);
else if (arg != null)
s = arg.ToString();
En el caso de este ejemplo, el método que implementa ICustomFormatter.Format está diseñado para que actúe
como un método de devolución de llamada para el método String.Format(IFormatProvider, String, Object[]). Por lo
tanto, examina el parámetro formatProvider para determinar si contiene una referencia al objeto
TelephoneFormatter actual. Pero también se puede llamar al método directamente desde el código. En ese caso,
puede usar el parámetro formatProvider para proporcionar un objeto CultureInfo o NumberFormatInfo que
aporte información de formato específica de la referencia cultural.
Procedimiento para valores de fecha y hora de ida y
vuelta
16/09/2020 • 12 minutes to read • Edit Online
En muchas aplicaciones, un valor de fecha y hora sirve para identificar inequívocamente un único punto en el
tiempo. En este artículo se muestra cómo guardar y restaurar un valor DateTime, DateTimeOffset y de fecha y hora
con información sobre la zona horaria, de manera que el valor restaurado identifique la misma hora que el
guardado.
Cuando se usa un valor DateTime de ida y vuelta, esta técnica mantiene correctamente la hora para todas las horas
locales y universales. Por ejemplo, si un valor local DateTime se guarda en un sistema de la zona horaria estándar
del Pacífico de Estados Unidos y se restaura en un sistema de la zona horaria estándar central de Estados Unidos, la
fecha y hora restauradas serán dos horas posteriores a la hora original, lo que refleja la diferencia horaria entre las
dos zonas. Pero esta técnica no es necesariamente precisa para horas no especificadas. Todos los valores DateTime
cuya propiedad Kind es Unspecified se tratan como si fueran horas locales. Si no es una hora local, DateTime no
identificará correctamente el punto en el tiempo. La solución alternativa para esta limitación es acoplar
estrechamente un valor de fecha y hora con su zona horaria para la operación de guardado y restauración.
Esta técnica identifica siempre de forma inequívoca un valor DateTimeOffset como un único punto en el tiempo. El
valor puede convertirse entonces en la hora universal coordinada (UTC) al llamar al método
DateTimeOffset.ToUniversalTime, o bien puede convertirse en la hora de una zona horaria concreta al llamar a los
métodos DateTimeOffset.ToOffset o TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo). La limitación
principal de esta técnica está en que las operaciones aritméticas de fecha y hora, cuando se realizan en un valor
DateTimeOffset que representa la hora en una zona horaria determinada, podrían no generar resultados precisos
para esa zona horaria. Esto se debe a que, cuando se crea una instancia de un valor DateTimeOffset, se desasocia de
su zona horaria. Por lo tanto, ya no se pueden aplicar las reglas de ajuste de esa zona de horaria al realizar cálculos
de fecha y hora. Para evitar este problema, puede definir un tipo personalizado que incluya tanto el valor de fecha y
hora como la zona horaria correspondiente.
Acción de ida y vuelta para un valor de fecha y hora con su zona horaria
1. Defina una clase o una estructura con dos campos. El primer campo es un objeto DateTime o
DateTimeOffset y el segundo es un objeto TimeZoneInfo. El ejemplo siguiente es una versión sencilla de ese
tipo.
public DateInTimeZone() {}
this.thisDate = date;
this.tz = timeZone;
}
Esta técnica debe reflejar siempre de forma inequívoca el punto correcto de tiempo antes y después de guardar y
restaurar, siempre que la implementación del objeto combinado de fecha y hora y de zona horaria no permita que
el valor de fecha quede fuera de la sincronización con el valor de zona horaria.
Compilar el código
Para estos ejemplos se necesita lo siguiente:
Que los espacios de nombres siguientes se importen con directivas using de C# o con instrucciones
Imports de Visual Basic:
Vea también
Elección entre DateTime, DateTimeOffset, TimeSpan y TimeZoneInfo
Cadenas con formato de fecha y hora estándar
Procedimiento para mostrar milisegundos en los
valores de fecha y hora
16/09/2020 • 7 minutes to read • Edit Online
Los métodos de formato de fecha y hora predeterminados, como DateTime.ToString(), incluyen las horas, minutos y
segundos de un valor de tiempo, pero excluyen el componente correspondiente a los milisegundos. En este tema se
muestra cómo se incluye un componente de milisegundos de un valor de fecha y hora en cadenas de fecha y hora
con formato.
Para mostrar el componente de milisegundos de un valor DateTime
1. Cuando trabaje con la representación de cadena de una fecha, conviértala en un valor DateTime o
DateTimeOffset mediante el método estático DateTime.Parse(String) o DateTimeOffset.Parse(String).
2. Para extraer la representación de cadena del componente de milisegundos de una hora, llame al método
DateTime.ToString(String) o ToString del valor de fecha y hora y pase el modelo de formato personalizado
fff o FFF en solitario o junto a otros especificadores de formato personalizado como el parámetro
format .
Ejemplo
En el ejemplo se muestra el componente de milisegundos de un valor DateTime y DateTimeOffset en la consola en
su presentación en solitario e incluido en una cadena de fecha y hora más larga.
using System;
using System.Globalization;
using System.Text.RegularExpressions;
try
{
DateTime dateValue = DateTime.Parse(dateString);
DateTimeOffset dateOffsetValue = DateTimeOffset.Parse(dateString);
// Display Millisecond component with modified full date and time pattern.
Console.WriteLine("Modified full date time pattern: {0}",
dateValue.ToString(fullPattern));
Console.WriteLine("Modified full date time pattern: {0}",
dateOffsetValue.ToString(fullPattern));
}
catch (FormatException)
{
Console.WriteLine("Unable to convert {0} to a date.", dateString);
}
}
}
// The example displays the following output if the current culture is en-US:
// Millisecond component only: 126
// Millisecond component only: 126
// Date and Time with Milliseconds: 07/16/2008 08:32:45.126 AM
// Date and Time with Milliseconds: 07/16/2008 08:32:45.126 AM
// Modified full date time pattern: Wednesday, July 16, 2008 8:32:45.126 AM
// Modified full date time pattern: Wednesday, July 16, 2008 8:32:45.126 AM
Imports System.Globalization
Imports System.Text.REgularExpressions
Module MillisecondDisplay
Public Sub Main()
Try
Dim dateValue As Date = Date.Parse(dateString)
Dim dateOffsetValue As DateTimeOffset = DateTimeOffset.Parse(dateString)
' Append millisecond pattern to current culture's full date time pattern
Dim fullPattern As String = DateTimeFormatInfo.CurrentInfo.FullDateTimePattern
fullPattern = Regex.Replace(fullPattern, "(:ss|:s)", "$1.fff")
' Display Millisecond component with modified full date and time pattern.
Console.WriteLine("Modified full date time pattern: {0}", _
dateValue.ToString(fullPattern))
Console.WriteLine("Modified full date time pattern: {0}", _
dateOffsetValue.ToString(fullPattern))
Catch e As FormatException
Console.WriteLine("Unable to convert {0} to a date.", dateString)
End Try
End Sub
End Module
' The example displays the following output if the current culture is en-US:
' Millisecond component only: 126
' Millisecond component only: 126
' Date and Time with Milliseconds: 07/16/2008 08:32:45.126 AM
' Date and Time with Milliseconds: 07/16/2008 08:32:45.126 AM
' Modified full date time pattern: Wednesday, July 16, 2008 8:32:45.126 AM
' Modified full date time pattern: Wednesday, July 16, 2008 8:32:45.126 AM
El modelo de formato fff incluye todos los ceros finales en el valor de milisegundos. El modelo de formato FFF
suprime todos estos ceros. En el siguiente ejemplo se ilustra la diferencia.
NOTE
Es posible mostrar unidades fraccionarias de segundo muy pequeñas, como diezmilésimas o cienmilésimas de segundo. Sin
embargo, estos valores no suelen ser significativos. La precisión de los valores de fecha y hora depende de la resolución del
reloj del sistema. En los sistemas operativos Windows NT 3.5 (y versiones posteriores) y Windows Vista, la resolución del reloj
es aproximadamente de 10 a 15 milisegundos.
Vea también
DateTimeFormatInfo
Cadenas con formato de fecha y hora personalizado
Procedimiento para mostrar fechas en calendarios no
gregorianos
16/09/2020 • 13 minutes to read • Edit Online
Los tipos DateTime y DateTimeOffset usan el calendario gregoriano como calendario predeterminado. Esto
significa que al llamar al método ToString de un valor de fecha y hora se muestra la representación de cadena de
esa fecha y hora en el calendario gregoriano, aunque se creara con otro calendario. Esto se muestra en el ejemplo
siguiente, que usa dos maneras diferentes de crear un valor de fecha y hora con el calendario persa, pero muestra
esos valores de fecha y hora en el calendario gregoriano cuando llama al método ToString. En este ejemplo se
reflejan dos técnicas usadas habitualmente, aunque incorrectas, para mostrar la fecha en un calendario
determinado.
Se pueden usar dos técnicas distintas para mostrar la fecha en un calendario determinado. La primera exige que el
calendario sea el predeterminado de una referencia cultural determinada. La segunda se puede usar con cualquier
calendario.
Para mostrar la fecha del calendario predeterminado de una referencia cultural
1. Cree una instancia de un objeto de calendario derivado de la clase Calendar que representa al calendario
que se va a usar.
2. Cree una instancia de un objeto CultureInfo que representa la referencia cultural cuyo formato se va a usar
para mostrar la fecha.
3. Llame al método Array.Exists para determinar si el objeto de calendario es miembro de la matriz devuelta
por la propiedad CultureInfo.OptionalCalendars. Esto indica que el calendario puede actuar como calendario
predeterminado del objeto CultureInfo. Si no es miembro de la matriz, siga las instrucciones de la sección
"Para mostrar la fecha en cualquier calendario".
4. Asigne el objeto de calendario a la propiedad Calendar del objeto DateTimeFormatInfo devuelto por la
propiedad CultureInfo.DateTimeFormat.
NOTE
La clase CultureInfo también tiene una propiedad Calendar. Pero es de solo lectura y constante; no cambia para
reflejar el nuevo calendario predeterminado asignado a la propiedad DateTimeFormatInfo.Calendar.
5. Llame al método ToString o ToString y pásele el objeto CultureInfo cuyo calendario predeterminado se
modificó en el paso anterior.
Para mostrar la fecha en cualquier calendario
1. Cree una instancia de un objeto de calendario derivado de la clase Calendar que representa al calendario
que se va a usar.
2. Determine qué elementos de fecha y hora deben aparecer en la representación de cadena del valor de fecha
y hora.
3. Para cada elemento de fecha y hora que quiera mostrar, llame al método Get del objeto de calendario... .
Están disponibles los siguientes métodos:
GetYear, para mostrar el año en el calendario adecuado.
GetMonth, para mostrar el mes en el calendario adecuado.
GetDayOfMonth, para mostrar el número del día del mes en el calendario adecuado.
GetHour, para mostrar la hora del día en el calendario adecuado.
GetMinute, para mostrar los minutos de la hora en el calendario adecuado.
GetSecond, para mostrar los segundos del minuto en el calendario adecuado.
GetMilliseconds, para mostrar los milisegundos del segundo en el calendario adecuado.
Ejemplo
En el ejemplo se muestra una fecha con dos calendarios diferentes. Se muestra la fecha después de definir el
calendario Hijri como calendario predeterminado de la referencia cultural ar-JO y se muestra la fecha con el
calendario persa, que no se admite como calendario opcional de la referencia cultural fa-IR.
using System;
using System.Globalization;
Console.WriteLine();
if (this.CalendarExists(culture))
{
Console.WriteLine("Displaying date in supported {0} calendar...",
this.thisCalendar.GetType().Name);
culture.DateTimeFormat.Calendar = this.thisCalendar;
return dateToDisplay.ToString(specifier, culture);
}
else
{
Console.WriteLine("Displaying date in unsupported {0} calendar...",
thisCalendar.GetType().Name);
string separator = targetCulture.DateTimeFormat.DateSeparator;
return thisCalendar.GetYear(dateToDisplay.DateTime).ToString("0000") +
separator +
thisCalendar.GetMonth(dateToDisplay.DateTime).ToString("00") +
separator +
thisCalendar.GetDayOfMonth(dateToDisplay.DateTime).ToString("00");
}
}
}
// The example displays the following output to the console:
// Using the system default culture: 7/3/2008
// Using the ar-JO culture's original default calendar: 03/07/2008
// Using the ar-JO culture with Hijri as the default calendar:
// Displaying date in supported HijriCalendar calendar...
// 1429/06/29
// Displaying date in supported HijriCalendar calendar...
// 1429/06/29
//
// Using the ir-FA culture's default calendar: 7/3/2008
// Displaying date in unsupported PersianCalendar calendar...
// 1387/04/13
// Displaying date in unsupported PersianCalendar calendar...
// 1387/04/13
Imports System.Globalization
Console.WriteLine()
' Display the date using the ir-FA culture's default calendar.
Console.WriteLine("Using the ir-FA culture's default calendar: {0}", _
dateValue1.ToString("d", ic))
' Display a Date value.
Console.WriteLine(persianUtil.DisplayDate(dateValue1, ic))
' Display a DateTimeOffset value.
Console.WriteLine(persianUtil.DisplayDate(dateValue2, ic))
End Sub
End Class
If Me.CalendarExists(culture) Then
Console.WriteLine("Displaying date in supported {0} calendar...", _
thisCalendar.GetType().Name)
culture.DateTimeFormat.Calendar = Me.thisCalendar
Return dateToDisplay.ToString(specifier, culture)
Else
Console.WriteLine("Displaying date in unsupported {0} calendar...", _
thisCalendar.GetType().Name)
Cada objeto CultureInfo puede admitir uno o varios calendarios, que se indican mediante la propiedad
OptionalCalendars. Uno de ellos se designa como calendario predeterminado de la referencia cultural y es devuelto
por la propiedad de solo lectura CultureInfo.Calendar. Otro de los calendarios opcionales se puede designar como
valor predeterminado si se asigna un objeto Calendar que represente ese calendario a la propiedad
DateTimeFormatInfo.Calendar devuelta por la propiedad CultureInfo.DateTimeFormat. Pero algunos calendarios,
como el persa representado por la clase PersianCalendar, no actúan como calendarios opcionales de ninguna
referencia cultural.
En el ejemplo se define una clase de utilidad de calendario reutilizable, CalendarUtility , para controlar muchos de
los detalles de generación de la representación de cadena de una fecha mediante un calendario determinado. La
clase CalendarUtility tiene los siguientes miembros:
Un constructor parametrizado cuyo único parámetro es un objeto Calendar en el que se va a representar
una fecha. Se asigna a un campo privado de la clase.
CalendarExists , un método privado que devuelve un valor booleano que indica si el calendario
representado por el objeto CalendarUtility es compatible con el objeto CultureInfo pasado al método
como parámetro. El método encapsula una llamada al método Array.Exists, al que pasa la matriz
CultureInfo.OptionalCalendars.
HasSameName , un método privado asignado al delegado Predicate<T> que se pasa como parámetro al
método Array.Exists. Cada miembro de la matriz se pasa al método hasta que este devuelve true . El método
determina si el nombre de un calendario opcional es igual que el calendario representado por el objeto
CalendarUtility .
DisplayDate , un método público sobrecargado al que se pasan dos parámetros: un valor DateTime o
DateTimeOffset para expresar en el calendario representado por el objeto CalendarUtility ; y la referencia
cultural cuyas reglas de formato se van a usar. Su comportamiento a la hora de devolver la representación
de cadena de una fecha depende de si la referencia cultural cuyas reglas de formato se van a usar admite el
calendario de destino.
Independientemente del calendario usado para crear un valor DateTime o DateTimeOffset en este ejemplo, ese
valor normalmente se expresa como una fecha gregoriana. Esto se debe a que los tipos DateTime y DateTimeOffset
no conservan ninguna información del calendario. Internamente, se representan como el número de tics
transcurridos desde la medianoche del 1 de enero de 0001. La interpretación de ese número depende del
calendario. En la mayoría de las referencias culturales, el calendario predeterminado es el gregoriano.
Codificación de caracteres de .NET
16/09/2020 • 31 minutes to read • Edit Online
En este artículo se proporciona una introducción a los sistemas de codificación de character que se usan con .NET.
Se explica cómo funcionan los tipos String, Char, Rune y StringInfo con Unicode, UTF-16 y UTF-8.
El término character se usa aquí en el sentido general de lo que un lector percibe como un solo elemento de
presentación. Algunos ejemplos comunes son la letra "a", el símbolo "@" y el emoji " ". A veces, lo que parece un
charcter consta en realidad de varios elementos de visualización independientes, como se explica en la sección
sobre los clústeres de grafemas.
PrintChars("Hello");
"Hello".Length = 5
s[0] = 'H' ('\u0048')
s[1] = 'e' ('\u0065')
s[2] = 'l' ('\u006c')
s[3] = 'l' ('\u006c')
s[4] = 'o' ('\u006f')
Cada char acter se representa mediante un solo valor de char. Ese patrón se aplica a la mayoría de los idiomas del
mundo. Por ejemplo, esta es la salida de dos acter chinos que suenan como charnǐ hǎo y significan Hola:
PrintChars("你好");
"你好".Length = 2
s[0] = '你' ('\u4f60')
s[1] = '好' ('\u597d')
Pero en algunos idiomas, símbolos y emoji, se necesitan dos instancias de char para representar un único
character. Por ejemplo, compare los acter y las instancias de char de la palabra que significa char Osage en el
idioma osage:
PrintChars(" ");
" ".Length = 17
s[0] = '�' ('\ud801')
s[1] = '�' ('\udccf')
s[2] = '�' ('\ud801')
s[3] = '�' ('\udcd8')
s[4] = '�' ('\ud801')
s[5] = '�' ('\udcfb')
s[6] = '�' ('\ud801')
s[7] = '�' ('\udcd8')
s[8] = '�' ('\ud801')
s[9] = '�' ('\udcfb')
s[10] = '�' ('\ud801')
s[11] = '�' ('\udcdf')
s[12] = ' ' ('\u0020')
s[13] = '�' ('\ud801')
s[14] = '�' ('\udcbb')
s[15] = '�' ('\ud801')
s[16] = '�' ('\udcdf')
En el ejemplo anterior, cada character excepto el espacio se representa mediante dos instancias de char .
Un solo emoji Unicode se representa mediante dos instancias de char , tal como se ilustra en el ejemplo siguiente,
que muestra un emoji de buey:
" ".Length = 2
s[0] = '�' ('\ud83d')
s[1] = '�' ('\udc02')
En estos ejemplos se muestra que el valor de string.Length , que indica el número de instancias de char , no
indica necesariamente el número de character mostrados. Una sola instancia de char no representa
necesariamente un character.
Los pares de char que se asignan a un único acter se denominan charpares suplentes. Para entender cómo
funcionan, debe comprender la codificación Unicode y UTF-16.
Normalmente se hace referencia a los puntos de código mediante la sintaxis U+xxxx , donde xxxx es el valor
entero con codificación hexadecimal.
Dentro del intervalo completo de puntos de código hay dos subintervalos:
El plano básico multilingüe (BMP) en el intervalo U+0000..U+FFFF . Este intervalo de 16 bits proporciona
65 536 puntos de código, suficientes para cubrir la mayoría de los sistemas de escritura del mundo.
Los puntos de código suplementarios en el intervalo U+10000..U+10FFFF . Este intervalo de 21 bits
proporciona más de un millón de puntos de código adicionales que se pueden usar con idiomas menos
conocidos y otros fines, como los emoji.
En el diagrama siguiente se ilustra la relación entre el BMP y los puntos de código suplementarios.
Pares suplentes
La conversión de dos valores de 16 bits en un único valor de 21 bits se facilita mediante un intervalo especial
denominado puntos de código suplentes, de U+D800 a U+DFFF (decimal 55.296 a 57.343), ambos incluidos.
En el diagrama siguiente se ilustra la relación entre el BMP y los puntos de código suplentes.
Cuando un punto de código suplente superior ( U+D800..U+DBFF ) va seguido inmediatamente por un punto de
código suplente inferior ( U+DC00..U+DFFF ), el par se interpreta como un punto de código suplementario mediante
la fórmula siguiente:
En el ejemplo anterior se muestra que "\ud83c\udf39" es la codificación UTF-16 del punto de código
U+1F339 ROSE (' ') mencionado anteriormente.
Clústeres de grafemas
Lo que parece un acter podría ser el resultado de una combinación de varios puntos de código, por lo que un
término más descriptivo que se usa a menudo en lugar de "acter" es charclúster de grafemaschar. El término
equivalente en .NET es elemento de texto.
Tenga en cuenta las instancias de string "a", "á", "á" y " ". Si el sistema operativo las trata como se especifica
en el estándar Unicode, cada una de estas instancias de string aparece como un solo elemento de texto o clúster
de grafemas. Sin embargo, las dos últimas están representadas por más de un punto de código de valor escalar.
La instancia "a" de string está representada por un valor escalar y contiene una instancia de char .
U+0061 LATIN SMALL LETTER A
La instancia "á" de string está representada por un valor escalar y contiene una instancia de char .
U+00E1 LATIN SMALL LETTER A WITH ACUTE
La instancia "á" de string parece igual que "á" pero está representada por dos valores escalares y contiene
dos instancias de char .
U+0061 LATIN SMALL LETTER A
U+0301 COMBINING ACUTE ACCENT
Por último, la instancia " " de string está representada por cuatro valores y contiene siete instancias de
char .
En algunos de los ejemplos anteriores, como el de la combinación del modificador del acento o el modificador del
tono de piel, el punto de código no se muestra como un elemento independiente en la pantalla, sino que sirve para
modificar el aspecto de un elemento de texto que venía antes. En estos ejemplos se muestra que puede tomar
varios valores escalares para componer lo que consideramos un solo "character" o un "clúster de grafemas".
Para enumerar los clústeres de grafemas de una instancia de string , use la clase StringInfo como se muestra en el
ejemplo siguiente. Si está familiarizado con Swift, el tipo StringInfo de .NET es conceptualmente similar al tipo
character de Swift.
UTF-8 y UTF-32
Las secciones anteriores se centraban en UTF-16 porque es lo que usa .NET para codificar instancias de string .
Existen otros sistemas de codificación para Unicode: UTF-8 y UTF-32. Estas codificaciones usan unidades de código
de 8 bits y unidades de código de 32 bits, respectivamente.
Al igual que UTF-16, UTF-8 requiere varias unidades de código para representar algunos valores escalares
Unicode. UTF-32 puede representar cualquier valor escalar en una sola unidad de código de 32 bits.
Estos son algunos ejemplos en los que se muestra cómo se representa el mismo punto de código Unicode en cada
uno de estos tres sistemas de codificación Unicode:
Como se indicó anteriormente, una única unidad de código UTF-16 de un par suplente no tiene sentido. Del mismo
modo, una sola unidad de código UTF-8 no tiene sentido si se encuentra en una secuencia de dos, tres o cuatro
usadas para calcular un valor escalar.
Modos endian
En .NET, las unidades de código UTF-16 de una instancia de string se almacenan en memoria contigua como una
secuencia de enteros de 16 bits (instancias de char ). Los bits de las unidades de código individuales se disponen
de acuerdo con el modo endian de la arquitectura actual.
En una arquitectura little-endian, la instancia de string que consta de los puntos de código UTF-16 [ D801 DCCC ]
se dispondría en memoria como los bytes [ 0x01, 0xD8, 0xCC, 0xDC ] . En una arquitectura big-endian, la misma
instancia de string se dispondría en memoria como los bytes [ 0xD8, 0x01, 0xDC, 0xCC ] .
Los sistemas informáticos que se comunican entre sí deben estar de acuerdo con la representación de los datos
que atraviesan la conexión. La mayoría de los protocolos de red usan UTF-8 como estándar al transmitir texto, en
parte para evitar problemas que podrían derivarse de una máquina big-endian que se comunica con una máquina
little-endian. La instancia de string que consta de los puntos de código UTF-8 [ F0 90 93 8C ] siempre se
representará como los bytes [ 0xF0, 0x90, 0x93, 0x8C ] con independencia del modo endian.
Para usar UTF-8 para transmitir texto, las aplicaciones .NET a menudo usan código como el del ejemplo siguiente:
WARNING
Dado que UTF-8 es habitual en Internet, puede resultar tentador leer los bytes sin formato de la conexión y tratar los datos
como si fueran UTF-8. Sin embargo, debe validar que el formato es correcto. Un cliente malintencionado podría enviar UTF-8
con un formato incorrecto a su servicio. Si trabaja en esos datos como si tuvieran el formato correcto, podría provocar
errores o vulnerabilidades de seguridad en la aplicación. Para validar los datos UTF-8, puede usar un método como
Encoding.UTF8.GetString , que realizará la validación mientras convierte los datos entrantes en una instancia de string .
Las API como Encoding.UTF8.GetString nunca devuelven instancias de string con un formato incorrecto. Los
métodos Encoding.GetString y Encoding.GetBytes detectan secuencias con un formato incorrecto en la entrada y
realizan la sustitución de caracteres zzchar al generar la salida. Por ejemplo, si Encoding.ASCII.GetString(byte[])
observa un byte no ASCII en la entrada (fuera del intervalo de U+0000..U+007F), inserta un "?" en la instancia de
string devuelta. Encoding.UTF8.GetString(byte[]) reemplaza las secuencias UTF-8 con un formato incorrecto por
U+FFFD REPLACEMENT CHARACTER ('�') en la instancia de string devuelta. Para más información, consulte el
estándar Unicode, secciones 5.22 y 3.9.
Las clases Encoding integradas también se pueden configurar para producir una excepción en lugar de realizar la
sustitución de caracteres zzchar cuando se observan secuencias con un formato incorrecto. Este enfoque se suele
usar en aplicaciones que dependen de la seguridad donde la sustitución de caracteres zzchar podría no ser
aceptable.
Para leer información sobre cómo usar las clases Encoding integradas, vea Uso de las clases de codificación de
caracteres en .NETzzchar.
Vea también
String
Char
Rune
Globalización y localización
Procedimiento para usar clases de codificación de
caracteres en .NET
16/09/2020 • 67 minutes to read • Edit Online
En este artículo se explica cómo usar las clases que proporciona .NET para codificar y descodificar texto mediante
varios esquemas de codificación. En las instrucciones se da por hecho que ha leído Introducción a la codificación
de caracteres en .NET.
Codificadores y descodificadores
.NET proporciona clases de codificación que codifican y descodifican texto mediante varios sistemas de
codificación. Por ejemplo, la clase UTF8Encoding describe las reglas de codificación y descodificación de UTF-8.
.NET usa la codificación UTF-16 (representada por la clase UnicodeEncoding) con las instancias de string . Existen
codificadores y descodificadores para otros esquemas de codificación.
La codificación y la descodificación también pueden incluir validación. Por ejemplo, la clase UnicodeEncoding
comprueba todas las instancias de char en el intervalo suplente para asegurarse de que están en pares suplentes
válidos. Una estrategia de reserva determina cómo trata un codificador los caracteres no válidos o cómo trata un
descodificador los bytes no válidos.
WARNING
Las clases de codificación de .NET proporcionan una manera de almacenar y convertir datos de caracteres. No se deben usar
para almacenar datos binarios en formato de cadena. Dependiendo de la codificación empleada, la conversión de datos
binarios al formato de cadena con las clases de codificación puede presentar un comportamiento inesperado y mostrar datos
inexactos o dañados. Para convertir datos binarios en un formato de cadena, use el método Convert.ToBase64String .
Todas las clases de codificación de caracteres de .NET heredan de la clase System.Text.Encoding, que es una clase
abstracta que define la funcionalidad común a todas las codificaciones de caracteres. Para acceder a los objetos
individuales de codificación implementados en .NET, haga lo siguiente:
Use las propiedades estáticas de la clase Encoding, que devuelven objetos que representan las
codificaciones de caracteres estándar disponibles en .NET (ASCII, UTF-7, UTF-8, UTF-16 y UTF-32). Por
ejemplo, la propiedad Encoding.Unicode devuelve un objeto UnicodeEncoding : Cada objeto usa la reserva
de reemplazo para controlar las cadenas que no puede codificar y los bytes que no puede descodificar. Para
más información, consulte Reserva de reemplazo.
Llame al constructor de clase de la codificación. Se pueden crear instancias de los objetos para las
codificaciones ASCII, UTF-7, UTF-8, UTF-16 y UTF-32 de esta manera. De forma predeterminada, cada objeto
usa la reserva de reemplazo para controlar las cadenas que no puede codificar y los bytes que no puede
descodificar, pero puede especificar que se debe producir una excepción en su lugar. Para más información,
consulte Reserva de reemplazo y Reserva de excepción.
Llame al constructor Encoding(Int32) y pásele un entero que represente la codificación. Los objetos de
codificación estándar usan la reserva de reemplazo, y los objetos de codificación para la página de códigos
y el juego de caracteres de doble byte (DBCS) usan el retroceso de ajuste perfecto para controlar las
cadenas que no pueden codificar y los bytes que no pueden descodificar. Para más información, consulte
Reserva con ajuste perfecto.
Llame al método Encoding.GetEncoding, que devuelve cualquier estándar, página de códigos o codificación
DBCS disponible en .NET. Las sobrecargas permiten especificar un objeto de reserva para el codificador y
para el descodificador.
Se puede recuperar información sobre todas las codificaciones disponibles en .NET llamando al método
Encoding.GetEncodings. .NET admite los esquemas de codificación de caracteres que se muestran en la tabla
siguiente.
Codificaciones de juegos de caracteres de doble byte (DBCS) Admite idiomas que contienen más de 256 caracteres, como
el chino, el japonés y el coreano. En un DBCS, un par de
puntos de código (un byte doble) representa cada carácter. La
propiedad Encoding.IsSingleByte devuelve false para las
codificaciones DBCS. Puede recuperar un objeto de
codificación para un DBCS determinado llamando al método
Encoding.GetEncoding(Int32) . Cuando una aplicación controla
datos DBCS, el primer byte de un carácter DBCS (el byte
inicial) se procesa junto con el byte final que le sigue
inmediatamente. Puesto que un único par de puntos de
código de doble byte puede representar caracteres diferentes
dependiendo de la página de códigos, este esquema aún no
permite la combinación de dos idioma, como el japonés y el
chino, en el mismo flujo de datos.
Estas codificaciones permiten trabajar con caracteres Unicode, así como con codificaciones que son las más usadas
en aplicaciones heredadas. Además, puede crear una codificación personalizada definiendo una clase que se deriva
de Encoding e invalidar sus miembros.
Console.WriteLine("Strings to encode:");
foreach (var stringValue in strings) {
Console.WriteLine(" {0}", stringValue);
Module Example
Public Sub Main()
Dim strings() As String = {"This is the first sentence. ",
"This is the second sentence. "}
Dim asciiEncoding As Encoding = Encoding.ASCII
Console.WriteLine("Strings to encode:")
For Each stringValue In strings
Console.WriteLine(" {0}", stringValue)
Un descodificador convierte una matriz de bytes que refleja una codificación de caracteres determinada en un
juego de caracteres, ya sea en una matriz de caracteres o en una cadena. Para descodificar una matriz de bytes en
una matriz de caracteres, se llama al método Encoding.GetChars . Para descodificar una matriz de bytes en una
cadena, se llama al método GetString . Si desea determinar cuántos caracteres son necesarios para almacenar los
bytes descodificados antes de realizar la descodificación, puede llamar al método GetCharCount .
En el ejemplo siguiente se codifican tres cadenas y después se descodifican en una sola matriz de caracteres. Se
mantiene un índice que indica la posición inicial de la matriz de caracteres para el siguiente juego de caracteres
descodificados. Se llama al método GetCharCount para asegurarse de que la matriz de caracteres es
suficientemente grande para alojar todos los caracteres descodificados. A continuación, se llama al método
ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32) para descodificar la matriz de bytes.
using System;
using System.Text;
Module Example
Public Sub Main()
Dim strings() As String = {"This is the first sentence. ",
"This is the second sentence. ",
"This is the third sentence. "}
Dim asciiEncoding As Encoding = Encoding.ASCII
' Array to hold encoded bytes.
Dim bytes() As Byte
' Array to hold decoded characters.
Dim chars(50) As Char
' Create index for current position of character array.
Dim index As Integer
Los métodos de codificación y descodificación de una clase derivada de Encoding están diseñados para funcionar
en un conjunto completo de datos; es decir, todos los datos que se va a codificar o descodificar se proporciona en
una única llamada al método. Sin embargo, en algunos casos, los datos están disponibles en una secuencia y los
datos que se va a codificar o descodificar pueden estar disponibles solo desde operaciones de lectura
independientes. Para ello, la operación de codificación o descodificación debe recordar cualquier estado guardado
de su invocación anterior. Los métodos de clases derivadas de Encoder y Decoder pueden controlar las
operaciones de codificación y descodificación que abarcan varias llamadas a métodos.
Hay disponible un objeto Encoder para una codificación determinada desde la propiedad Encoding.GetEncoder de
esa codificación. Hay disponible un objeto Decoder para una codificación determinada desde la propiedad
Encoding.GetDecoder de esa codificación. Para las operaciones de descodificación, tenga en cuenta que las clases
derivadas de Decoder incluyen un método Decoder.GetChars , pero no tienen un método que se corresponda con
Encoding.GetString.
En el ejemplo siguiente se muestra la diferencia entre el uso de los métodos Encoding.GetString y
Decoder.GetChars para descodificar una matriz de bytes Unicode. En el ejemplo se codifica una cadena que
contiene algunos caracteres Unicode en un archivo y, a continuación, se usan los dos métodos de descodificación
para descodificarlos de diez bytes en diez bytes. Puesto que hay un par suplente en los bytes décimo y undécimo,
se descodifica en llamadas a métodos independientes. Como muestra el resultado, el método Encoding.GetString
no puede descodificar los bytes correctamente y, en su lugar, los reemplaza con U+FFFD (carácter de reemplazo).
Por otra parte, el método Decoder.GetChars puede descodificar correctamente la matriz de bytes para obtener la
cadena original.
using System;
using System.IO;
using System.Text;
FileStream fs = File.Create(@".\characters.bin");
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(bytes);
bw.Close();
Imports System.IO
Imports System.Text
Module Example
Public Sub Main()
' Use default replacement fallback for invalid encoding.
Dim enc As New UnicodeEncoding(True, False, False)
IMPORTANT
Los problemas más frecuentes en las operaciones de codificación se producen cuando un carácter Unicode no se puede
asignar a una codificación determinada de la página de códigos. Los problemas más comunes de las operaciones de
descodificación se producen cuando las secuencias no válidas de bytes no se pueden traducir a caracteres Unicode válidos.
Por estas razones, debe saber qué estrategia de reserva emplea un determinado objeto de codificación. Siempre que sea
posible, debe especificar la estrategia de reserva usada por un objeto de codificación cuando se crea una instancia del objeto.
Best-Fit Fallback
Cuando un carácter no tiene una coincidencia exacta en la codificación de destino, el codificador puede intentar
asignarle a un carácter similar. (La reserva con ajuste perfecto es principalmente un problema de codificación en
lugar de un problema de descodificación. Hay muy pocas páginas de códigos que contengan caracteres que no se
puedan asignar correctamente a Unicode.) La reserva con ajuste perfecto es el valor predeterminado para las
codificaciones de páginas de códigos y de juegos de caracteres de doble byte recuperadas por las sobrecargas de
Encoding.GetEncoding(Int32) y Encoding.GetEncoding(String).
NOTE
En teoría, las clases de codificación Unicode proporcionadas en .NET (UTF8Encoding, UnicodeEncoding y UTF32Encoding)
admiten cada carácter de todos los juegos de caracteres, por lo que se pueden usar para eliminar los problemas de reserva
con ajuste perfecto.
Las estrategias de ajuste perfecto varían en páginas de códigos diferentes. Por ejemplo, para algunas páginas de
códigos, los caracteres latinos de ancho completo se asignarán a caracteres latinos de ancho medio, que son más
comunes. Para otras páginas de códigos no se realiza esta asignación. Incluso con una estrategia de ajuste perfecto
dinámica, algunos caracteres no tienen un ajuste imaginable en algunas codificaciones. Por ejemplo, un ideograma
chino no tiene ninguna asignación razonable a la página de códigos 1252. En este caso, se emplea una cadena de
reemplazo. De forma predeterminada, esta cadena es simplemente un carácter QUESTION MARK (U+003F).
NOTE
Las estrategias de ajuste perfecto no están documentadas de forma detallada. Sin embargo, varias páginas de códigos se
documentan en el sitio web de Unicode Consortium. Revise el archivo Léame.txt de esa carpeta para obtener una
descripción de cómo interpretar los archivos de asignación.
En el ejemplo siguiente se usa la página de códigos 1252 (la página de códigos de Windows para los idiomas de
Europa occidental) para mostrar la asignación con ajuste perfecto y sus desventajas. El método
Encoding.GetEncoding(Int32) se usa para recuperar un objeto de codificación para la página de códigos 1252. De
forma predeterminada, usa una asignación con ajuste perfecto para los caracteres Unicode que no admite. En el
ejemplo se crea una instancia de una cadena que contiene tres caracteres no ASCII, CIRCLED LATIN CAPITAL
LETTER S (U+24C8), SUPERSCRIPT FIVE (U+2075) e INFINITY (U+221E), separados por espacios en blanco. Como
muestra el resultado del ejemplo, cuando se codifica la cadena, los tres caracteres originales que no son espacios
en blanco se reemplazan con QUESTION MARK (U+003F), DIGIT FIVE (U+0035) y DIGIT EIGHT (U+0038). DIGIT
EIGHT es un reemplazo especialmente deficiente para el carácter INFINITY no compatible y QUESTION MARK
indica que no había ninguna asignación disponible para el carácter original.
using System;
using System.Text;
Console.WriteLine("\n");
Module Example
Public Sub Main()
' Get an encoding for code page 1252 (Western Europe character set).
Dim cp1252 As Encoding = Encoding.GetEncoding(1252)
La asignación con ajuste perfecto es el comportamiento predeterminado para un objeto Encoding que codifica los
datos Unicode en datos de página de códigos, y hay aplicaciones heredadas que se basan en este comportamiento.
Sin embargo, la mayoría de las aplicaciones nuevas deben evitarlo por razones de seguridad. Por ejemplo, las
aplicaciones no deben asignar nombres de dominio mediante una codificación con ajuste perfecto.
NOTE
También puede implementar una asignación personalizada de reserva con ajuste perfecto para una codificación. Para más
información, vea la sección Implementing a Custom Fallback Strategy .
Si la reserva con ajuste perfecto es el valor predeterminado para un objeto de codificación, puede elegir otra
estrategia de reserva cuando se recupera un objeto Encoding llamando a la sobrecarga de
Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback) o Encoding.GetEncoding(String, EncoderFallback,
DecoderFallback) . La próxima sección incluye un ejemplo que reemplaza con un asterisco (*) cada carácter que no
se puede asignar a la página de códigos 1252.
using System;
using System.Text;
Console.WriteLine();
Console.WriteLine();
}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
// Round-trip: False
// * * *
// 002A 0020 002A 0020 002A
Imports System.Text
Module Example
Public Sub Main()
Dim cp1252r As Encoding = Encoding.GetEncoding(1252,
New EncoderReplacementFallback("*"),
New DecoderReplacementFallback("*"))
Replacement Fallback
Cuando un carácter no tiene una coincidencia exacta en el esquema de destino, pero no hay ningún carácter
adecuado al que se pueda asignar, la aplicación puede especificar un carácter o una cadena de reemplazo. Este es el
comportamiento predeterminado del descodificador Unicode, que reemplaza cualquier secuencia de dos bytes que
no pueda descodificar con REPLACEMENT_CHARACTER (U+FFFD). También es el comportamiento predeterminado
de la clase ASCIIEncoding , que reemplaza cada carácter que no puede codificar o descodificar con un signo de
interrogación. En el ejemplo siguiente se muestra el reemplazo de caracteres para la cadena Unicode del ejemplo
anterior. Como muestra el resultado, cada carácter que no se puede descodificar en un valor de bytes ASCII se
reemplaza con 0x3F, que es el código ASCII de un signo de interrogación.
using System;
using System.Text;
Console.WriteLine("\n");
Console.WriteLine();
}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
//
// Encoded bytes: 3F 20 3F 20 3F
//
// Round-trip: False
// ? ? ?
// 003F 0020 003F 0020 003F
Imports System.Text
Module Example
Public Sub Main()
Dim enc As Encoding = Encoding.Ascii
.NET incluye las clases EncoderReplacementFallback y DecoderReplacementFallback, que sustituyen una cadena de
reemplazo si un carácter no se asigna exactamente en una operación de codificación o descodificación. De forma
predeterminada, esta cadena de reemplazo es un signo de interrogación, pero puede llamar a una sobrecarga del
constructor de clase para elegir otra cadena diferente. Normalmente, la cadena de reemplazo es un carácter único,
aunque esto no es un requisito. En el ejemplo siguiente se cambia el comportamiento del codificador de la página
de códigos 1252 creando una instancia de un objeto EncoderReplacementFallback que usa un asterisco (*) como
cadena de reemplazo.
using System;
using System.Text;
Console.WriteLine();
Console.WriteLine();
}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
// Round-trip: False
// * * *
// 002A 0020 002A 0020 002A
Imports System.Text
Module Example
Public Sub Main()
Dim cp1252r As Encoding = Encoding.GetEncoding(1252,
New EncoderReplacementFallback("*"),
New DecoderReplacementFallback("*"))
NOTE
También puede implementar una clase de reemplazo para una codificación. Para más información, vea la sección
Implementing a Custom Fallback Strategy .
Además de QUESTION MARK (U+003F), el REPLACEMENT CHARACTER de Unicode (U+FFFD) se suele usar como
cadena de reemplazo, especialmente al descodificar secuencias de bytes que no se puede traducir correctamente a
caracteres Unicode. Sin embargo, se puede elegir cualquier cadena de reemplazo y esta puede contener varios
caracteres.
Exception Fallback
En lugar de proporcionar una reserva con ajuste perfecto o una cadena de reemplazo, un codificador puede
producir EncoderFallbackException si no puede codificar un juego de caracteres y un descodificador puede
producir DecoderFallbackException si no puede descodificar una matriz de bytes. Para producir una excepción en
operaciones de codificación y descodificación, se proporciona un objeto EncoderExceptionFallback y un objeto
DecoderExceptionFallback , respectivamente, al método Encoding.GetEncoding(String, EncoderFallback,
DecoderFallback) . En el ejemplo siguiente se muestra la reserva de excepción con la clase ASCIIEncoding .
using System;
using System.Text;
Console.WriteLine("\n");
Console.WriteLine();
}
catch (EncoderFallbackException e) {
Console.Write("Exception: ");
if (e.IsUnknownSurrogate())
Console.WriteLine("Unable to encode surrogate pair 0x{0:X4} 0x{1:X3} at index {2}.",
Convert.ToUInt16(e.CharUnknownHigh),
Convert.ToUInt16(e.CharUnknownLow),
e.Index);
else
Console.WriteLine("Unable to encode 0x{0:X4} at index {1}.",
Convert.ToUInt16(e.CharUnknown),
e.Index);
return;
}
Console.WriteLine();
Console.WriteLine();
}
}
catch (DecoderFallbackException e) {
Console.Write("Unable to decode byte(s) ");
foreach (byte unknown in e.BytesUnknown)
Console.Write("0x{0:X2} ");
Module Example
Public Sub Main()
Dim enc As Encoding = Encoding.GetEncoding("us-ascii",
New EncoderExceptionFallback(),
New DecoderExceptionFallback())
return true;
}
this.index--;
return charsToReturn[this.index + 1];
}
Public Overloads Overrides Function Fallback(ByVal charUnknownHigh As Char, ByVal charUnknownLow As Char,
ByVal index As Integer) As Boolean
' Do not try to map surrogates to ASCII.
Return False
End Function
Public Overloads Overrides Function Fallback(ByVal charUnknown As Char, ByVal index As Integer) As Boolean
' Return false if there are already characters to map.
If count >= 1 Then Return False
Return True
End Function
Me.index -= 1
Return charsToReturn(Me.index + 1)
End Function
using System;
using System.Collections.Generic;
using System.Text;
class Program
{
static void Main()
{
Encoding enc = Encoding.GetEncoding("us-ascii", new CustomMapper(), new DecoderExceptionFallback());
Console.WriteLine("\n");
Console.WriteLine();
}
}
}
Imports System.Text
Imports System.Collections.Generic
Module Module1
Sub Main()
Dim enc As Encoding = Encoding.GetEncoding("us-ascii", New CustomMapper(), New
DecoderExceptionFallback())
Vea también
Introducción a la codificación de caracteres en .NET
Encoder
Decoder
DecoderFallback
Encoding
EncoderFallback
Globalización y localización
Procedimientos recomendados para el uso de
cadenas en .NET
16/09/2020 • 60 minutes to read • Edit Online
.NET proporciona una gran compatibilidad para desarrollar aplicaciones localizadas y globalizadas, y simplifica la
aplicación de las convenciones de la referencia cultural actual o de una referencia cultural concreta al realizar
operaciones comunes como ordenar y mostrar cadenas. Pero ordenar o comparar cadenas no es siempre una
operación dependiente de la referencia cultural. Por ejemplo, las cadenas usadas internamente por una aplicación
normalmente se deben administrar de forma idéntica en todas las referencias culturales. Cuando los datos de
cadenas independientes de la referencia cultural (como etiquetas XML, etiquetas HTML, nombres de usuario, rutas
de acceso de archivos y nombres de objetos del sistema) se interpretan como si fueran dependientes de la
referencia cultural, el código de aplicación puede estar sujeto a errores imperceptibles, un rendimiento inadecuado
y, en algunos casos, a problemas de seguridad.
En este tema, se examinan los métodos de ordenación, comparación y uso de mayúsculas y minúsculas de cadenas
de .NET, se presentan recomendaciones para seleccionar un método adecuado de control de cadenas y se
proporciona información adicional sobre los métodos de control de cadenas. También se examina cómo se usan
para la presentación y el almacenamiento los datos con formato, como los datos numéricos y los datos de fecha y
hora.
Por ejemplo, el método IndexOf , que devuelve el índice de una subcadena en un objeto String que coincide con un
carácter o una cadena, tiene nueve sobrecargas:
IndexOf(Char), IndexOf(Char, Int32)y IndexOf(Char, Int32, Int32), que de forma predeterminada realizan una
búsqueda ordinal (con distinción entre mayúsculas y minúsculas e independiente de la referencia cultural) de
un carácter de la cadena.
IndexOf(String), IndexOf(String, Int32)y IndexOf(String, Int32, Int32), que de forma predeterminada realizan una
búsqueda con distinción entre mayúsculas y minúsculas y dependiente de la referencia cultural de una
subcadena de la cadena.
IndexOf(String, StringComparison), IndexOf(String, Int32, StringComparison)y IndexOf(String, Int32, Int32,
StringComparison), que incluyen un parámetro de tipo StringComparison que permite especificar el formato de
la comparación.
Se recomienda seleccionar una sobrecarga que no use valores predeterminados, por las razones siguientes:
Algunas sobrecargas con parámetros predeterminados (las que buscan un valor Char en la instancia de la
cadena) realizan una comparación ordinal, mientras que otras (las que buscan una cadena en la instancia de
la cadena) son dependientes de la referencia cultural. Es difícil recordar qué método usa cada valor
predeterminado y resulta fácil confundir las sobrecargas.
La intención del código que usa valores predeterminados para las llamadas al método no está clara. En el
ejemplo siguiente, en el cual se usan valores predeterminados, es difícil saber si el desarrollador pretendía
realizar una comparación ordinal o lingüística de dos cadenas, o si había alguna diferencia al usar
mayúsculas y minúsculas entre protocol y "http" que pudiera hacer que la prueba de igualdad devolviera
el valor false .
En general, se recomienda llamar a un método que no use los valores predeterminados, ya que hace que la
intención del código no sea ambigua. Esto, a su vez, hace el código más legible y más fácil de depurar y mantener.
En el ejemplo siguiente se abordan las cuestiones que se derivan del ejemplo anterior. Indica claramente que se
usa la comparación ordinal y que se omiten las diferencias en cuanto al uso de mayúsculas y minúsculas.
Sin embargo, la evaluación de dos cadenas para comprobar su igualdad o su criterio de ordenación no produce
ningún resultado correcto único; el resultado depende de los criterios empleados para comparar las cadenas. En
especial, las comparaciones de cadenas que son ordinales o que se basan en las convenciones de ordenación y uso
de mayúsculas y minúsculas de la referencia cultural actual o de la referencia cultural invariable (una referencia
cultural válida para la configuración regional basada en el idioma inglés) pueden producir resultados diferentes.
Además, las comparaciones de cadenas mediante las diferentes versiones de .NET o con .NET en distintos sistemas
operativos o versiones de sistema operativo pueden devolver resultados diferentes. Para más información, vea Las
cadenas y el estándar Unicode.
Comparaciones de cadenas que usan la referencia cultural actual
Un criterio implica usar las convenciones de la referencia cultural actual a la hora de comparar cadenas. Las
comparaciones que se basan en la referencia cultural actual usan la referencia cultural o la configuración regional
actual del subproceso. Si el usuario no establece la referencia cultural, se usa como valor predeterminado la
configuración de la ventana Opciones regionales del Panel de control. Siempre debe usar comparaciones
basadas en la referencia cultural actual cuando los datos sean lingüísticamente pertinentes y cuando refleje una
interacción con el usuario dependiente de la referencia cultural.
En cambio, el comportamiento de comparación y uso de mayúsculas y minúsculas de .NET cambia cuando la
referencia cultural cambia. Esto ocurre cuando una aplicación se ejecuta en un equipo que tiene una referencia
cultural diferente que el equipo en el que se desarrolló la aplicación o cuando el subproceso en ejecución cambia
su referencia cultural. Este comportamiento es deliberado, pero sigue resultando no obvio para muchos
desarrolladores. En el ejemplo siguiente, se muestran las diferencias en el criterio de ordenación entre las
referencias culturales de inglés de EE. UU. ("en-US") y sueco ("sv-SE"). Tenga en cuenta que las palabras
"ångström", "Windows" y "Visual Studio" aparecen en distintas posiciones en las matrices de cadenas ordenadas.
using System;
using System.Globalization;
using System.Threading;
Console.WriteLine();
}
}
// The example displays the following output:
// Sorting using the en-US culture:
// able
// Æble
// ångström
// apple
// Visual Studio
// Windows
//
// Sorting using the sv-SE culture:
// able
// Æble
// apple
// Windows
// Visual Studio
// ångström
Imports System.Globalization
Imports System.Threading
Module Example
Public Sub Main()
Dim values() As String = {"able", "ångström", "apple", _
"Æble", "Windows", "Visual Studio"}
Array.Sort(values)
DisplayArray(values)
Las comparaciones sin distinción entre mayúsculas y minúsculas que usan la referencia cultural actual son iguales
que las comparaciones dependientes de la referencia cultural, excepto que omiten el uso de mayúsculas y
minúsculas según indica la referencia cultural actual del subproceso. Este comportamiento también se puede
manifestar en los criterios de ordenación.
Las comparaciones que usan semántica de la referencia cultural actual son el valor predeterminado para los
métodos siguientes:
Sobrecargas deString.Compare que no incluyen un parámetro StringComparison .
Sobrecargas deString.CompareTo .
El método predeterminado String.StartsWith(String) y el método String.StartsWith(String, Boolean, CultureInfo)
con un parámetro null CultureInfo .
El método predeterminado String.EndsWith(String) y el método String.EndsWith(String, Boolean, CultureInfo)
con un parámetro null CultureInfo.
Sobrecargas deString.IndexOf que aceptan String como un parámetro de búsqueda y que no tienen un
parámetro StringComparison .
Sobrecargas deString.LastIndexOf que aceptan String como un parámetro de búsqueda y que no tienen un
parámetro StringComparison .
En cualquier caso, se recomienda llamar a una sobrecarga que tenga un parámetro StringComparison para aclarar
la intención de la llamada al método.
Pueden surgir errores imperceptibles y no tan imperceptibles cuando los datos de cadenas no lingüísticos se
interpretan lingüísticamente, o cuando los datos de cadenas de una referencia cultural determinada se interpretan
usando las convenciones de otra referencia cultural. El ejemplo canónico es el problema con I en turco.
Para casi todos los alfabetos latinos, incluso en inglés de EE. UU., el carácter "i" (\u0069) es la versión en
minúsculas del carácter "I" (\u0049). Esta regla de mayúsculas y minúsculas se convierte rápidamente en el valor
predeterminado para alguien que programe en esa referencia cultural. En cambio, el alfabeto turco ("tr-TR") incluye
un carácter "I con punto" "İ" (\u0130), que es la versión en mayúsculas de "i". El turco también incluye un carácter
"i sin punto" en minúscula, "ı" (\u0131), que en mayúsculas es "I". Este comportamiento también se produce en la
referencia cultural de azerbaiyano ("az").
Por tanto, los supuestos sobre poner en mayúsculas "i" o escribir "I" en minúsculas no son válidas en todas las
referencias culturales. Si usa las sobrecargas predeterminadas para las rutinas de comparación de cadenas, estarán
sujetas a variaciones entre distintas referencias culturales. Si los datos que se van a comparar son no lingüísticos,
el uso de las sobrecargas predeterminadas puede generar resultados no deseables, como ilustra el siguiente
intento de realizar una comparación sin distinción entre mayúsculas y minúsculas de las cadenas "file" y "FILE".
using System;
using System.Globalization;
using System.Threading;
Module Example
Public Sub Main()
Dim fileUrl = "file"
Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
Console.WriteLine("Culture = {0}", _
Thread.CurrentThread.CurrentCulture.DisplayName)
Console.WriteLine("(file == FILE) = {0}", _
fileUrl.StartsWith("FILE", True, Nothing))
Console.WriteLine()
Esta comparación podría producir problemas importantes si la referencia cultural se usa involuntariamente en
configuraciones que afectan a la seguridad, como en el ejemplo siguiente. Una llamada al método como
IsFileURI("file:") devuelve true si la referencia cultural actual es inglés de EE. U.U., pero false si la referencia
cultural actual es el turco. Así, en los sistemas turcos, alguien podría sortear las medidas de seguridad que
bloquean el acceso a los URI sin distinción entre mayúsculas y minúsculas que comienzan con "FILE":.
En este caso, como "file:" se debe interpretar como un identificador no lingüístico e independiente de la referencia
cultural, el código se debe escribir como se muestra en el ejemplo siguiente:
IMPORTANT
Aunque los métodos de comparación de cadenas hacen caso omiso de los caracteres nulos incrustados, los métodos de
búsqueda de cadenas como String.Contains, String.EndsWith, String.IndexOf, String.LastIndexOfy String.StartsWith sí los
tienen en cuenta.
En el ejemplo siguiente se realiza una comparación dependiente de la referencia cultural de la cadena "Aa" con una
cadena similar que contiene varios caracteres nulos insertados entre "A" y "a", y se muestra cómo las dos cadenas
se consideran iguales:
using System;
Pero las cadenas no se consideran iguales cuando se usa la comparación ordinal, como se muestra en el ejemplo
siguiente:
Las comparaciones ordinales sin distinción entre mayúsculas y minúsculas son el siguiente enfoque más
conservador. Estas comparaciones omiten la mayor parte del uso de mayúsculas y minúsculas; por ejemplo,
"windows" coincide con "Windows". A la hora de tratar con caracteres ASCII, esta directiva es equivalente a
StringComparison.Ordinal, salvo que omite el uso de mayúsculas y minúsculas habitual de ASCII. Por tanto,
cualquier carácter de [A, Z] (\u0041-\u005A) coincide con el carácter correspondiente de [a, z] (\u0061-\007A). El
uso de mayúsculas y minúsculas fuera del intervalo ASCII emplea las tablas de la referencia cultural de todos los
idiomas. Por tanto, la siguiente comparación:
String.Compare(strA.ToUpperInvariant(), strB.ToUpperInvariant(),
StringComparison.Ordinal);
String.Compare(strA.ToUpperInvariant(), strB.ToUpperInvariant(),
StringComparison.Ordinal)
NOTE
El comportamiento de las cadenas del sistema de archivos, claves del Registro y valores, y variables de entorno se representa
mejor mediante StringComparison.OrdinalIgnoreCase.
A la hora de interpretar nombres de archivo, cookies u otros elementos donde pueda aparecer una combinación
como "å", las comparaciones ordinales siguen ofreciendo el comportamiento más transparente y adecuado.
En conjunto, la referencia cultural de todos los idiomas tiene muy pocas propiedades que la hagan útil para la
comparación. Realiza la comparación de manera lingüísticamente pertinente, lo que le impide garantizar una
equivalencia simbólica completa, pero no es la opción ideal para la presentación en cualquier referencia cultural.
Una de las pocas razones para usar StringComparison.InvariantCulture con el fin de realizar una comparación es
para conservar datos ordenados cuando se desea realizar una presentación idéntica transculturalmente. Por
ejemplo, si un archivo de datos grande que contiene una lista de identificadores ordenados para su presentación
acompaña una aplicación, al agregar datos a esta lista se necesitaría realizar una inserción con ordenación de estilo
invariable.
VA LO R DE
SY ST EM . ST RIN GC O M PA RISO N
Variables de entorno.
this.fname = name;
if (comparer != null)
this.comparer = comparer;
else
this.comparer = StringComparer.OrdinalIgnoreCase;
}
if (! (obj is FileName))
return comparer.Compare(this.fname, obj.ToString());
else
return comparer.Compare(this.fname, ((FileName) obj).Name);
}
}
Public Class FileName : Implements IComparable
Dim fname As String
Dim comparer As StringComparer
Me.fname = name
String.Equals
Interpretación predeterminada: StringComparison.Ordinal.
La clase String le permite comprobar la igualdad llamando a las sobrecargas de método estático o de instancia
Equals , o usando el operador de igualdad estático. Las sobrecargas y el operador usan la comparación ordinal de
forma predeterminada. Sin embargo, todavía sigue siendo recomendable llamar a una sobrecarga que especifique
explícitamente el tipo StringComparison aunque desee realizar una comparación ordinal; esto facilita la búsqueda
de cierta interpretación de la cadena en el código.
String.ToUpper y String.ToLower
Interpretación predeterminada: StringComparison.CurrentCulture.
Debe tener cuidado al usar estos métodos, ya que forzar que una cadena esté en mayúsculas o en minúsculas se
usa a menudo como una pequeña normalización para comparar cadenas independientemente del uso de
mayúsculas y minúsculas. En tal caso, considere la posibilidad de emplear una comparación sin distinción entre
mayúsculas y minúsculas.
También están disponibles los métodos String.ToUpperInvariant y String.ToLowerInvariant . ToUpperInvariant es la
manera estándar de normalizar el uso de mayúsculas y minúsculas. Las comparaciones realizadas mediante
StringComparison.OrdinalIgnoreCase tienen un comportamiento que es la composición de dos llamadas: llamar a
ToUpperInvariant en ambos argumentos de cadena y realizar una comparación mediante
StringComparison.Ordinal.
También hay sobrecargas para convertir a mayúsculas y minúsculas en una referencia cultural concreta, pasando al
método un objeto CultureInfo que representa esa referencia cultural.
Char.ToUpper y Char.ToLower
Interpretación predeterminada: StringComparison.CurrentCulture.
Estos métodos funcionan de manera similar a los métodos String.ToUpper y String.ToLower descritos en la sección
anterior.
String.StartsWith y String.EndsWith
Interpretación predeterminada: StringComparison.CurrentCulture.
De forma predeterminada, estos dos métodos realizan una comparación dependiente de la referencia cultural.
String.IndexOf y String.LastIndexOf
Interpretación predeterminada: StringComparison.CurrentCulture.
No hay coherencia en cómo las sobrecargas predeterminadas de estos métodos realizan las comparaciones. Todos
los métodos String.IndexOf y String.LastIndexOf que incluyen un parámetro Char realizan una comparación
ordinal, pero los métodos String.IndexOf y String.LastIndexOf predeterminados que incluyen un parámetro String
realizan una comparación dependiente de la referencia cultural.
Si llama al método String.IndexOf(String) o String.LastIndexOf(String) y le pasa una cadena para ubicar en la
instancia actual, se recomienda llamar a una sobrecarga que especifique explícitamente el tipo StringComparison .
Las sobrecargas que incluyen un argumento Char no le permiten especificar un tipo StringComparison .
// Incorrect.
string []storedNames;
Array.Sort(names); // Line A.
}
' Incorrect.
Dim storedNames() As String
Aparece una variación recomendada en el ejemplo siguiente, que usa el mismo método de comparación ordinal
(independiente de la referencia cultural) para ordenar y buscar en la matriz. El código cambiado se refleja en las
líneas etiquetadas como Line A y Line B en los dos ejemplos.
// Correct.
string []storedNames;
' Correct.
Dim storedNames() As String
Si estos datos se conservan y mueven entre distintas referencias culturales, y se usa la ordenación para presentar
estos datos al usuario, es mejor usar StringComparison.InvariantCulture, que funciona lingüísticamente para
obtener una mejor salida para el usuario pero no se ve afectado por los cambios en la referencia cultural. En el
ejemplo siguiente se modifican los dos ejemplos anteriores para usar la referencia cultural de todos los idiomas
con el fin de ordenar y buscar en la matriz.
// Correct.
string []storedNames;
' Correct.
Dim storedNames() As String
Dim concat1 As String = "The amount is " & 126.03 & "."
Console.WriteLine(concat1)
Dim concat2 As String = "The amount is " & 126.03.ToString(CultureInfo.InvariantCulture) & "."
Console.WriteLine(concat2)
Para la interpolación de cadenas, en lugar de asignar una cadena interpolada a una instancia String, asígnela
a un elemento FormattableString. Después, se puede llamar al método FormattableString.ToString() para
generar una cadena de resultado que refleje las convenciones de la referencia cultural actual, o bien puede
llamar al método FormattableString.ToString(IFormatProvider) para generar una cadena de resultado que
refleje las convenciones de la referencia cultural especificada. También se puede pasar la cadena que admite
formato al método estático FormattableString.Invariant con el fin de generar una cadena de resultado que
refleje las convenciones de la referencia cultural invariable. En el ejemplo siguiente se muestra este enfoque.
(La salida del ejemplo refleja una referencia cultural actual de en-US).
using System;
using System.Globalization;
class Program
{
static void Main()
{
Decimal value = 126.03m;
FormattableString amount = $"The amount is {value:C}";
Console.WriteLine(amount.ToString());
Console.WriteLine(amount.ToString(new CultureInfo("fr-FR")));
Console.WriteLine(FormattableString.Invariant(amount));
}
}
// The example displays the following output:
// The amount is $126.03
// The amount is 126,03 €
// The amount is ¤126.03
Imports System.Globalization
Module Program
Sub Main()
Dim value As Decimal = 126.03
Dim amount As FormattableString = $"The amount is {value:C}"
Console.WriteLine(amount.ToString())
Console.WriteLine(amount.ToString(new CultureInfo("fr-FR")))
Console.WriteLine(FormattableString.Invariant(amount))
End Sub
End Module
' The example displays the following output:
' The amount is $126.03
' The amount is 126,03 €
' The amount is ¤126.03
Puede conservar datos que no son de cadena como datos binarios o como datos con formato. Si decide guardarlos
como datos con formato, debe llamar a una sobrecarga del método de formato que incluya un parámetro
provider y pasarle la propiedad CultureInfo.InvariantCulture . La referencia cultural de todos los idiomas
proporciona un formato coherente para los datos con formato que es independiente de la referencia cultural y del
equipo. En cambio, si se conservan datos a los que se aplica formato con referencias culturales distintas de la
referencia cultural de todos los idiomas, se presentan varias limitaciones:
Es probable que los datos no puedan usarse si se recuperan en un sistema que tiene una referencia cultural
distinta, o si el usuario del sistema actual cambia la referencia cultural actual e intenta recuperar los datos.
Las propiedades de una referencia cultural en un equipo específico pueden diferir de los valores estándar. En
cualquier momento, un usuario puede personalizar la configuración de visualización que depende de la cultural.
Debido a esto, es posible que los datos con formato que se guardan en un sistema no sean legibles después de
que el usuario personalice la configuración de la referencia cultural. Es posible que la portabilidad de los datos
con formato entre equipos sea incluso más limitada.
Las normas internacionales, regionales o nacionales que rigen el formato de los números o las fechas y horas
cambian con el tiempo, y estos cambios se incorporan en las actualizaciones de los sistemas operativos
Windows. Cuando cambian las convenciones de formato, los datos a los que se aplicó formato usando las
convenciones anteriores pueden llegar a ser ilegibles.
En el ejemplo siguiente se muestra la portabilidad limitada que se deriva de usar el formato dependiente de la
referencia cultural para conservar los datos. En el ejemplo se guarda una matriz de valores de fecha y hora en un
archivo. Se les da formato con las convenciones de la referencia cultural Inglés (Estados Unidos). Después de que la
aplicación cambie la referencia cultural del subproceso actual a Francés (Suiza), intenta leer los valores guardados
usando las convenciones de formato de la referencia cultural actual. El intento de leer dos de los elementos de
datos genera una excepción FormatException y la matriz de fechas ahora contiene dos elementos incorrectos que
iguales a MinValue.
using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
Module Example
Private filename As String = ".\dates.dat"
06.05.1758 21:26
05.05.1818 07:19
22.04.1870 23:54
08.09.1890 06:47
18.02.1905 15:12
Operaciones básicas de cadenas en .NET
16/09/2020 • 2 minutes to read • Edit Online
A menudo, las aplicaciones responden a los usuarios mediante la construcción de mensajes basados en los datos
proporcionados por el usuario. Por ejemplo, no es raro que los sitios web respondan a un usuario que acaba de
iniciar sesión con un saludo especializado que incluye el nombre del usuario.
Varios métodos de las clases System.String y System.Text.StringBuilder le permiten construir cadenas
personalizadas de forma dinámica para mostrarlas en la interfaz de usuario. Estos métodos también le ayudan a
realizar una serie de operaciones básicas de cadenas, como crear cadenas nuevas a partir de matrices de bytes,
comparar los valores de cadenas y modificar cadenas existentes.
Secciones relacionadas
Conversión de tipos en .NET
Se describe cómo convertir un tipo en otro.
Aplicar formato a tipos
Se describe cómo dar formato a cadenas mediante los especificadores de formato.
Creación de cadenas en .NET
16/09/2020 • 7 minutes to read • Edit Online
.NET Framework permite crear cadenas mediante asignaciones simples y además sobrecarga un constructor de
clases para admitir la creación de cadenas con una serie de parámetros distintos. .NET Framework también
proporciona varios métodos en la clase System.String que crean nuevos objetos de cadena al combinar varias
cadenas, matrices de cadenas u objetos.
Formato
Puede usar el método String.Format para crear cadenas con formato y concatenar cadenas que representan a
varios objetos. Este método convierte automáticamente cualquier objeto pasado en una cadena. Por ejemplo, si la
aplicación debe mostrar un valor Int32 y un valor DateTime al usuario, puede construir fácilmente una cadena
para representar estos valores con el método Format . Para más información sobre las convenciones de formato
usadas con este método, consulte la sección sobre formatos compuestos.
En el ejemplo siguiente se usa el método Format para crear una cadena que emplea una variable de entero.
int numberOfFleas = 12;
string miscInfo = String.Format("Your dog has {0} fleas. " +
"It is time to get a flea collar. " +
"The current universal date is: {1:u}.",
numberOfFleas, DateTime.Now);
Console.WriteLine(miscInfo);
// The example displays the following output:
// Your dog has 12 fleas. It is time to get a flea collar.
// The current universal date is: 2008-03-28 13:31:40Z.
En este ejemplo, DateTime.Now muestra la fecha y hora actuales en el formato especificado por la referencia
cultural asociada al subproceso actual.
Concat
El método String.Concat se puede usar para crear fácilmente un objeto de cadena a partir de dos o más objetos
existentes. Proporciona una manera independiente del lenguaje de concatenar cadenas. Este método acepta
cualquier clase que derive de System.Object . En el ejemplo siguiente se crea una cadena a partir de dos objetos
de cadena existentes y un carácter de separación.
Join
El método String.Join crea una cadena a partir de una matriz de cadenas y una cadena separadora. Este método
resulta útil si quiere concatenar varias cadenas para formar una lista quizás separada por una coma.
En el ejemplo siguiente se usa un espacio para enlazar una matriz de cadenas.
Insertar
El método String.Inser t crea una cadena al insertar una cadena en la posición especificada de otra cadena. Este
método usa un índice basado en cero. En el ejemplo siguiente se inserta una cadena en la quinta posición del índice
de MyString y se crea una nueva cadena con este valor.
CopyTo
El método String.CopyTo copia partes de una cadena en una matriz de caracteres. Puede especificar el índice
inicial de la cadena y el número de caracteres que se va a copiar. Este método toma el índice de origen, una matriz
de caracteres, el índice de destino y el número de caracteres que se va a copiar. Todos los índices se basan en cero.
En el ejemplo siguiente se usa el método CopyTo para copiar los caracteres de la palabra "Hello" de un objeto de
cadena en la primera posición del índice de una matriz de caracteres.
Vea también
Operaciones básicas de cadenas
Formatos compuestos
Recorte y eliminación de caracteres de cadenas en
.NET
16/09/2020 • 8 minutes to read • Edit Online
Si va a analizar una frase en las palabras que la forman, el resultado pueden ser palabras con espacios en blanco
delante y detrás. En este caso, puede usar uno de los métodos de recorte de la clase System.String para quitar
espacios u otros caracteres de una posición especificada de la cadena. En la tabla siguiente se describen los
métodos de recorte disponibles.
String.Trim Quita del comienzo y del final de una cadena los espacios en
blanco o los caracteres especificados en una matriz de
caracteres.
Trim
Los espacios en blanco situados delante y detrás de una cadena se pueden quitar fácilmente con el método
String.Trim, como se muestra en el ejemplo siguiente.
También se pueden quitar del principio y el final de una cadena los caracteres especificados en una matriz de
caracteres. En el ejemplo siguiente se quitan los caracteres de espacio en blanco, los puntos y asteriscos.
using System;
Module Example
Public Sub Main()
Dim header As String = "* A Short String. *"
Console.WriteLine(header)
Console.WriteLine(header.Trim({" "c, "*"c, "."c}))
End Sub
End Module
' The example displays the following output:
' * A Short String. *
' A Short String
TrimEnd
El método String.TrimEnd quita caracteres del final de una cadena, creando un nuevo objeto de cadena. A este
método se le pasa una matriz de caracteres para especificar los caracteres que se van a quitar. El orden de los
elementos en la matriz de caracteres no afecta a la operación de recorte. El recorte se detiene cuando se encuentra
un carácter no especificado en la matriz.
En el ejemplo siguiente se quitan las últimas letras de una cadena con el método TrimEnd . En este ejemplo, se
invierte la posición de los caracteres 'r' y 'W' para ilustrar que el orden de los caracteres en la matriz no tiene
importancia. Observe que este código quita la última palabra de MyString y parte de la primera.
TrimStart
El método String.TrimStar t es parecido al método String.TrimEnd , con la diferencia de que crea una nueva
cadena quitando caracteres del comienzo de un objeto de cadena existente. Al método TrimStar t se le pasa una
matriz de caracteres para especificar los caracteres que se van a quitar. Lo mismo que en el método TrimEnd , el
orden de los elementos en la matriz de caracteres no afecta a la operación de recorte. El recorte se detiene cuando
se encuentra un carácter no especificado en la matriz.
En el siguiente ejemplo se quita la primera palabra de una cadena. En este ejemplo, se invierte la posición de los
caracteres 'l' y 'H' para ilustrar que el orden de los caracteres en la matriz no tiene importancia.
Quitar
El método String.Remove quita un número especificado de caracteres, comenzando en una posición especificada de
una cadena existente. Este método utiliza un índice basado en cero.
En el ejemplo siguiente se quitan diez caracteres de una cadena, comenzando en la posición cinco de un índice de
base cero de la cadena.
Sustituya
También puede quitar un carácter o una subcadena de una cadena llamando al método String.Replace(String,
String) y especificando una cadena vacía (String.Empty) como reemplazo. En el ejemplo siguiente se quitan todas
las comas de una cadena.
using System;
Module Example
Public Sub Main()
Dim phrase As String = "a cold, dark night"
Console.WriteLine("Before: {0}", phrase)
phrase = phrase.Replace(",", "")
Console.WriteLine("After: {0}", phrase)
End Sub
End Module
' The example displays the following output:
' Before: a cold, dark night
' After: a cold dark night
Vea también
Operaciones básicas de cadenas
Relleno de cadenas en .NET
16/09/2020 • 3 minutes to read • Edit Online
Use uno de los siguientes métodos String para crear una cadena que conste de una cadena original rellenada con
caracteres iniciales o finales hasta una longitud total especificada. El carácter de relleno puede ser un espacio o un
carácter especificado. La cadena resultante aparece alineada a la derecha o bien a la izquierda. Si la longitud de la
cadena original ya es igual o mayor que la longitud total deseada, los métodos de relleno devuelven la cadena
original sin modificarla. Para obtener más información, vea las secciones Devoluciones de las dos sobrecargas de
los métodos String.PadLeft y String.PadRight.
String.PadLeft Rellena una cadena con caracteres iniciales hasta una longitud
total especificada.
String.PadRight Rellena una cadena con caracteres finales hasta una longitud
total especificada.
PadLeft
El método String.PadLeft crea una cadena mediante la concatenación de suficientes caracteres de relleno iniciales
con una cadena original para alcanzar la longitud total especificada. El método String.PadLeft(Int32) usa el espacio
en blanco como carácter de relleno y el método String.PadLeft(Int32, Char) le permite especificar su propio carácter
de relleno.
En el ejemplo de código siguiente se usa el método PadLeft para crear una cadena con una longitud de veinte
caracteres. En el ejemplo se muestra " --------Hello World! " en la consola.
PadRight
El método String.PadRight crea una cadena mediante la concatenación de suficientes caracteres de relleno finales
con una cadena original para alcanzar la longitud total especificada. El método String.PadRight(Int32) usa el espacio
en blanco como carácter de relleno y el método String.PadRight(Int32, Char) le permite especificar su propio
carácter de relleno.
En el ejemplo de código siguiente se usa el método PadRight para crear una cadena con una longitud de veinte
caracteres. En el ejemplo se muestra " Hello World!-------- " en la consola.
String^ MyString = "Hello World!";
Console::WriteLine(MyString->PadRight(20, '-'));
Vea también
Operaciones básicas de cadenas
Comparación de cadenas en .NET
16/09/2020 • 13 minutes to read • Edit Online
.NET proporciona varios métodos para comparar los valores de cadenas. En la tabla siguiente se enumeran y
describen los métodos de comparación de valores.
Comparar
El método String.Compare estático proporciona una manera de comparar dos cadenas exhaustivamente. En este
método se tiene en cuenta la referencia cultural. Esta función se puede usar para comparar dos cadenas o
subcadenas de dos cadenas. Además, se proporcionan sobrecargas para tener en cuenta o no las diferencias de
referencia cultural y de mayúsculas y minúsculas. En la tabla siguiente, se muestran los tres valores enteros que
este método puede devolver.
VA LO R DEVUELTO C O N DIC IÓ N
o bien
o bien
IMPORTANT
La finalidad principal del método String.Compare es que se utilice para la ordenación o clasificación de cadenas. El método
String.Compare no debe utilizarse para comprobar la igualdad (es decir, para buscar explícitamente un valor devuelto que sea
0 sin tener en cuenta si una cadena es menor o mayor que otra). En su lugar, para determinar si dos cadenas son iguales, use
el método String.Equals(String, String, StringComparison) .
En el ejemplo siguiente, se usa el método String.Compare para determinar los valores relativos de dos cadenas.
CompareOrdinal
El método String.CompareOrdinal compara dos objetos de cadena sin tener en cuenta la referencia cultural local.
Los valores devueltos de este método son idénticos a los que devolvía el método Compare en la tabla anterior.
IMPORTANT
La finalidad principal del método String.CompareOrdinal es que se utilice para la ordenación o clasificación de cadenas. El
método String.CompareOrdinal no debe utilizarse para comprobar la igualdad (es decir, para buscar explícitamente un valor
devuelto que sea 0 sin tener en cuenta si una cadena es menor o mayor que otra). En su lugar, para determinar si dos
cadenas son iguales, use el método String.Equals(String, String, StringComparison) .
En el ejemplo siguiente se usa el método CompareOrdinal para comparar los valores de dos cadenas.
CompareTo
El método String.CompareTo compara la cadena que encapsula el objeto de cadena actual con otra cadena u objeto.
Los valores devueltos de este método son idénticos a los que devolvía el método String.Compare en la tabla
anterior.
IMPORTANT
La finalidad principal del método String.CompareTo es que se utilice para la ordenación o clasificación de cadenas. El método
String.CompareTo no debe utilizarse para comprobar la igualdad (es decir, para buscar explícitamente un valor devuelto que
sea 0 sin tener en cuenta si una cadena es menor o mayor que otra). En su lugar, para determinar si dos cadenas son iguales,
use el método String.Equals(String, String, StringComparison) .
En el ejemplo siguiente se usa el método String.CompareTo para comparar los objetos string1 y string2 .
Es igual a
El método String.Equals puede determinar con facilidad si dos cadenas son iguales. Este método distingue entre
mayúsculas y minúsculas y devuelve un valor booleano True o False . Se puede usar desde una clase existente,
como se muestra en el siguiente ejemplo. En el ejemplo siguiente se usa el método Equals para determinar si un
objeto de cadena contiene la frase "Hello World".
StartsWith y EndsWith
El método String.Star tsWith se puede usar para determinar si un objeto de cadena comienza con los mismos
caracteres que forman otra cadena. Este método distingue entre mayúsculas y minúsculas y devuelve true si el
objeto de cadena actual comienza con la cadena que se pasa y false si no lo hace. En el ejemplo siguiente se usa
este método para determinar si un objeto de cadena comienza con "Hello".
IndexOf y LastIndexOf
El método String.IndexOf se puede usar para determinar la posición de la primera aparición de un carácter
concreto dentro de una cadena. Este método, que distingue entre mayúsculas y minúsculas, empieza a contar desde
el comienzo de una cadena y devuelve la posición del carácter que se pasa utilizando un índice de base cero. Si no
encuentra el carácter, se devuelve un valor de –1.
En el ejemplo siguiente se usa el método IndexOf para buscar la primera aparición del carácter ' l ' en una cadena.
Vea también
Operaciones básicas de cadenas
Realizar operaciones de cadenas que no distinguen entre referencias culturales
Ordenación de tablas de peso (para .NET en Windows)
Tabla de elementos de intercalación predeterminada Unicode (para .NET Core en macOS y Linux)
Cambio de mayúsculas y minúsculas en .NET
16/09/2020 • 8 minutes to read • Edit Online
Si escribe una aplicación que acepta la entrada de un usuario, nunca podrá estar seguro de si usará mayúsculas o
minúsculas para escribir los datos. Normalmente querrá que las cadenas usen mayúsculas y minúsculas de forma
coherente, especialmente si se van a mostrar en la interfaz de usuario. En la tabla siguiente se describen tres
métodos para cambiar las mayúsculas y minúsculas. Los dos primeros métodos proporcionan una sobrecarga que
acepta una referencia cultural.
WARNING
Tenga en cuenta que los métodos String.ToUpper y String.ToLower no deben usarse para convertir cadenas para compararlas
ni para comprobar su igualdad. Para más información, vea la sección Comparar cadenas con mayúsculas y minúsculas
mezcladas.
ToUpper
El método String.ToUpper convierte todos los caracteres de una cadena a mayúsculas. En el siguiente ejemplo, se
convierte la cadena "Hello World!" de mayúsculas y minúsculas mezcladas a mayúsculas.
El ejemplo anterior tiene en cuenta la referencia cultural de forma predeterminada; aplica las convenciones de
mayúsculas y minúsculas de la referencia cultural actual. Para realizar un cambio de mayúsculas y minúsculas sin
tener en cuenta la referencia cultural o para aplicar las convenciones de mayúsculas y minúsculas de una referencia
cultural determinada, use la sobrecarga del método String.ToUpper(CultureInfo) y proporcione un valor
CultureInfo.InvariantCulture o un objeto System.Globalization.CultureInfo que representa la referencia cultural
especificada al parámetro culture. Para obtener un ejemplo que muestra cómo usar el método ToUpper para
realizar un cambio de mayúsculas y minúsculas sin tener en cuenta la referencia cultural, consulte Realizar cambios
de mayúsculas y minúsculas que no tienen en cuenta las referencias culturales.
ToLower
El método String.ToLower es similar al método anterior, pero en su lugar convierte todos los caracteres de una
cadena a minúsculas. En el siguiente ejemplo, se convierte la cadena "Hello World!" en minúsculas.
El ejemplo anterior tiene en cuenta la referencia cultural de forma predeterminada; aplica las convenciones de
mayúsculas y minúsculas de la referencia cultural actual. Para realizar un cambio de mayúsculas y minúsculas sin
tener en cuenta la referencia cultural o para aplicar las convenciones de mayúsculas y minúsculas de una referencia
cultural determinada, use la sobrecarga del método String.ToLower(CultureInfo) y proporcione un valor
CultureInfo.InvariantCulture o un objeto System.Globalization.CultureInfo que representa la referencia cultural
especificada al parámetro culture. Para obtener un ejemplo que muestra cómo usar el método
ToLower(CultureInfo) para realizar un cambio de mayúsculas y minúsculas sin tener en cuenta la referencia cultural,
consulte Realizar cambios de mayúsculas y minúsculas que no tienen en cuenta las referencias culturales.
ToTitleCase
El método TextInfo.ToTitleCase convierte el primer carácter de cada palabra a mayúsculas y el resto de los
caracteres a minúsculas. Sin embargo, se da por hecho que las palabras que están completamente en mayúsculas
son siglas y no se convierten.
El método TextInfo.ToTitleCase tiene en cuenta la referencia cultural; es decir, usa las convenciones de mayúsculas y
minúsculas de una referencia cultural determinada. Para llamar al método, recupere primero el objeto TextInfo que
representa las convenciones de mayúsculas y minúsculas de la referencia cultural determinada a partir de la
propiedad CultureInfo.TextInfo de una referencia cultural determinada.
En el ejemplo siguiente, se pasa cada cadena de una matriz al método TextInfo.ToTitleCase. Las cadenas incluyen
cadenas de título correctas así como acrónimos. Las cadenas se convierten a mayúsculas de tipo título usando las
convenciones de mayúsculas y minúsculas de la referencia cultural Inglés (Estados Unidos).
using System;
using System.Globalization;
TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
foreach (var value in values)
Console.WriteLine("{0} --> {1}", value, ti.ToTitleCase(value));
}
}
// The example displays the following output:
// a tale of two cities --> A Tale Of Two Cities
// gROWL to the rescue --> Growl To The Rescue
// inside the US government --> Inside The US Government
// sports and MLB baseball --> Sports And MLB Baseball
// The Return of Sherlock Holmes --> The Return Of Sherlock Holmes
// UNICEF and children --> UNICEF And Children
Imports System.Globalization
Module Example
Public Sub Main()
Dim values() As String = {"a tale of two cities", "gROWL to the rescue",
"inside the US government", "sports and MLB baseball",
"The Return of Sherlock Holmes", "UNICEF and children"}
Recuerde que, aunque tiene en cuenta la referencia cultural, el método TextInfo.ToTitleCase no proporciona reglas
de mayúsculas y minúsculas lingüísticamente correctas. En el ejemplo anterior, el método convierte "a tale of two
cities" en "A Tale Of Two Cities". Sin embargo, el uso lingüísticamente correcto de las mayúsculas y minúsculas de
título para la referencia cultural en-US es "A Tale of Two Cities".
Vea también
Operaciones básicas de cadenas
Realizar operaciones de cadenas que no distinguen entre referencias culturales
Utilizar la clase StringBuilder en .NET
16/09/2020 • 12 minutes to read • Edit Online
El objeto String es inmutable. Cada vez que se usa uno de los métodos de la clase System.String, se crea un objeto
de cadena en la memoria, lo que requiere una nueva asignación de espacio para ese objeto. En las situaciones en
las que es necesario realizar modificaciones repetidas en una cadena, la sobrecarga asociada a la creación de un
objeto String puede ser costosa. La clase System.Text.StringBuilder se puede usar para modificar una cadena sin
crear un objeto. Por ejemplo, el uso de la clase StringBuilder puede mejorar el rendimiento al concatenar muchas
cadenas en un bucle.
using System;
using System.Text;
Imports System.Text
Además, se puede usar la propiedad de lectura y escritura Capacity para establecer la longitud máxima del objeto.
En el ejemplo siguiente se usa la propiedad Capacity para definir la longitud máxima del objeto.
myStringBuilder->Capacity = 25;
myStringBuilder.Capacity = 25;
myStringBuilder.Capacity = 25
El método EnsureCapacity se puede usar para comprobar la capacidad del objeto StringBuilder actual. Si la
capacidad es mayor que el valor transmitido, no se realiza ningún cambio, pero si es menor que este, la capacidad
actual se cambia para que coincida con el valor en cuestión.
También se puede ver o establecer la propiedad Length. Si la propiedad Length se establece en un valor mayor que
el de la propiedad Capacity , la propiedad Capacity se cambia automáticamente al mismo valor de la propiedad
Length . Si la propiedad Length se establece en un valor menor que la longitud de la cadena de StringBuilder
actual, se acorta la cadena.
Anexar
El método Append se puede usar para agregar texto o la representación de cadena de un objeto al final de una
cadena representada por el objeto StringBuilder actual. En el ejemplo siguiente, se inicializa StringBuilder en
"Hello World" y, después, se anexa texto al final del objeto. El espacio se asigna automáticamente según sea
necesario.
AppendFormat
El método StringBuilder.AppendFormat agrega texto al final del objeto StringBuilder. Admite la característica de
formatos compuestos (para obtener más información, consulte Formatos compuestos) mediante la llamada a la
implementación de IFormattable del objeto u objetos a los que se va a dar formato. Por tanto, acepta las cadenas de
formato estándar para valores numéricos, de fecha y hora y de enumeración; las cadenas de formato personalizado
para valores numéricos y de fecha y hora; y las cadenas de formato definidas para los tipos personalizados. (Para
obtener información acerca del formato, consulte Aplicar formato a tipos.) Este método se puede usar para
personalizar el formato de las variables y anexar esos valores a StringBuilder. En el ejemplo siguiente se usa el
método AppendFormat para colocar un valor entero con formato de valor de divisa al final de un objeto
StringBuilder.
Insertar
El método Insert agrega una cadena o un objeto en una posición especificada del objeto StringBuilder actual. En el
ejemplo siguiente se usa este método para insertar una palabra en la sexta posición de un objeto StringBuilder.
Quitar
El método Remove se puede usar para quitar un número de caracteres especificado del objeto StringBuilder, a
partir de un índice de base cero definido. En el ejemplo siguiente se usa el método Remove para acortar un objeto
StringBuilder.
Sustituya
El método Replace se puede usar para reemplazar caracteres del objeto StringBuilder por otro carácter
especificado. En el ejemplo siguiente se usa el método Replace para buscar todas las instancias del carácter de
signo de exclamación (!) y reemplazarlas por el carácter de signo de interrogación (?) en un objeto StringBuilder.
StringBuilder^ myStringBuilder = gcnew StringBuilder("Hello World!");
myStringBuilder->Replace('!', '?');
Console::WriteLine(myStringBuilder);
// The example displays the following output:
// Hello World?
using System;
using System.Text;
Module Example
Public Sub Main()
Dim sb As New StringBuilder()
Dim flag As Boolean = True
Dim spellings() As String = {"recieve", "receeve", "receive"}
sb.AppendFormat("Which of the following spellings is {0}:", flag)
sb.AppendLine()
For ctr As Integer = 0 To spellings.GetUpperBound(0)
sb.AppendFormat(" {0}. {1}", ctr, spellings(ctr))
sb.AppendLine()
Next
sb.AppendLine()
Console.WriteLine(sb.ToString())
End Sub
End Module
' The example displays the following output:
' Which of the following spellings is True:
' 0. recieve
' 1. receeve
' 2. receive
Vea también
System.Text.StringBuilder
Operaciones básicas de cadenas
Aplicación de formato a tipos
Procedimiento para realizar manipulaciones de
cadena básicas en .NET
16/09/2020 • 5 minutes to read • Edit Online
En el ejemplo siguiente, se usan algunos de los métodos descritos en los temas de Operaciones básicas de cadenas
para construir una clase que realice manipulaciones de cadena de una manera que podría encontrarse en una
aplicación real. La clase MailToData almacena el nombre y dirección de los individuos en propiedades distintas y
proporciona una forma de combinar los campos City , State y Zip en una única cadena para mostrar al usuario.
Además, la clase permite al usuario que escriba la información sobre la ciudad, estado y código postal como una
sola cadena; de forma automática, la aplicación analiza la cadena única y escribe la información adecuada en la
propiedad correspondiente.
Para simplificar, en este ejemplo se usa una aplicación de consola con una interfaz de línea de comandos.
Ejemplo
using System;
class MainClass
{
static void Main()
{
MailToData MyData = new MailToData();
if (MyData.Validated) {
Console.WriteLine("Name: {0}", MyData.Name);
Console.WriteLine("Address: {0}", MyData.Address);
Console.WriteLine("City: {0}", MyData.City);
Console.WriteLine("State: {0}", MyData.State);
Console.WriteLine("Zip: {0}", MyData.Zip);
// Throw a FormatException if the user did not enter the necessary spaces
// between elements.
try
{
// City may consist of multiple words, so we'll have to parse the
// string from right to left starting with the zip code.
int zipIndex = citystatezip.LastIndexOf(" ");
if (zipIndex == -1) {
msg = "\nCannot identify a zip code." + msgEnd;
throw new FormatException(msg);
}
zip = citystatezip.Substring(zipIndex + 1);
// Put the value of city, state, and zip together in the proper manner.
string MyCityStateZip = String.Concat(city, ", ", state, " ", zip);
return MyCityStateZip;
}
}
Class MainClass
Public Shared Sub Main()
Dim MyData As New MailToData()
If MyData.Validated Then
Console.WriteLine("Name: {0}", MyData.Name)
Console.WriteLine("Address: {0}", MyData.Address)
Console.WriteLine("City: {0}", MyData.City)
Console.WriteLine("State: {0}", MyData.State)
Console.WriteLine("ZIP Code: {0}", MyData.Zip)
' Throw a FormatException if the user did not enter the necessary spaces
' between elements.
Try
' City may consist of multiple words, so we'll have to parse the
' string from right to left starting with the zip code.
Dim zipIndex As Integer = strCityStateZip.LastIndexOf(" ")
If zipIndex = -1 Then
msg = vbCrLf + "Cannot identify a zip code." + msgEnd
msg = vbCrLf + "Cannot identify a zip code." + msgEnd
Throw New FormatException(msg)
End If
strZip = strCityStateZip.Substring(zipIndex + 1)
Cuando se ejecuta el código anterior, se pide al usuario que escriba su nombre y dirección. La aplicación coloca la
información en las propiedades adecuadas y muestra la información al usuario, creando una única cadena que
muestra la información sobre la ciudad, el estado y el código postal.
Vea también
Operaciones básicas de cadenas
Expresiones regulares de .NET
16/09/2020 • 17 minutes to read • Edit Online
Las expresiones regulares proporcionan un método eficaz y flexible para procesar texto. La notación extensa de
coincidencia de patrones de expresiones regulares permite analizar rápidamente grandes cantidades de texto
para lo siguiente:
Buscar patrones concretos de caracteres.
Validar el texto para garantizar que coincide con un patrón predefinido (como una dirección de correo
electrónico).
Extraer, editar, reemplazar o eliminar subcadenas de texto.
Agregar cadenas extraídas en una colección para generar un informe.
Para muchas aplicaciones que usan cadenas o analizan grandes bloques de texto, las expresiones regulares son
una herramienta indispensable.
WARNING
Cuando se usa System.Text.RegularExpressions para procesar entradas que no son de confianza, pase un tiempo de
expiración. Un usuario malintencionado puede proporcionar entradas a RegularExpressions y provocar un ataque por
denegación de servicio. Las API del marco ASP.NET Core en las que se usa RegularExpressions pasan un tiempo de
expiración.
TIP
El espacio de nombres System.Web.RegularExpressions contiene un número de objetos de expresión regular que
implementan modelos de expresión regular predefinidos para el análisis de cadenas a partir de documentos HTML, XML y
ASP.NET. Por ejemplo, la clase TagRegex identifica las etiquetas de inicio en una cadena y la clase CommentRegex identifica
los comentarios de ASP.NET en una cadena.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "(Mr\.? |Mrs\.? |Miss |Ms\.? )"
Dim names() As String = {"Mr. Henry Hunt", "Ms. Sara Samuels", _
"Abraham Adams", "Ms. Nicole Norris"}
For Each name As String In names
Console.WriteLine(Regex.Replace(name, pattern, String.Empty))
Next
End Sub
End Module
' The example displays the following output:
' Henry Hunt
' Sara Samuels
' Abraham Adams
' Nicole Norris
El patrón de expresión regular (Mr\.? |Mrs\.? |Miss |Ms\.? ) busca coincidencias con cualquier aparición de "Mr
", "Mr. ", "Mrs ", "Mrs. ", "Miss ", "Ms " o "Ms. ". La llamada al método Regex.Replace reemplaza la cadena
coincidente con String.Empty; es decir, la quita de la cadena original.
Ejemplo 2: Identificación de palabras duplicadas
Duplicar palabras accidentalmente es un error frecuente que cometen los escritores. Se puede usar una expresión
regular para identificar palabras duplicadas, como se muestra en el ejemplo siguiente.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module modMain
Public Sub Main()
Dim pattern As String = "\b(\w+?)\s\1\b"
Dim input As String = "This this is a nice day. What about this? This tastes good. I saw a a dog."
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
Console.WriteLine("{0} (duplicates '{1}') at position {2}", _
match.Value, match.Groups(1).Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' This this (duplicates 'This') at position 0
' a a (duplicates 'a') at position 66
' Get numeric string, convert it to a value, and add it to List object.
Dim expenses As New List(Of Decimal)
En un equipo cuya referencia cultural actual sea Inglés - Estados Unidos (en-US), el ejemplo crea dinámicamente
la expresión regular \$\s*[-+]?([0-9]{0,3}(,[0-9]{3})*(\.[0-9]+)?) . Este patrón de expresión regular se puede
interpretar de la manera siguiente:
M O DELO IN T ERP RETA C IÓ N
Si se encuentra cada uno de estos subpatrones en la cadena de entrada, la búsqueda de coincidencias se realiza
correctamente y se agrega al objeto Match un objeto MatchCollection que contiene información sobre la
coincidencia.
Temas relacionados
T IT L E DESC RIP C IÓ N
Lenguaje de expresiones regulares: referencia rápida Ofrece información sobre el conjunto de caracteres,
operadores y construcciones que se pueden utilizar para
definir expresiones regulares.
Modelo de objetos de expresión regular Proporciona información y ejemplos de código que muestran
cómo usar las clases de expresiones regulares.
T IT L E DESC RIP C IÓ N
Detalles del comportamiento de expresiones regulares Proporciona información sobre las funcionalidades y el
comportamiento de las expresiones regulares de .NET.
Referencia
System.Text.RegularExpressions
System.Text.RegularExpressions.Regex
Expresiones regulares: referencia rápida (descarga en formato Word)
Expresiones regulares: referencia rápida (descarga en formato PDF)
Lenguaje de expresiones regulares - Referencia
rápida
16/09/2020 • 22 minutes to read • Edit Online
Una expresión regular es un modelo con el que el motor de expresiones regulares intenta buscar una
coincidencia en el texto de entrada. Un modelo consta de uno o más literales de carácter, operadores o
estructuras. Para obtener una breve introducción, consulte Expresiones regulares de .NET.
Cada sección de esta referencia rápida enumera una categoría determinada de caracteres, operadores y
construcciones que puede usar para definir expresiones regulares.
Esta información también se proporciona en dos formatos que se pueden descargar e imprimir para facilitar su
consulta:
Descargar en formato Word (.docx)
Descarga en formato PDF (.pdf)
Escapes de carácter
El carácter de barra diagonal inversa (\) en una expresión regular indica que el carácter que le sigue es un
carácter especial (como se muestra en la tabla siguiente) o que se debe interpretar literalmente. Para más
información, consulte Escapes de carácter.
Clases de caracteres
Una clase de caracteres coincide con cualquiera de un juego de caracteres. Las clases de caracteres incluyen los
elementos del lenguaje enumerados en la tabla siguiente. Para más información, consulte Clases de caracteres.
Delimitadores
Los delimitadores, o aserciones atómicas de ancho cero, hacen que una coincidencia tenga éxito o no
dependiendo de la posición actual en la cadena, pero no hacen que el motor avance por la cadena ni consuma
caracteres. Los metacaracteres enumerados en la tabla siguiente son delimitadores. Para obtener más
información, consulte Delimitadores.
Construcciones de agrupamiento
Las construcciones de agrupamiento definen subexpresiones de una expresión regular y, normalmente,
capturan subcadenas de una cadena de entrada. Las construcciones de agrupamiento incluyen los elementos del
lenguaje enumerados en la tabla siguiente. Para obtener más información, consulte Construcciones de
agrupamiento.
C O N ST RUC C IÓ N DE
A GRUPA M IEN TO DESC RIP C IÓ N M O DELO C O IN C IDEN C IA S
o con nombre.
(?' nombre '
subexpresión )
"Write" en
"Console.Write(value)"
Cuantificadores
Un cuantificador especifica cuántas instancias del elemento anterior (que puede ser un carácter, un grupo o una
clase de caracteres) debe haber en la cadena de entrada para que se encuentre una coincidencia. Los
cuantificadores incluyen los elementos del lenguaje enumerados en la tabla siguiente. Para obtener más
información, consulte Cuantificadores.
C O N ST RUC C IÓ N DE
REF EREN C IA S IN VERSA S DESC RIP C IÓ N M O DELO C O IN C IDEN C IA S
Construcciones de alternancia
Las estructuras de alternancia modifican una expresión regular para habilitar o no la coincidencia. Estas
construcciones incluyen los elementos del lenguaje enumerados en la tabla siguiente. Para obtener más
información, consulte Construcciones de alternancia.
C O N ST RUC C IO N ES DE
A LT ERN A N C IA DESC RIP C IÓ N M O DELO C O IN C IDEN C IA S
C O N ST RUC C IO N ES DE
A LT ERN A N C IA DESC RIP C IÓ N M O DELO C O IN C IDEN C IA S
Sustituciones
Las sustituciones son elementos del lenguaje de expresiones regulares que se admiten en modelos de
reemplazo. Para obtener más información, consulte Substituciones. Los metacaracteres enumerados en la tabla
siguiente son aserciones atómicas de ancho cero.
Construcciones misceláneas
Las estructuras misceláneas modifican un modelo de expresión regular o proporcionan información sobre él. En
la tabla siguiente se enumeran las construcciones misceláneas admitidas por .NET. Para obtener más
información, consulte Construcciones misceláneas.
Vea también
System.Text.RegularExpressions
System.Text.RegularExpressions.Regex
Expresiones regulares
Clases de expresiones regulares
Expresiones regulares: referencia rápida (descarga en formato Word)
Expresiones regulares: referencia rápida (descarga en formato PDF)
Escapes de carácter en expresiones regulares
16/09/2020 • 8 minutes to read • Edit Online
La barra diagonal inversa (\) en una expresión regular indica una de las siguientes situaciones:
El carácter que va detrás de ella es un carácter especial, como se muestra en la tabla de la sección siguiente.
Por ejemplo, \b es un delimitador que indica que una coincidencia de expresión regular debería comenzar
en un límite de palabras, \t representa un carácter de tabulación y \x020 representa un espacio.
Un carácter que de otro modo se interpretaría como una construcción de lenguaje sin escape, se debe
interpretar literalmente. Por ejemplo, una llave ( { ) inicia la definición de un cuantificador, pero una barra
diagonal inversa seguida de una llave ( \{ ) indica que el motor de expresiones regulares debería coincidir
con la llave. De igual forma, una sola barra diagonal inversa marca el principio de una construcción de
lenguaje con escape, pero dos barras diagonales inversas ( \\ ) indican que el motor de expresiones
regulares debería coincidir con la barra diagonal inversa.
NOTE
Los escapes de caracteres se reconocen en los patrones de expresiones regulares, pero no en los patrones de reemplazo.
Todos los caracteres excepto los siguientes: Los caracteres que no aparecen en la columna Carácter o
secuencia no tienen ningún significado especial en las
.$^{[(|)*+?\ expresiones regulares, sino que equivalen a sí mismos.
Un ejemplo
En el ejemplo siguiente se muestra el uso de escapes de carácter en una expresión regular. Analiza una cadena que
contiene los nombres de las ciudades más grandes del mundo y sus poblaciones en 2009. Cada nombre de ciudad
se separa de su población por un carácter de tabulación ( \t ) o una barra vertical (| o \u007c ). Cada ciudad y su
población está separada de la siguiente por un retorno de carro y un avance de línea.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim delimited As String = "\G(.+)[\t\u007c](.+)\r?\n"
Dim input As String = "Mumbai, India|13,922,125" + vbCrLf + _
"Shanghai, China" + vbTab + "13,831,900" + vbCrLf + _
"Karachi, Pakistan|12,991,000" + vbCrLf + _
"Delhi, India" + vbTab + "12,259,230" + vbCrLf + _
"Istanbul, Turkey|11,372,613" + vbCrLf
Console.WriteLine("Population of the World's Largest Cities, 2009")
Console.WriteLine()
Console.WriteLine("{0,-20} {1,10}", "City", "Population")
Console.WriteLine()
For Each match As Match In Regex.Matches(input, delimited)
Console.WriteLine("{0,-20} {1,10}", match.Groups(1).Value, _
match.Groups(2).Value)
Next
End Sub
End Module
' The example displays the following output:
' Population of the World's Largest Cities, 2009
'
' City Population
'
' Mumbai, India 13,922,125
' Shanghai, China 13,831,900
' Karachi, Pakistan 12,991,000
' Delhi, India 12,259,230
' Istanbul, Turkey 11,372,613
Vea también
Lenguaje de expresiones regulares: referencia rápida
Clases de caracteres en expresiones regulares
16/09/2020 • 58 minutes to read • Edit Online
Una clase de caracteres define un conjunto de caracteres, cualquiera de los cuales puede estar en una cadena de
entrada para que se produzca una coincidencia. El lenguaje de expresiones regulares de .NET admite las
siguientes clases de caracteres:
Grupos de caracteres positivos. Un carácter de la cadena de entrada debe coincidir con uno de los
caracteres del conjunto especificado. Para obtener más información, consulte Grupo de caracteres
positivos.
Grupos de caracteres negativos. Ningún carácter de la cadena de entrada debe coincidir con ninguno de
los caracteres del conjunto especificado. Para obtener más información, consulte Grupo de caracteres
negativos.
Cualquier carácter. El carácter . (punto) en una expresión regular es un carácter comodín que coincide
con cualquier carácter excepto con \n . Para obtener más información, consulte Cualquier carácter.
Una categoría general o un bloque con nombre Unicode. Para que se produzca una coincidencia, un
carácter de la cadena de entrada debe ser miembro de una categoría Unicode determinada o debe estar
dentro de un intervalo contiguo de caracteres Unicode. Para obtener más información, consulte Categoría
Unicode o bloque Unicode.
Un bloque con nombre o una categoría general negativa Unicode. Para que se produzca una coincidencia,
un carácter de la cadena de entrada no debe ser miembro de una categoría Unicode determinada o no
debe estar dentro de un intervalo contiguo de caracteres Unicode. Para obtener más información, consulte
Categoría Unicode o bloque Unicode negativo.
Un carácter de palabra. Un carácter de la cadena de entrada puede pertenecer a cualquiera de las
categorías Unicode que son adecuadas para los caracteres que se usan para formar palabras. Para obtener
más información, consulte Carácter de palabra.
Un carácter que no se usa en las palabras. Un carácter de la cadena de entrada puede pertenecer a
cualquier categoría Unicode que no se usa para formar palabras. Para obtener más información, consulte
Carácter que no se usa en las palabras.
Un carácter de espacio en blanco. Un carácter de la cadena de entrada puede ser cualquiera de los
caracteres separadores Unicode, así como cualquiera de los caracteres de una serie de caracteres de
control. Para obtener más información, consulte Carácter de espacio en blanco.
Un carácter que no sea un espacio en blanco. Un carácter de la cadena de entrada puede ser cualquier
carácter que no sea un espacio en blanco. Para obtener más información, consulte Carácter que no sea un
espacio en blanco.
Un dígito decimal. Un carácter de la cadena de entrada puede ser cualquiera de los caracteres clasificados
como dígitos decimales de Unicode. Para obtener más información, consulte Carácter de dígito decimal.
Un carácter que no sea un dígito decimal. Un carácter de la cadena de entrada puede ser cualquier
carácter que no sea un dígito decimal de Unicode. Para obtener más información, consulte Carácter de
dígito decimal.
.NET admite expresiones de sustracción de clases de caracteres, que permiten definir un conjunto de caracteres
como el resultado de excluir una clase de caracteres de otra clase de caracteres. Para obtener más información,
consulte Sustracción de clases de caracteres.
NOTE
Las clases que coinciden con los caracteres por categoría, como \w para que coincidan con caracteres alfabéticos o \p{} para
que coincidan con una categoría Unicode, que se basan en la clase CharUnicodeInfo para proporcionar información sobre
las categorías de caracteres. A partir de .NET Framework 4.6.2, las categorías de caracteres se basan en el estándar Unicode,
versión 8.0.0. Desde .NET Framework 4 hasta .NET Framework 4.6.1, se basan en el estándar Unicode, versión 6.3.0.
donde grupo_caracteres es una lista de cada uno de los caracteres que pueden aparecer en la cadena de entrada
para que se produzca una coincidencia. grupo_caracteres puede estar formado por cualquier combinación de
uno o varios caracteres literales, caracteres de escape o clases de caracteres.
La sintaxis para especificar un intervalo de caracteres es la siguiente:
[firstCharacter-lastCharacter]
donde firstCharacter es el carácter que comienza el intervalo y lastCharacter es el carácter final del intervalo. Un
intervalo de caracteres es una serie contigua de caracteres que se define especificando el primer carácter de la
serie, un guion (-) y, a continuación, el último carácter de la serie. Dos caracteres son contiguos si tienen puntos
de código Unicode adyacentes. firstCharacter debe ser el carácter con el punto de código inferior y lastCharacter
debe ser el carácter con el punto de código superior.
NOTE
Dado que un grupo de caracteres positivos puede incluir un conjunto de caracteres y un rango de caracteres, un carácter
de guión ( - ) siempre se interpreta como el separador de rango, a menos que sea el primer carácter del grupo o el último.
En la tabla siguiente se recogen algunos de los patrones de expresiones regulares comunes que contienen clases
de caracteres positivos.
En el ejemplo siguiente se define un grupo de caracteres positivos que contiene los caracteres "a" y "e" de
manera que la cadena de entrada deba contener las palabras "grey" o "gray" seguidas de cualquier otra palabra
para que se produzca una coincidencia.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "gr[ae]y\s\S+?[\s\p{P}]"
Dim input As String = "The gray wolf jumped over the grey wall."
Dim matches As MatchCollection = Regex.Matches(input, pattern)
For Each match As Match In matches
Console.WriteLine($"'{match.Value}'")
Next
End Sub
End Module
' The example displays the following output:
' 'gray wolf '
' 'grey wall.'
En el ejemplo siguiente se buscan palabras que comienzan por cualquier letra mayúscula. Utiliza la subexpresión
[A-Z] para representar el intervalo de letras mayúsculas de la A a la Z.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b[A-Z]\w*\b"
Dim input As String = "A city Albany Zulu maritime Marseilles"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(match.Value)
Next
End Sub
End Module
donde grupo_caracteres es una lista de cada uno de los caracteres que no pueden aparecer en la cadena de
entrada para que se produzca una coincidencia. grupo_caracteres puede estar formado por cualquier
combinación de uno o varios caracteres literales, caracteres de escape o clases de caracteres.
La sintaxis para especificar un intervalo de caracteres es la siguiente:
[^*firstCharacter*-*lastCharacter*]
donde firstCharacter es el carácter que comienza el intervalo y lastCharacter es el carácter final del intervalo. Un
intervalo de caracteres es una serie contigua de caracteres que se define especificando el primer carácter de la
serie, un guion (-) y, a continuación, el último carácter de la serie. Dos caracteres son contiguos si tienen puntos
de código Unicode adyacentes. firstCharacter debe ser el carácter con el punto de código inferior y lastCharacter
debe ser el carácter con el punto de código superior.
NOTE
Dado que un grupo de caracteres negativos puede incluir un conjunto de caracteres y un rango de caracteres, un carácter
de guión ( - ) siempre se interpreta como el separador de rango, a menos que sea el primer carácter del grupo o el último.
Es posible concatenar dos o más intervalos de caracteres. Por ejemplo, para especificar el intervalo de dígitos
decimales del "0" al "9", el intervalo de letras minúsculas de la "a" a la "f" y el intervalo de letras mayúsculas de la
"A" a la "F", utilice [0-9a-fA-F] .
El carácter inicial de acento circunflejo ( ^ ) de un grupo de caracteres negativos es obligatorio e indica que el
grupo de caracteres es un grupo de caracteres negativos en lugar de un grupo de caracteres positivos.
IMPORTANT
Un grupo de caracteres negativos dentro de un patrón de expresión regular más grande no es una aserción de ancho cero.
Es decir, después de evaluar el grupo de caracteres negativos, el motor de expresiones regulares avanza un carácter en la
cadena de entrada.
En la tabla siguiente se recogen algunos de los patrones de expresiones regulares comunes que contienen
grupos de caracteres negativos.
En el ejemplo siguiente se busca cualquier palabra que comience por los caracteres "th" y no vaya seguida de
una "o".
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\bth[^o]\w+\b"
Dim input As String = "thought thing though them through thus " + _
"thorough this"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(match.Value)
Next
End Sub
End Module
' The example displays the following output:
' thing
' them
' through
' thus
' this
Cualquier carácter: .
El carácter de punto (.) coincide con cualquier carácter excepto con \n (carácter de nueva línea, \u000A), con los
dos requisitos siguientes:
Si la opción RegexOptions.Singleline modifica un patrón de expresión regular o si la opción . modifica la
parte del patrón que contiene la clase de caracteres s , . coincide con cualquier carácter. Para obtener
más información, consulte Opciones de expresiones regulares.
El ejemplo siguiente muestra el comportamiento predeterminado de la clase de caracteres . y con la
opción RegexOptions.Singleline. La expresión regular ^.+ comienza en el principio de la cadena y
coincide con todos los caracteres. De forma predeterminada, la coincidencia termina al final de la primera
línea; el patrón de la expresión regular coincide con el carácter de retorno de carro, \r o \u000D, pero no
coincide con \n . Dado que la opción RegexOptions.Singleline interpreta la cadena de entrada completa
como una sola línea, coincide con cada carácter de la cadena de entrada, incluido \n .
using System;
using System.Text.RegularExpressions;
Console.WriteLine();
foreach (Match match in Regex.Matches(input, pattern, RegexOptions.Singleline))
Console.WriteLine(Regex.Escape(match.Value));
}
}
// The example displays the following output:
// This\ is\ one\ line\ and\r
//
// This\ is\ one\ line\ and\r\nthis\ is\ the\ second\.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "^.+"
Dim input As String = "This is one line and" + vbCrLf + "this is the second."
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(Regex.Escape(match.Value))
Next
Console.WriteLine()
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.SingleLine)
Console.WriteLine(Regex.Escape(match.Value))
Next
End Sub
End Module
' The example displays the following output:
' This\ is\ one\ line\ and\r
'
' This\ is\ one\ line\ and\r\nthis\ is\ the\ second\.
NOTE
Dado que coincide con cualquier carácter excepto con \n , la clase de caracteres . también coincide con \r (el carácter
de retorno de carro, \u000D).
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As STring = "\b.*[.?!;:](\s|\z)"
Dim input As String = "this. what: is? go, thing."
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(match.Value)
Next
End Sub
End Module
' The example displays the following output:
' this. what: is? go, thing.
NOTE
Dado que coincide con cualquier carácter, el elemento del lenguaje . se utiliza a menudo con un cuantificador no
expansivo si un patrón de expresión regular intenta coincidir varias veces con cualquier carácter. Para obtener más
información, consulte Cuantificadores.
coincide con cualquier carácter que pertenezca a una categoría general o bloque con nombre de Unicode, donde
nombre es la abreviatura de la categoría o el nombre del bloque con nombre. Para obtener una lista de
abreviaturas de categorías, consulte la sección Categorías generales Unicode compatibles más adelante en este
tema. Para obtener una lista de bloques con nombre, consulte la sección Bloques con nombre compatibles más
adelante en este tema.
En el ejemplo siguiente se usa la construcción \p{ nombre } para buscar coincidencias con una categoría
general de Unicode (en este caso, Pd o Punctuation, Dash) y un bloque con nombre (los bloques con nombre
IsGreek e IsBasicLatin ).
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b(\p{IsGreek}+(\s)?)+\p{Pd}\s(\p{IsBasicLatin}+(\s)?)+"
Dim input As String = "Κατα Μαθθαίον - The Gospel of Matthew"
coincide con cualquier carácter que no pertenezca a una categoría general o bloque con nombre de Unicode,
donde nombre es la abreviatura de la categoría o el nombre del bloque con nombre. Para obtener una lista de
abreviaturas de categorías, consulte la sección Categorías generales Unicode compatibles más adelante en este
tema. Para obtener una lista de bloques con nombre, consulte la sección Bloques con nombre compatibles más
adelante en este tema.
En el siguiente ejemplo se usa la construcción \P{ nombre } para quitar cualquier símbolo de divisa (en este
caso, la categoría Sc , o Symbol, Currency) de las cadenas numéricas.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "(\P{Sc})+"
El patrón de expresión regular (\P{Sc})+ coincide con uno o varios caracteres que no son símbolos de divisa;
quita eficazmente cualquier símbolo de divisa de la cadena de resultado.
Carácter de palabra: \w
\w coincide con cualquier carácter de palabra. Un carácter de palabra es un miembro de alguna de las
categorías Unicode enumeradas en la tabla siguiente.
Ll Letra, minúscula
Lu Letra, mayúscula
Lo Letra, otra
Lm Letra, modificador
NOTE
Dado que coincide con cualquier carácter de palabra, el elemento del lenguaje \w se suele usar con un cuantificador
diferido si un patrón de expresión regular intenta coincidir varias veces con cualquier carácter de palabra, seguido de un
carácter de palabra específico. Para obtener más información, consulte Cuantificadores.
En el ejemplo siguiente se usa el elemento del lenguaje \w para buscar coincidencias de caracteres duplicados
en una palabra. El ejemplo define un patrón de expresión regular, (\w)\1 , que se puede interpretar de la
siguiente manera.
EL EM EN TO DESC RIP C IÓ N
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "(\w)\1"
Dim words() As String = {"trellis", "seer", "latter", "summer", _
"hoarse", "lesser", "aardvark", "stunned"}
For Each word As String In words
Dim match As Match = Regex.Match(word, pattern)
If match.Success Then
Console.WriteLine("'{0}' found in '{1}' at position {2}.", _
match.Value, word, match.Index)
Else
Console.WriteLine("No double characters in '{0}'.", word)
End If
Next
End Sub
End Module
' The example displays the following output:
' 'll' found in 'trellis' at position 3.
' 'ee' found in 'seer' at position 1.
' 'tt' found in 'latter' at position 2.
' 'mm' found in 'summer' at position 2.
' No double characters in 'hoarse'.
' 'ss' found in 'lesser' at position 2.
' 'aa' found in 'aardvark' at position 0.
' 'nn' found in 'stunned' at position 3.
En otras palabras, coincide con cualquier carácter excepto con los que figuran en las categorías Unicode de la
tabla siguiente.
Ll Letra, minúscula
Lu Letra, mayúscula
Lo Letra, otra
Lm Letra, modificador
NOTE
Dado que coincide con cualquier carácter que no sea de palabra, el elemento del lenguaje \W se suele usar con un
cuantificador diferido si un patrón de expresión regular intenta coincidir varias veces con cualquier carácter que no sea de
palabra, seguido de un carácter que no sea de palabra específico. Para obtener más información, consulte Cuantificadores.
EL EM EN TO DESC RIP C IÓ N
(\W){1,2} Coincide una o dos veces con un carácter que no se usa para
formar palabras. Este es el segundo grupo de captura.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "\b(\w+)(\W){1,2}"
Dim input As String = "The old, grey mare slowly walked across the narrow, green pasture."
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(match.Value)
Console.Write(" Non-word character(s):")
Dim captures As CaptureCollection = match.Groups(2).Captures
For ctr As Integer = 0 To captures.Count - 1
Console.Write("'{0}' (\u{1}){2}", captures(ctr).Value, _
Convert.ToUInt16(captures(ctr).Value.Chars(0)).ToString("X4"), _
If(ctr < captures.Count - 1, ", ", ""))
Next
Console.WriteLine()
Next
End Sub
End Module
' The example displays the following output:
' The
' Non-word character(s):' ' (\u0020)
' old,
' Non-word character(s):',' (\u002C), ' ' (\u0020)
' grey
' Non-word character(s):' ' (\u0020)
' mare
' Non-word character(s):' ' (\u0020)
' slowly
' Non-word character(s):' ' (\u0020)
' walked
' Non-word character(s):' ' (\u0020)
' across
' Non-word character(s):' ' (\u0020)
' the
' Non-word character(s):' ' (\u0020)
' narrow,
' Non-word character(s):',' (\u002C), ' ' (\u0020)
' green
' Non-word character(s):' ' (\u0020)
' pasture.
' Non-word character(s):'.' (\u002E)
Dado que el objeto Group del segundo grupo de captura contiene solo un carácter que no se usa para formar
palabras, el ejemplo recupera todos los caracteres que no se usan para formar palabras capturados del objeto
CaptureCollection que devuelve la propiedad Group.Captures.
EL EM EN TO DESC RIP C IÓ N
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "\b\w+(e)?s(\s|$)"
Dim input As String = "matches stores stops leave leaves"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(match.Value)
Next
End Sub
End Module
' The example displays the following output:
' matches
' stores
' stops
' leaves
EL EM EN TO DESC RIP C IÓ N
Module Example
Public Sub Main()
Dim pattern As String = "\b(\S+)\s?"
Dim input As String = "This is the first sentence of the first paragraph. " + _
"This is the second sentence." + vbCrLf + _
"This is the only sentence of the second paragraph."
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(match.Groups(1))
Next
End Sub
End Module
' The example displays the following output:
' This
' is
' the
' first
' sentence
' of
' the
' first
' paragraph.
' This
' is
' the
' second
' sentence.
' This
' is
' the
' only
' sentence
' of
' the
' second
' paragraph.
EL EM EN TO DESC RIP C IÓ N
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "^(\(?\d{3}\)?[\s-])?\d{3}-\d{4}$"
Dim inputs() As String = {"111 111-1111", "222-2222", "222 333-444", _
"(212) 111-1111", "111-AB1-1111", _
"212-111-1111", "01 999-9999"}
EL EM EN TO DESC RIP C IÓ N
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "^\D\d{1,5}\D*$"
Dim inputs() As String = {"A1039C", "AA0001", "C18A", "Y938518"}
Lu Letra, mayúscula
Ll Letra, minúscula
Lm Letra, modificador
C AT EGO RÍA DESC RIP C IÓ N
Lo Letra, otra
Me Marca, inclusión
Nl Número, letra
No Número, otro
Pc Puntuación, conector
Pd Puntuación, raya
Ps Puntuación, abrir
Pe Puntuación, cerrar
Po Puntuación, otro
Sm Símbolo, matemático
Sc Símbolo, divisa
Sk Símbolo, modificador
So Símbolo, otro
C AT EGO RÍA DESC RIP C IÓ N
Zs Separador, espacio
Zl Separador, línea
Zp Separador, párrafo
Cc Otro, control
Cf Otro, formato
Cs Otro, suplente
Puede determinar la categoría Unicode de cualquier carácter concreto pasando dicho carácter al método
GetUnicodeCategory. En el ejemplo siguiente se utiliza el método GetUnicodeCategory para determinar la
categoría de cada elemento de una matriz que contiene determinados caracteres latinos.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim chars() As Char = {"a"c, "X"c, "8"c, ","c, " "c, ChrW(9), "!"c}
o bien
IsGreekandCoptic
o bien
IsCombiningMarksforSymbols
Elija las clases de caracteres para que una expresión de sustracción de clases de caracteres produzca resultados
satisfactorios. Evite el uso de expresiones que produzcan un conjunto vacío de caracteres, que no coincidan con
nada, o expresiones equivalentes al grupo base original. Por ejemplo, el conjunto vacío es el resultado de la
expresión [\p{IsBasicLatin}-[\x00-\x7F]] , que resta todos los caracteres del intervalo de caracteres
IsBasicLatin de la categoría general IsBasicLatin . Ocurre lo mismo con el grupo base original, que es el
resultado de la expresión [a-z-[0-9]] . Esto se debe a que el grupo base, que está formado por el intervalo de
caracteres comprendido entre las letras de la "a" a la "z", no contiene ningún carácter del grupo excluido, que es
el intervalo de caracteres formado por los dígitos decimales del "0" al "9".
En el ejemplo siguiente se define una expresión regular, ^[0-9-[2468]]+$ , que coincide con cero y con los dígitos
impares de una cadena de entrada. La expresión regular se interpreta como se muestra en la tabla siguiente.
EL EM EN TO DESC RIP C IÓ N
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim inputs() As String = {"123", "13579753", "3557798", "335599901"}
Dim pattern As String = "^[0-9-[2468]]+$"
Vea también
GetUnicodeCategory
Lenguaje de expresiones regulares: referencia rápida
Opciones de expresiones regulares
Delimitadores en expresiones regulares
16/09/2020 • 31 minutes to read • Edit Online
Los delimitadores, o aserciones atómicas de ancho cero, especifican la posición de la cadena en que se debe
producir una coincidencia. Cuando se usa un delimitador en una expresión de búsqueda, el motor de expresiones
regulares no avanza por la cadena o ni consume caracteres, sino que solo busca una coincidencia en la posición
especificada. Por ejemplo, ^ especifica que la coincidencia debe empezar al principio de una cadena o línea. Por
consiguiente, la expresión regular ^http: coincide con "http": solo cuando se encuentra al principio de una línea.
En la tabla siguiente, se enumeran los delimitadores que admiten las expresiones regulares de .NET.
using System;
using System.Text.RegularExpressions;
Console.WriteLine(".");
match = match.NextMatch();
}
Console.WriteLine();
Console.WriteLine(".");
match = match.NextMatch();
}
Console.WriteLine();
}
}
// The example displays the following output:
// The Brooklyn Dodgers played in the National League in 1911, 1912, 1932-1957.
//
// The Brooklyn Dodgers played in the National League in 1911, 1912, 1932-1957.
// The Chicago Cubs played in the National League in 1903-present.
// The Detroit Tigers played in the American League in 1901-present.
// The New York Giants played in the National League in 1885-1957.
// The Washington Senators played in the American League in 1901-1960.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "Brooklyn Dodgers, National League, 1911, 1912, 1932-1957" + vbCrLf +
"Chicago Cubs, National League, 1903-present" + vbCrLf +
"Detroit Tigers, American League, 1901-present" + vbCrLf +
"New York Giants, National League, 1885-1957" + vbCrLf +
"Washington Senators, American League, 1901-1960" + vbCrLf
((\w+(\s?)){2,} Coincide con uno o varios caracteres que se usan para formar
palabras seguidos de cero o un espacio, al menos dos veces.
Este es el primer grupo de captura. Esta expresión también
define un segundo y tercer grupo de captura: El segundo
grupo está compuesto por la palabra capturada y el tercero
consta de los espacios en blanco capturados.
M O DELO DESC RIP C IÓ N
(\w+\s\w+) Coincide con uno o varios caracteres que se usan para formar
palabras seguidos de un espacio, seguidos de uno o varios
caracteres que se usan para formar palabras. Este es el cuarto
grupo de captura.
Si usa $ con la opción RegexOptions.Multiline , la coincidencia también se puede producir al final de una línea.
Observe que $ coincide con \n , pero no coincide con \r\n (la combinación de caracteres de retorno de carro
y nueva línea, o CR/LF). Para buscar la combinación de caracteres CR/LF, incluya \r?$ en el patrón de expresión
regular.
En el ejemplo siguiente se agrega el delimitador $ al patrón de expresión regular usado en el ejemplo de la
sección Principio de cadena o línea . Cuando se usa con la cadena de entrada original, que incluye cinco líneas de
texto, el método Regex.Matches(String, String) no puede encontrar una coincidencia, porque el final de la primera
línea no coincide con el patrón $ . Cuando la cadena de entrada original se divide en una matriz de cadenas, el
método Regex.Matches(String, String) consigue encontrar coincidencias en cada una de las cinco líneas. Cuando se
llama al método Regex.Matches(String, String, RegexOptions) con el parámetro options establecido en
RegexOptions.Multiline, no se encuentra ninguna coincidencia porque el patrón de la expresión regular no tiene
en cuenta el elemento de retorno de carro (\u+000D). Sin embargo, cuando el patrón de la expresión regular se
modifica al remplazar $ por \r?$ , si se llama al método Regex.Matches(String, String, RegexOptions) con el
parámetro options establecido en RegexOptions.Multiline , ahora encuentra cinco coincidencias.
using System;
using System.Text.RegularExpressions;
Console.WriteLine(".");
match = match.NextMatch();
}
Console.WriteLine();
Console.WriteLine(".");
match = match.NextMatch();
}
Console.WriteLine();
Console.WriteLine(".");
match = match.NextMatch();
}
Console.WriteLine();
}
}
// The example displays the following output:
// Attempting to match the entire input string:
//
// Attempting to match each element in a string array:
// The Brooklyn Dodgers played in the National League in 1911, 1912, 1932-1957.
// The Chicago Cubs played in the National League in 1903-present.
// The Detroit Tigers played in the American League in 1901-present.
// The New York Giants played in the National League in 1885-1957.
// The Washington Senators played in the American League in 1901-1960.
//
// Attempting to match each line of an input string with '$':
//
// Attempting to match each line of an input string with '\r?$':
// The Brooklyn Dodgers played in the National League in 1911, 1912, 1932-1957.
// The Chicago Cubs played in the National League in 1903-present.
// The Detroit Tigers played in the American League in 1901-present.
// The New York Giants played in the National League in 1885-1957.
// The Washington Senators played in the American League in 1901-1960.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "Brooklyn Dodgers, National League, 1911, 1912, 1932-1957" + vbCrLf +
"Chicago Cubs, National League, 1903-present" + vbCrLf +
"Detroit Tigers, American League, 1901-present" + vbCrLf +
"New York Giants, National League, 1885-1957" + vbCrLf +
"Washington Senators, American League, 1901-1960" + vbCrLf
match = match.NextMatch()
Loop
Console.WriteLine()
End Sub
End Module
' The example displays the following output:
' Attempting to match the entire input string:
'
' Attempting to match each element in a string array:
' The Brooklyn Dodgers played in the National League in 1911, 1912, 1932-1957.
' The Chicago Cubs played in the National League in 1903-present.
' The Detroit Tigers played in the American League in 1901-present.
' The New York Giants played in the National League in 1885-1957.
' The Washington Senators played in the American League in 1901-1960.
'
' Attempting to match each line of an input string with '$':
'
' Attempting to match each line of an input string with '\r?$':
' The Brooklyn Dodgers played in the National League in 1911, 1912, 1932-1957.
' The Chicago Cubs played in the National League in 1903-present.
' The Detroit Tigers played in the American League in 1901-present.
' The New York Giants played in the National League in 1885-1957.
' The Washington Senators played in the American League in 1901-1960.
Console.WriteLine(".");
match = match.NextMatch();
}
Console.WriteLine();
}
}
// The example displays the following output:
// The Brooklyn Dodgers played in the National League in 1911, 1912, 1932-1957.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "Brooklyn Dodgers, National League, 1911, 1912, 1932-1957" + vbCrLf +
"Chicago Cubs, National League, 1903-present" + vbCrLf +
"Detroit Tigers, American League, 1901-present" + vbCrLf +
"New York Giants, National League, 1885-1957" + vbCrLf +
"Washington Senators, American League, 1901-1960" + vbCrLf
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim inputs() As String = {"Brooklyn Dodgers, National League, 1911, 1912, 1932-1957",
"Chicago Cubs, National League, 1903-present" + vbCrLf,
"Detroit Tigers, American League, 1901-present" + vbLf,
"New York Giants, National League, 1885-1957",
"Washington Senators, American League, 1901-1960" + vbCrLf}
Dim pattern As String = "^((\w+(\s?)){2,}),\s(\w+\s\w+),(\s\d{4}(-(\d{4}|present))?,?)+\r?\Z"
Module Example
Public Sub Main()
Dim inputs() As String = {"Brooklyn Dodgers, National League, 1911, 1912, 1932-1957",
"Chicago Cubs, National League, 1903-present" + vbCrLf,
"Detroit Tigers, American League, 1901-present" + vbLf,
"New York Giants, National League, 1885-1957",
"Washington Senators, American League, 1901-1960" + vbCrLf}
Dim pattern As String = "^((\w+(\s?)){2,}),\s(\w+\s\w+),(\s\d{4}(-(\d{4}|present))?,?)+\r?\z"
Coincidencias contiguas: \G
El delimitador \G especifica que debe producirse una coincidencia en el punto en el que finalizó la coincidencia
anterior. El uso de este delimitador con el método Regex.Matches o Match.NextMatch permite asegurarse de que
todas las coincidencias son contiguas.
En el ejemplo siguiente se usa una expresión regular para extraer los nombres de especies de roedores de una
cadena delimitada por comas.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "capybara,squirrel,chipmunk,porcupine,gopher," +
"beaver,groundhog,hamster,guinea pig,gerbil," +
"chinchilla,prairie dog,mouse,rat"
Dim pattern As String = "\G(\w+\s?\w*),?"
Dim match As Match = Regex.Match(input, pattern)
Do While match.Success
Console.WriteLine(match.Groups(1).Value)
match = match.NextMatch()
Loop
End Sub
End Module
' The example displays the following output:
' capybara
' squirrel
' chipmunk
' porcupine
' gopher
' beaver
' groundhog
' hamster
' guinea pig
' gerbil
' chinchilla
' prairie dog
' mouse
' rat
La expresión regular \G(\w+\s?\w*),? se interpreta como se muestra en la tabla siguiente.
(\w+\s?\w*) Coincide con uno o varios caracteres que se usan para formar
palabras seguidos de cero o un espacio, seguidos de cero o
más caracteres que se usan para formar palabras. Este es el
primer grupo de captura.
Límite de palabras: \b
El delimitador \b especifica que la coincidencia se debe producir en un límite entre un carácter que se usa para
formar palabras (el elemento del lenguaje \w ) y un carácter que no se usa para formar palabras (el elemento del
lenguaje \W ). Los caracteres que se usan para formar palabras son los caracteres alfanuméricos y de subrayado;
un carácter que no se usa para formar palabras es cualquier carácter que no es alfanumérico ni de subrayado.
(Para más información, vea Clases de carácter). La coincidencia también se puede producir en un límite de
palabras al principio o al final de la cadena.
El delimitador \b se usa con frecuencia para asegurarse de que una subexpresión coincide con una palabra
completa en lugar de solo con el principio o el final de una palabra. La expresión regular \bare\w*\b del ejemplo
siguiente muestra este uso. Coincide con cualquier palabra que comience por la subcadena "are". El resultado del
ejemplo también muestra que \b coincide tanto con el principio como con el final de la cadena de entrada.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim input As String = "area bare arena mare"
Dim pattern As String = "\bare\w*\b"
Console.WriteLine("Words that begin with 'are':")
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("'{0}' found at position {1}",
match.Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' Words that begin with 'are':
' 'area' found at position 0
' 'arena' found at position 10
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim input As String = "equity queen equip acquaint quiet"
Dim pattern As String = "\Bqu\w+"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("'{0}' found at position {1}",
match.Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' 'quity' found at position 1
' 'quip' found at position 14
' 'quaint' found at position 21
Vea también
Lenguaje de expresiones regulares: referencia rápida
Opciones de expresiones regulares
Construcciones de agrupamiento en expresiones
regulares
16/09/2020 • 66 minutes to read • Edit Online
Las construcciones de agrupamiento definen las subexpresiones de una expresión regular y capturan las
subcadenas de una cadena de entrada. Puede utilizar construcciones de agrupamiento para hacer lo siguiente:
Buscar una subexpresión que se repite en la cadena de entrada.
Aplicar un cuantificador a una subexpresión que tiene varios elementos del lenguaje de expresiones
regulares. Para más información sobre los cuantificadores, vea Quantifiers.
Incluir una subexpresión en la cadena devuelta por los métodos Regex.Replace y Match.Result .
Recuperar subexpresiones individuales de la propiedad Match.Groups y procesarlas por separado del
texto coincidente en su conjunto.
En la tabla siguiente se enumeran las construcciones de agrupamiento admitidas por el motor de expresiones
regulares de .NET y se indica si son de captura o sin captura.
Para obtener información sobre los grupos y el modelo de objetos de expresiones regulares, vea
Construcciones de agrupamiento y objetos de las expresiones regulares.
Subexpresiones coincidentes
La construcción de agrupación siguiente captura una subexpresión coincidente:
( subexpresión )
donde subexpresión es cualquier patrón de expresión regular válido. Las capturas que usan paréntesis se
numeran automáticamente de izquierda a derecha según el orden de los paréntesis de apertura de la
expresión regular, empezando desde uno. La captura con el número cero es el texto coincidente con el patrón
de la expresión regular completa.
NOTE
De manera predeterminada, el elemento de lenguaje ( subexpresión ) captura la subexpresión coincidente. No
obstante, la subexpresión coincidente no se captura si el parámetro RegexOptions del método de coincidencia de
patrones de una expresión regular incluye la marca RegexOptions.ExplicitCapture o si se aplica la opción n a esta
subexpresión (vea Opciones de grupo más adelante en este tema).
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "(\w+)\s(\1)\W"
Dim input As String = "He said that that was the the correct answer."
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
Console.WriteLine("Duplicate '{0}' found at positions {1} and {2}.", _
match.Groups(1).Value, match.Groups(1).Index, match.Groups(2).Index)
Next
End Sub
End Module
' The example displays the following output:
' Duplicate 'that' found at positions 8 and 13.
' Duplicate 'the' found at positions 22 and 26.
O bien
(?'name'subexpression)
donde nombre es un nombre de grupo válido, y subexpresión es cualquier patrón de expresión regular válido.
nombre no debe contener ningún carácter de puntuación y no puede comenzar por un número.
NOTE
Si el parámetro RegexOptions del método de coincidencia de patrones de una expresión regular incluye la marca
RegexOptions.ExplicitCapture o si se aplica la opción n a esta subexpresión (vea Opciones de grupo más adelante en
este tema), la única forma de capturar una subexpresión es asignar nombres explícitamente a los grupos de captura.
Puede tener acceso a los grupos capturados con nombre de las maneras siguientes:
Usando la construcción de referencia inversa con nombre dentro de la expresión regular. Para hacer
referencia a la subexpresión coincidente desde la misma expresión regular, se usa la sintaxis \k<
nombre > , donde nombre es el nombre de la subexpresión capturada.
Usando la construcción de referencia inversa dentro de la expresión regular. Para hacer referencia a la
subexpresión coincidente desde la misma expresión regular, se usa la sintaxis \ número, donde
número es el número ordinal de la subexpresión capturada. Las subexpresiones coincidentes con
nombre se numeran consecutivamente de izquierda a derecha después de las subexpresiones
coincidentes.
Usando la secuencia de reemplazo ${ nombre } en una llamada al método Regex.Replace o
Match.Result , donde nombre es el nombre de la subexpresión capturada.
Usando la secuencia de reemplazo $ número en una llamada al método Regex.Replace o Match.Result
, donde número es el número ordinal de la subexpresión capturada.
Mediante programación, usando el objeto GroupCollection devuelto por la propiedad Match.Groups .
El miembro en la posición cero de la colección representa la coincidencia de la expresión regular
completa. Cada miembro subsiguiente representa una subexpresión coincidente. Los grupos
capturados con nombre se almacenan en la colección después de los grupos capturados numerados.
Mediante programación, proporcionando el nombre de la subexpresión al indizador del objeto
GroupCollection (en C#) o a su propiedad Item[] (en Visual Basic).
Un patrón de expresión regular simple muestra cómo se puede hacer referencia a los grupos numerados (sin
nombre) y con nombre mediante programación o utilizando la sintaxis del lenguaje de expresiones regulares.
La expresión regular ((?<One>abc)\d+)?(?<Two>xyz)(.*) produce los siguientes grupos de captura por número
y por nombre. El primer grupo de captura (el número 0) siempre hace referencia al patrón completo.
N ÚM ERO N O M B RE M O DELO
3 Uno (?<One>abc)
4 Dos (?<Two>xyz)
En el ejemplo siguiente se muestra una expresión regular que identifica las palabras duplicadas y la palabra
que sigue inmediatamente a cada palabra duplicada. El patrón de la expresión regular define dos
subexpresiones con nombre: duplicateWord , que representa la palabra duplicada; y nextWord , que representa
la palabra que sigue a la palabra duplicada.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "(?<duplicateWord>\w+)\s\k<duplicateWord>\W(?<nextWord>\w+)"
Dim input As String = "He said that that was the the correct answer."
Console.WriteLine(Regex.Matches(input, pattern, RegexOptions.IgnoreCase).Count)
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
Console.WriteLine("A duplicate '{0}' at position {1} is followed by '{2}'.", _
match.Groups("duplicateWord").Value, match.Groups("duplicateWord").Index, _
match.Groups("nextWord").Value)
Next
End Sub
End Module
' The example displays the following output:
' A duplicate 'that' at position 8 is followed by 'was'.
' A duplicate 'the' at position 22 is followed by 'correct'.
Tenga en cuenta que un nombre de grupo se puede repetir en una expresión regular. Por ejemplo, es posible
que más de un grupo se llame digit , como muestra el ejemplo siguiente. En el caso de nombres duplicados,
el valor del objeto Group viene determinado por la última captura correcta en la cadena de entrada. Además,
la colección CaptureCollection se rellena con información de cada captura igual que si el nombre de grupo no
estuviera duplicado.
En el ejemplo siguiente, la expresión regular \D+(?<digit>\d+)\D+(?<digit>\d+)? incluye dos apariciones de un
grupo llamado digit . El primer grupo llamado digit captura uno o más caracteres de dígito. El segundo
grupo llamado digit captura cero o una aparición de uno o más caracteres de dígito. Tal y como muestra la
salida del ejemplo, si el segundo grupo de captura coincide correctamente con el texto, el valor de ese texto
define el valor del objeto Group . Si el segundo grupo de captura no coincide con la cadena de entrada, el
valor de la última coincidencia correcta define el valor del objeto Group .
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "\D+(?<digit>\d+)\D+(?<digit>\d+)?"
Dim inputs() As String = {"abc123def456", "abc123def"}
For Each input As String In inputs
Dim m As Match = Regex.Match(input, pattern)
If m.Success Then
Console.WriteLine("Match: {0}", m.Value)
For grpCtr As Integer = 1 to m.Groups.Count - 1
Dim grp As Group = m.Groups(grpCtr)
Console.WriteLine("Group {0}: {1}", grpCtr, grp.Value)
For capCtr As Integer = 0 To grp.Captures.Count - 1
Console.WriteLine(" Capture {0}: {1}", capCtr,
grp.Captures(capCtr).Value)
Next
Next
Else
Console.WriteLine("The match failed.")
End If
Console.WriteLine()
Next
End Sub
End Module
' The example displays the following output:
' Match: abc123def456
' Group 1: 456
' Capture 0: 123
' Capture 1: 456
'
' Match: abc123def
' Group 1: 123
' Capture 0: 123
O bien
(?'name1-name2' subexpression)
donde nombre1 es el grupo actual (opcional), nombre2 es un grupo definido previamente y subexpresión es
cualquier patrón de expresión regular válido. La definición de grupo de compensación elimina la definición de
nombre2 y almacena el intervalo entre nombre2 y nombre1 en nombre1. Si no se ha definido el grupo
nombre2 , la búsqueda de coincidencias retrocede. Como al eliminar la última definición de nombre2 se
revela la definición anterior de nombre2, esta construcción permite usar la pila de capturas del grupo
nombre2 como contador para realizar el seguimiento de construcciones anidadas como paréntesis o
corchetes de apertura y cierre.
La definición del grupo de compensación utiliza nombre2 como pila. El carácter inicial de cada construcción
anidada se coloca en el grupo y en su colección Group.Captures . Cuando se encuentra una coincidencia con
el carácter de cierre, el carácter de apertura correspondiente se quita del grupo, y la colección Captures
disminuye en una unidad. Después de buscar las coincidencias con los caracteres de apertura y cierre de
todas las construcciones anidadas, nombre2 estará vacío.
NOTE
Después de modificar la expresión regular del ejemplo siguiente para que utilice el carácter de apertura y cierre
adecuado de una construcción anidada, puede utilizarla con la mayoría de las estructuras anidadas, como expresiones
matemáticas o líneas de código de programa que incluyen varias llamadas a métodos anidadas.
En el ejemplo siguiente se usa una definición de grupo de compensación para que coincida con los corchetes
angulares de apertura y de cierre (<>) de una cadena de entrada. En el ejemplo se definen dos grupos con
nombre, Open y Close , que se utilizan como una pila para realizar el seguimiento de los pares de corchetes
angulares coincidentes. Cada corchete angular de apertura capturado se inserta en la colección de captura del
grupo Open , y cada corchete angular de cierre capturado se inserta en la colección de captura del grupo
Close . Mediante la definición del grupo de compensación se comprueba que haya un corchete angular de
cierre para cada corchete angular de apertura. Si no lo hay, el subpatrón final, (?(Open)(?!)) , se evalúa solo si
el grupo Open no está vacío (y, por consiguiente, si no se han cerrado todas las construcciones anidadas). Si
se evalúa el subpatrón final, la coincidencia produce un error, porque el subpatrón (?!) es una aserción de
búsqueda anticipada negativa de ancho cero que siempre produce un error.
using System;
using System.Text.RegularExpressions;
class Example
{
public static void Main()
{
string pattern = "^[^<>]*" +
"(" +
"((?'Open'<)[^<>]*)+" +
"((?'Close-Open'>)[^<>]*)+" +
")*" +
"(?(Open)(?!))$";
string input = "<abc><mno<xyz>>";
Module Example
Public Sub Main()
Dim pattern As String = "^[^<>]*" & _
"(" + "((?'Open'<)[^<>]*)+" & _
"((?'Close-Open'>)[^<>]*)+" + ")*" & _
"(?(Open)(?!))$"
Dim input As String = "<abc><mno<xyz>>"
Dim rgx AS New Regex(pattern) '
Dim m As Match = Regex.Match(input, pattern)
If m.Success Then
Console.WriteLine("Input: ""{0}"" " & vbCrLf & "Match: ""{1}""", _
input, m)
Dim grpCtr As Integer = 0
For Each grp As Group In m.Groups
Console.WriteLine(" Group {0}: {1}", grpCtr, grp.Value)
grpCtr += 1
Dim capCtr As Integer = 0
For Each cap As Capture In grp.Captures
Console.WriteLine(" Capture {0}: {1}", capCtr, cap.Value)
capCtr += 1
Next
Next
Else
Console.WriteLine("Match failed.")
End If
End Sub
End Module
' The example displays the following output:
' Input: "<abc><mno<xyz>>"
' Match: "<abc><mno<xyz>>"
' Group 0: <abc><mno<xyz>>
' Capture 0: <abc><mno<xyz>>
' Group 1: <mno<xyz>>
' Capture 0: <abc>
' Capture 1: <mno<xyz>>
' Group 2: <xyz
' Capture 0: <abc
' Capture 1: <mno
' Capture 2: <xyz
' Group 3: >
' Capture 0: >
' Capture 1: >
' Capture 2: >
' Group 4:
' Group 5: mno<xyz>
' Capture 0: abc
' Capture 1: xyz
' Capture 2: mno<xyz>
PA SO M O DELO RESULTA DO
1 ^ Comienza la búsqueda de
coincidencias al principio de la cadena
de entrada
donde subexpresión es cualquier patrón de expresión regular válido. La construcción de grupo sin captura se
utiliza normalmente cuando un cuantificador se aplica a un grupo, pero las subcadenas capturadas por el
grupo no tienen ningún interés.
NOTE
Si una expresión regular incluye construcciones de agrupamiento anidadas, no se aplica una construcción de grupo sin
captura exterior a las construcciones de grupo anidadas interiores.
En el ejemplo siguiente se muestra una expresión regular que incluye grupos sin captura. Observe que la
salida no incluye ningún grupo capturado.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "(?:\b(?:\w+)\W*)+\."
Dim input As String = "This is a short sentence."
Dim match As Match = Regex.Match(input, pattern)
Console.WriteLine("Match: {0}", match.Value)
For ctr As Integer = 1 To match.Groups.Count - 1
Console.WriteLine(" Group {0}: {1}", ctr, match.Groups(ctr).Value)
Next
End Sub
End Module
' The example displays the following output:
' Match: This is a short sentence.
La expresión regular (?:\b(?:\w+)\W*)+\. coincide con una frase que termina en un punto. Dado que la
expresión regular se centra en frases y no en palabras individuales, las construcciones de agrupamiento se
usan exclusivamente como cuantificadores. El patrón de la expresión regular se interpreta como se muestra
en la tabla siguiente.
Opciones de grupo
La siguiente construcción de agrupamiento aplica o deshabilita las opciones especificadas dentro de una
subexpresión:
(?imnsx-imnsx: subexpresión )
donde subexpresión es cualquier patrón de expresión regular válido. Por ejemplo, (?i-s:) activa la opción
que no hace distinción entre mayúsculas y minúsculas y deshabilita el modo de una sola línea. Para obtener
más información sobre las opciones insertadas que puede especificar, vea Opciones de expresiones regulares.
NOTE
Puede especificar opciones que se apliquen a una expresión regular completa en lugar de a una subexpresión usando
un constructor de la clase System.Text.RegularExpressions.Regex o un método estático. También puede especificar
opciones insertadas que se aplican después de un punto concreto en una expresión regular usando la construcción de
lenguaje (?imnsx-imnsx) .
La construcción de opciones de grupo no es un grupo de captura. Es decir, aunque cualquier parte de una
cadena capturada por subexpresión se incluye en la coincidencia, no se incluye en un grupo capturado ni se
usa para rellenar el objeto GroupCollection .
Por ejemplo, la expresión regular \b(?ix: d \w+)\s del ejemplo siguiente utiliza opciones insertadas en una
construcción de agrupamiento para habilitar la coincidencia sin distinción entre mayúsculas y minúsculas y
omitir el espacio en blanco del patrón para identificar todas las palabras que comienzan por la letra "d". La
expresión regular se define como se muestra en la tabla siguiente.
donde subexpresión es cualquier patrón de expresión regular. Para que se produzca una coincidencia, la
cadena de entrada debe coincidir con el patrón de expresión regular de subexpresión, aunque la subcadena
coincidente no se incluya en el resultado de la coincidencia. Una aserción de búsqueda anticipada positiva de
ancho cero no retrocede.
Normalmente, una aserción de búsqueda anticipada positiva de ancho cero se encuentra al final de un patrón
de expresión regular. Define una subcadena que se debe encontrar al final de una cadena para que se
produzca una coincidencia, pero que no debe incluirse en la coincidencia. También resulta útil para evitar un
retroceso excesivo. Puede usar una aserción de búsqueda anticipada positiva de ancho cero para asegurarse
de que un grupo capturado determinado comienza por un texto que coincide con un subconjunto del patrón
definido para dicho grupo capturado. Por ejemplo, si un grupo de captura coincide con caracteres
consecutivos que se usan para formar palabras, puede usar una aserción de búsqueda anticipada positiva de
ancho cero para requerir que el primero de los caracteres sea alfabético y esté en mayúsculas.
En el ejemplo siguiente se usa una aserción de búsqueda anticipada positiva de ancho cero para buscar la
palabra que precede al verbo "is" en la cadena de entrada.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "\b\w+(?=\sis\b)"
Dim inputs() As String = {"The dog is a Malamute.", _
"The island has beautiful birds.", _
"The pitch missed home plate.", _
"Sunday is a weekend day."}
donde subexpresión es cualquier patrón de expresión regular. Para que se produzca la coincidencia, la cadena
de entrada no debe coincidir con el patrón de expresión regular de subexpresión, aunque la cadena
coincidente no se incluya en el resultado de la coincidencia.
Una aserción de búsqueda anticipada negativa de ancho cero se utiliza normalmente al principio o al final de
una expresión regular. Al principio de una expresión regular, puede definir un patrón concreto que no se
debería buscar cuando el principio de la expresión regular define un patrón similar pero más general que se
desea buscar. En este caso, se usa a menudo para limitar el retroceso. Al final de una expresión regular, puede
definir una subexpresión que no se puede producir al final de una coincidencia.
En el ejemplo siguiente se define una expresión regular que utiliza una aserción de búsqueda anticipada
negativa de ancho cero al principio de la expresión regular para buscar palabras que no comienzan por "un".
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b(?!un)\w+\b"
Dim input As String = "unite one unethical ethics use untie ultimate"
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
Console.WriteLine(match.Value)
Next
End Sub
End Module
' The example displays the following output:
' one
' ethics
' use
' ultimate
En el ejemplo siguiente se define una expresión regular que utiliza una aserción de búsqueda anticipada
negativa de ancho cero al final de la expresión regular para buscar palabras que no terminan por un carácter
de puntuación.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b\w+\b(?!\p{P})"
Dim input As String = "Disconnected, disjointed thoughts in a sentence fragment."
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(match.Value)
Next
End Sub
End Module
' The example displays the following output:
' disjointed
' thoughts
' in
' a
' sentence
donde subexpresión es cualquier patrón de expresión regular. Para que se produzca una coincidencia,
subexpresión debe encontrarse en la cadena de entrada a la izquierda de la posición actual, aunque
subexpression no esté incluida en el resultado de la coincidencia. Una aserción de búsqueda tardía positiva
de ancho cero no retrocede.
Las aserciones de búsqueda tardía positiva de ancho cero se usan normalmente al principio de las
expresiones regulares. El patrón que definen es una condición previa de una coincidencia, aunque no forma
parte del resultado de la coincidencia.
Por ejemplo, el ejemplo siguiente coincide con los dos últimos dígitos del año para el siglo XXI (es decir,
requiere que los dígitos "20" precedan a la cadena coincidente).
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "2010 1999 1861 2140 2009"
Dim pattern As String = "(?<=\b20)\d{2}\b"
Las aserciones de búsqueda tardía positiva de ancho cero también se usan para limitar el retroceso cuando el
último carácter o caracteres de un grupo capturado debe ser un subconjunto de los caracteres que coincide
con el patrón de la expresión regular de dicho grupo. Por ejemplo, si un grupo captura todos los caracteres
que se usan para formar palabras consecutivos, puede usar una aserción de búsqueda tardía positiva de
ancho cero para requerir que el último carácter sea alfabético.
donde subexpresión es cualquier patrón de expresión regular. Para que se produzca una coincidencia,
subexpresión no debe encontrarse en la cadena de entrada a la izquierda de la posición actual. Sin embargo,
cualquier subcadena que no coincida con subexpression no se incluye en el resultado de la coincidencia.
Las aserciones de búsqueda tardía negativa de ancho cero se usan normalmente al principio de las
expresiones regulares. El patrón que definen impide una coincidencia en la cadena que sigue. También se usan
para limitar el retroceso cuando el último carácter o caracteres de un grupo capturado no debe ser uno o
varios de los caracteres que coinciden con el patrón de expresión regular de dicho grupo. Por ejemplo, si un
grupo captura todos los caracteres que se usan para formar palabras consecutivos, se puede usar una
aserción de búsqueda tardía positiva de ancho cero para requerir que el último carácter no sea de subrayado
(_).
El ejemplo siguiente busca la fecha de cualquier día de la semana que no sea fin de semana (es decir, que no
sea ni sábado ni domingo).
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim dates() As String = {"Monday February 1, 2010", _
"Wednesday February 3, 2010", _
"Saturday February 6, 2010", _
"Sunday February 7, 2010", _
"Monday, February 8, 2010"}
Dim pattern As String = "(?<!(Saturday|Sunday) )\b\w+ \d{1,2}, \d{4}\b"
Grupos atómicos
La construcción de agrupamiento siguiente representa un grupo atómico (conocido en otros motores de
expresiones regulares como subexpresión sin retroceso, subexpresión atómica o subexpresión de una sola
vez):
(?> subexpresión )
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim inputs() As String = {"cccd.", "aaad", "aaaa"}
Dim back As String = "(\w)\1+.\b"
Dim noback As String = "(?>(\w)\1+).\b"
La expresión regular sin retroceso (?>(\w)\1+).\b se define como se muestra en la tabla siguiente.
Module Example
Public Sub Main()
Dim pattern As String = "(\b(\w+)\W+)+"
Dim input As String = "This is a short sentence."
Dim match As Match = Regex.Match(input, pattern)
Console.WriteLine("Match: '{0}'", match.Value)
For ctr As Integer = 1 To match.Groups.Count - 1
Console.WriteLine(" Group {0}: '{1}'", ctr, match.Groups(ctr).Value)
Dim capCtr As Integer = 0
For Each capture As Capture In match.Groups(ctr).Captures
Console.WriteLine(" Capture {0}: '{1}'", capCtr, capture.Value)
capCtr += 1
Next
Next
End Sub
End Module
' The example displays the following output:
' Match: 'This is a short sentence.'
' Group 1: 'sentence.'
' Capture 0: 'This '
' Capture 1: 'is '
' Capture 2: 'a '
' Capture 3: 'short '
' Capture 4: 'sentence.'
' Group 2: 'sentence'
' Capture 0: 'This'
' Capture 1: 'is'
' Capture 2: 'a'
' Capture 3: 'short'
' Capture 4: 'sentence'
El patrón de expresión regular (\b(\w+)\W+)+ extrae palabras individuales de una cadena. Se define como se
muestra en la tabla siguiente.
El segundo grupo de captura coincide con cada palabra de la frase. El primer grupo de captura coincide con
cada palabra, junto con la puntuación y el espacio en blanco que siguen a la palabra. El objeto Group cuyo
índice es 2 proporciona información sobre el texto coincidente con el segundo grupo de captura. El conjunto
de palabras completo capturado por el grupo de captura está disponible desde el objeto CaptureCollection
devuelto por la propiedad Group.Captures .
Vea también
Lenguaje de expresiones regulares: referencia rápida
Retroceso
cuantificadores en expresiones regulares
16/09/2020 • 35 minutes to read • Edit Online
Los cuantificadores especifican cuántas instancias de un carácter, grupo o clase de caracteres deben estar
presentes en la entrada para que se encuentre una coincidencia. En la tabla siguiente se indican los
cuantificadores compatibles con .NET.
{ n , m } { n , m }? Coincide de n a m veces.
Las cantidades n y m son constantes de tipo entero. Normalmente, los cuantificadores son expansivos, ya que
hacen que el motor de expresiones regulares busque el mayor número posible de repeticiones de patrones
concretos. Si se anexa el carácter ? a un cuantificador se convierte en diferido, ya que hace que el motor de
expresiones regulares busque el menor número posible de repeticiones. Para obtener una descripción completa
de la diferencia entre los cuantificadores expansivos y diferidos, consulte la sección Cuantificadores expansivos
y diferidos más adelante en este tema.
IMPORTANT
El anidamiento de cuantificadores (por ejemplo, como hace el patrón de expresión regular (a*)* ) puede aumentar el
número de comparaciones que debe realizar el motor de expresiones regulares, como una función exponencial del
número de caracteres de la cadena de entrada. Para obtener más información sobre este comportamiento y sus
soluciones alternativas, consulte Retroceso.
NOTE
Si los caracteres *, +, ?, { y } se encuentran en un patrón de expresión regular, el motor de expresiones regulares los
interpreta como cuantificadores o como parte de construcciones de cuantificador, a menos que se incluyan en una clase
de caracteres. Para interpretarlos como caracteres literales fuera de una clase de caracteres, debe anteponerles una barra
diagonal inversa para indicar su secuencia de escape. Por ejemplo, la cadena \* en un patrón de expresión regular se
interpreta como un carácter de asterisco literal ("*").
string input = "Autumn is a great time for an annual announcement to all antique collectors.";
foreach (Match match in Regex.Matches(input, pattern, RegexOptions.IgnoreCase))
Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index);
Dim input As String = "Autumn is a great time for an annual announcement to all antique collectors."
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index)
Next
' The example displays the following output:
' 'an' found at position 27.
' 'annual' found at position 30.
' 'announcement' found at position 37.
' 'antique' found at position 57.
an+ Coincide con una "a" seguida de uno o más caracteres "n".
(\w{3,}?\.){2}? Coincide con el patrón del primer grupo dos veces, pero el
menor número de veces posible.
La expresión regular no coincide con el primer número porque el cuantificador * intenta coincidir con el
elemento anterior tantas veces como sea posible en toda la cadena y, por tanto, encuentra una coincidencia al
final de la cadena.
Este no es el comportamiento deseado. En su lugar, puede usar el cuantificador diferido *? para extraer los
dígitos de ambos números, tal como se muestra en el ejemplo siguiente.
En la mayoría de los casos, las expresiones regulares con cuantificadores expansivos y diferidos devuelven las
mismas coincidencias. Suelen devolver resultados diferentes cuando se usan con el metacarácter comodín ( . ),
que coincide con cualquier carácter.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "(a?)*"
Dim input As String = "aaabbb"
Dim match As Match = Regex.Match(input, pattern)
Console.WriteLine("Match: '{0}' at index {1}",
match.Value, match.Index)
If match.Groups.Count > 1 Then
Dim groups As GroupCollection = match.Groups
For grpCtr As Integer = 1 To groups.Count - 1
Console.WriteLine(" Group {0}: '{1}' at index {2}",
grpCtr,
groups(grpCtr).Value,
groups(grpCtr).Index)
Dim captureCtr As Integer = 0
For Each capture As Capture In groups(grpCtr).Captures
captureCtr += 1
Console.WriteLine(" Capture {0}: '{1}' at index {2}",
captureCtr, capture.Value, capture.Index)
Next
Next
End If
End Sub
End Module
' The example displays the following output:
' Match: 'aaa' at index 0
' Group 1: '' at index 3
' Capture 1: 'a' at index 0
' Capture 2: 'a' at index 1
' Capture 3: 'a' at index 2
' Capture 4: '' at index 3
Para ver la diferencia práctica entre un grupo de captura que define un número mínimo y máximo de capturas y
otro que define un número fijo de capturas, tenga en cuenta los patrones de expresiones regulares
(a\1|(?(1)\1)){0,2} y (a\1|(?(1)\1)){2} . Ambas expresiones regulares constan de un único grupo de captura,
que se define como se muestra en la tabla siguiente.
(a\1 Coincide con "a", junto con el valor del primer grupo
capturado...
La primera expresión regular intenta coincidir con este patrón de cero a dos veces; el segundo, exactamente dos
veces. Dado que el primer modelo alcanza el número mínimo de capturas con su primera captura de
String.Empty, nunca se repite para intentar coincidir con a\1 ; el cuantificador {0,2} solo permite las
coincidencias vacías en la última iteración. En cambio, la segunda expresión regular coincide con "a" porque
evalúa a\1 una segunda vez. El número mínimo de iteraciones (dos) obliga al motor a repetirse tras una
coincidencia vacía.
using System;
using System.Text.RegularExpressions;
pattern = @"(a\1|(?(1)\1)){0,2}";
input = "aaabbb";
pattern = @"(a\1|(?(1)\1)){2}";
Console.WriteLine("Regex pattern: {0}", pattern);
match = Regex.Match(input, pattern);
Console.WriteLine("Matched '{0}' at position {1}.",
match.Value, match.Index);
if (match.Groups.Count > 1) {
for (int groupCtr = 1; groupCtr <= match.Groups.Count - 1; groupCtr++)
{
Group group = match.Groups[groupCtr];
Console.WriteLine(" Group: {0}: '{1}' at position {2}.",
groupCtr, group.Value, group.Index);
int captureCtr = 0;
foreach (Capture capture in group.Captures) {
captureCtr++;
Console.WriteLine(" Capture: {0}: '{1}' at position {2}.",
captureCtr, capture.Value, capture.Index);
}
}
}
}
}
// The example displays the following output:
// Regex pattern: (a\1|(?(1)\1)){0,2}
// Match: '' at position 0.
// Group: 1: '' at position 0.
// Capture: 1: '' at position 0.
//
// Regex pattern: (a\1|(?(1)\1)){2}
// Matched 'a' at position 0.
// Group: 1: 'a' at position 0.
// Capture: 1: '' at position 0.
// Capture: 2: 'a' at position 0.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern, input As String
pattern = "(a\1|(?(1)\1)){0,2}"
input = "aaabbb"
pattern = "(a\1|(?(1)\1)){2}"
Console.WriteLine("Regex pattern: {0}", pattern)
match = Regex.Match(input, pattern)
Console.WriteLine("Matched '{0}' at position {1}.",
match.Value, match.Index)
If match.Groups.Count > 1 Then
For groupCtr As Integer = 1 To match.Groups.Count - 1
Dim group As Group = match.Groups(groupCtr)
Console.WriteLine(" Group: {0}: '{1}' at position {2}.",
groupCtr, group.Value, group.Index)
Dim captureCtr As Integer = 0
For Each capture As Capture In group.Captures
captureCtr += 1
Console.WriteLine(" Capture: {0}: '{1}' at position {2}.",
captureCtr, capture.Value, capture.Index)
Next
Next
End If
End Sub
End Module
' The example displays the following output:
' Regex pattern: (a\1|(?(1)\1)){0,2}
' Match: '' at position 0.
' Group: 1: '' at position 0.
' Capture: 1: '' at position 0.
'
' Regex pattern: (a\1|(?(1)\1)){2}
' Matched 'a' at position 0.
' Group: 1: 'a' at position 0.
' Capture: 1: '' at position 0.
' Capture: 2: 'a' at position 0.
Vea también
Lenguaje de expresiones regulares: referencia rápida
Retroceso
Construcciones de referencia inversa en expresiones
regulares
16/09/2020 • 16 minutes to read • Edit Online
Las referencias inversas proporcionan una forma cómoda de identificar un carácter o subcadena repetidos dentro
de una cadena. Por ejemplo, si la cadena de entrada contiene varias apariciones de una subcadena arbitraria,
puede buscar una coincidencia con la primera aparición con un grupo de captura y después usar una referencia
inversa para buscar una coincidencia con las siguientes apariciones de la subcadena.
NOTE
Se usa una sintaxis independiente para hacer referencia a los grupos de captura con numeración y con nombre de las
cadenas de reemplazo. Para obtener más información, consulte Substituciones.
.NET define elementos del lenguaje independientes para hacer referencia a los grupos de captura con numeración
y con nombre. Para más información sobre los grupos de captura con nombre, vea Construcciones de
agrupamiento.
EL EM EN TO DESC RIP T IO N
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "(\w)\1"
Dim input As String = "trellis llama webbing dresser swagger"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("Found '{0}' at position {1}.", _
match.Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' Found 'll' at position 3.
' Found 'll' at position 8.
' Found 'bb' at position 16.
' Found 'ss' at position 25.
' Found 'gg' at position 33.
O bien
\k' nombre '
donde nombre es el nombre de un grupo de captura definido en el patrón de expresión regular. Si nombre no
está definido en el patrón de expresión regular, se produce un error de análisis y el motor de expresiones
regulares produce una clase ArgumentException.
En el ejemplo siguiente, se buscan caracteres de palabra duplicados en una cadena. Define una expresión regular,
(?<char>\w)\k<char> , que consta de los siguientes elementos.
EL EM EN TO DESC RIP T IO N
\k<char> Coincide con el siguiente carácter que sea igual que el valor
del grupo de captura char .
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "(?<char>\w)\k<char>"
Dim input As String = "trellis llama webbing dresser swagger"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("Found '{0}' at position {1}.", _
match.Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' Found 'll' at position 3.
' Found 'll' at position 8.
' Found 'bb' at position 16.
' Found 'ss' at position 25.
' Found 'gg' at position 33.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "(?<2>\w)\k<2>"
Dim input As String = "trellis llama webbing dresser swagger"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("Found '{0}' at position {1}.", _
match.Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' Found 'll' at position 3.
' Found 'll' at position 8.
' Found 'bb' at position 16.
' Found 'ss' at position 25.
' Found 'gg' at position 33.
Si nombre es la representación de cadena de un número y ningún grupo de captura tiene ese nombre, \k<
nombre > es igual que la referencia inversa \ número, donde número es la posición ordinal de la captura. En el
ejemplo siguiente, hay un único grupo de captura denominado char . La construcción de referencia inversa hace
referencia a él como \k<1> . Como muestra la salida del ejemplo, la llamada a Regex.IsMatch se realiza
correctamente porque char es el primer grupo de captura.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Console.WriteLine(Regex.IsMatch("aa", "(?<char>\w)\k<1>"))
' Displays "True".
End Sub
End Module
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Console.WriteLine(Regex.IsMatch("aa", "(?<2>\w)\k<1>"))
' Throws an ArgumentException.
End Sub
End Module
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "(?<1>a)(?<1>\1b)*"
Dim input As String = "aababb"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("Match: " + match.Value)
For Each group As Group In match.Groups
Console.WriteLIne(" Group: " + group.Value)
Next
Next
End Sub
End Module
' The example display the following output:
' Group: aababb
' Group: abb
Al comparar la expresión regular con la cadena de entrada ("aababb"), el motor de expresiones regulares realiza
las siguientes operaciones:
1. Comienza al principio de la cadena y hace que "a" coincida correctamente con la expresión (?<1>a) .
Ahora, el valor del grupo 1 es "a".
2. Se desplaza hasta el segundo carácter y hace que la cadena "ab" coincida correctamente con la expresión
\1b , o "ab". Después, asigna el resultado "ab" a \1 .
3. Se desplaza hasta el cuarto carácter. La expresión (?<1>\1b)* puede coincidir cero o más veces, así que la
cadena "abb" coincide correctamente con la expresión \1b . Vuelve a asignar el resultado, "abb", a \1 .
En este ejemplo, * es un cuantificador de bucle: se evalúa repetidas veces hasta que el motor de expresiones
regulares no puede coincidir con el patrón que define. Los cuantificadores de bucle no borran las definiciones de
grupo.
Si un grupo no ha capturado ninguna subcadena, no se define una referencia inversa a ese grupo y no coincide
nunca. Así lo ilustra el patrón de expresión regular \b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\b que se define de la
siguiente forma:
Una cadena de entrada puede coincidir con esta expresión regular aunque no estén presentes los dos dígitos
decimales que define el segundo grupo de captura. En el ejemplo siguiente, se muestra que, aunque la
coincidencia es correcta, hay un grupo de captura vacío entre dos grupos de captura correctos.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "\b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\b"
Dim inputs() As String = {"AA22ZZ", "AABB"}
For Each input As String In inputs
Dim match As Match = Regex.Match(input, pattern)
If match.Success Then
Console.WriteLine("Match in {0}: {1}", input, match.Value)
If match.Groups.Count > 1 Then
For ctr As Integer = 1 To match.Groups.Count - 1
If match.Groups(ctr).Success Then
Console.WriteLine("Group {0}: {1}", _
ctr, match.Groups(ctr).Value)
Else
Console.WriteLine("Group {0}: <no match>", ctr)
End If
Next
End If
End If
Console.WriteLine()
Next
End Sub
End Module
' The example displays the following output:
' Match in AA22ZZ: AA22ZZ
' Group 1: AA
' Group 2: 22
' Group 3: ZZ
'
' Match in AABB: AABB
' Group 1: AA
' Group 2: <no match>
' Group 3: BB
Vea también
Lenguaje de expresiones regulares: referencia rápida
Construcciones de alternancia en expresiones
regulares
16/09/2020 • 14 minutes to read • Edit Online
Las construcciones de alternancia modifican una expresión regular para habilitar la coincidencia condicional o
“either/or”. .NET admite tres construcciones de alternancia:
Coincidencia de patrones con |
Coincidencia condicional con (?(expresión)sí|no)
Coincidencia condicional basada en un grupo capturado válido
using System;
using System.Text.RegularExpressions;
string input = "The gray wolf blended in among the grey rocks.";
foreach (Match match in Regex.Matches(input, pattern1))
Console.WriteLine("'{0}' found at position {1}",
match.Value, match.Index);
Console.WriteLine();
foreach (Match match in Regex.Matches(input, pattern2))
Console.WriteLine("'{0}' found at position {1}",
match.Value, match.Index);
}
}
// The example displays the following output:
// 'gray' found at position 4
// 'grey' found at position 35
//
// 'gray' found at position 4
// 'grey' found at position 35
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
' Regular expression using character class.
Dim pattern1 As String = "\bgr[ae]y\b"
' Regular expression using either/or.
Dim pattern2 As String = "\bgr(a|e)y\b"
Dim input As String = "The gray wolf blended in among the grey rocks."
For Each match As Match In Regex.Matches(input, pattern1)
Console.WriteLine("'{0}' found at position {1}", _
match.Value, match.Index)
Next
Console.WriteLine()
For Each match As Match In Regex.Matches(input, pattern2)
Console.WriteLine("'{0}' found at position {1}", _
match.Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' 'gray' found at position 4
' 'grey' found at position 35
'
' 'gray' found at position 4
' 'grey' found at position 35
La expresión regular que usa el carácter | , \bgr(a|e)y\b , se interpreta como se muestra en la tabla siguiente:
El carácter | también se puede usar para realizar una coincidencia either/or con varios caracteres o
subexpresiones, que pueden incluir cualquier combinación de literales de carácter y elementos de lenguaje de
expresión regular. (La clase de caracteres no proporciona esta funcionalidad). En el ejemplo siguiente, se usa el
carácter | para extraer un número de la seguridad social (SSN) de EE. UU., de nueve dígitos y en formato
ddd-dd-dddd, o un número de identificación de empleador (EIN), de nueve dígitos y en formato dd-ddddddd.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b(\d{2}-\d{7}|\d{3}-\d{2}-\d{4})\b"
Dim input As String = "01-9999999 020-333333 777-88-9999"
Console.WriteLine("Matches for {0}:", pattern)
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(" {0} at position {1}", match.Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' Matches for \b(\d{2}-\d{7}|\d{3}-\d{2}-\d{4})\b:
' 01-9999999 at position 0
' 777-88-9999 at position 22
donde (?= expresión ) es una construcción de aserción de ancho cero. Para más información, consulte la
sección sobre Construcciones de agrupamiento en expresiones regulares. Como el motor de expresiones
regulares interpreta la expresión como un delimitador (una aserción de ancho cero), la expresión debe ser una
aserción de ancho cero (para más información, consulte Delimitadores en expresiones regulares) o una
subexpresión que también esté incluida en sí. De lo contrario, no se pueden encontrar coincidencias para el
patrón sí .
NOTE
Si expresión es un grupo de captura con nombre o con numeración, la construcción de alternancia se interpreta como una
prueba de captura; para obtener más información, consulte la sección siguiente, Coincidencia condicional basada en un
grupo capturado válido. En otras palabras, el motor de expresiones regulares no intenta coincidir con la subcadena
capturada, sino que, en vez de eso, comprueba la presencia o la ausencia del grupo.
El siguiente ejemplo es una variación del que aparece en la sección Coincidencia de patrones either/or con |. En él
se usa la coincidencia condicional para determinar si los tres primeros caracteres después de un límite de palabra
son dos dígitos seguidos por un guión. Si es así, intenta buscar un número de identificación de empleador (EIN)
de EE. UU. Si no, intenta buscar un número de la seguridad social (SSN) de EE.UU.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "\b(?(\d{2}-)\d{2}-\d{7}|\d{3}-\d{2}-\d{4})\b"
Dim input As String = "01-9999999 020-333333 777-88-9999"
Console.WriteLine("Matches for {0}:", pattern)
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(" {0} at position {1}", match.Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' Matches for \b(?(\d{2}-)\d{2}-\d{7}|\d{3}-\d{2}-\d{4})\b:
' 01-9999999 at position 0
' 777-88-9999 at position 22
o
(?( número ) sí | no )
donde nombre es el nombre y número es el número de un grupo de captura, sí es la expresión que debe coincidir
si nombre o número tienen una coincidencia, y no es la expresión opcional que debe coincidir si no la tienen.
Si nombre no se corresponde con el nombre de un grupo de captura que se usa en el patrón de expresión regular,
la construcción de alternancia se interpreta como una prueba de expresión, tal como se explica en la sección
anterior. Normalmente, esto significa que expresión se evalúa como false . Si número no se corresponde con un
grupo de captura numerado que se usa en el patrón de expresión regular, el motor de expresiones regulares
genera una excepción ArgumentException.
El siguiente ejemplo es una variación del que aparece en la sección Coincidencia de patrones either/or con |. En él
se usa un grupo de captura denominado n2 que consta de dos dígitos seguidos por un guión. La construcción
de alternancia prueba si este grupo de captura ha coincidido en la cadena de entrada. En caso afirmativo, la
construcción de alternancia intenta hacer coincidir los últimos siete dígitos de un número de identificación de
empleador de identificación de empleador (EIN) de EE. UU. En caso negativo, intenta coincidir con un número de
la seguridad social (SSN) de EE.UU.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b(?<n2>\d{2}-)?(?(n2)\d{7}|\d{3}-\d{2}-\d{4})\b"
Dim input As String = "01-9999999 020-333333 777-88-9999"
Console.WriteLine("Matches for {0}:", pattern)
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(" {0} at position {1}", match.Value, match.Index)
Next
End Sub
End Module
(?<n2>\d{2}-)? Coincide con cero o con dos dígitos seguidos por un guión.
Este grupo de captura se denomina n2 .
En el ejemplo siguiente se muestra una variación de este ejemplo, pero con un grupo numerado en lugar de un
grupo con nombre. Su patrón de expresión regular es \b(\d{2}-)?(?(1)\d{7}|\d{3}-\d{2}-\d{4})\b .
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b(\d{2}-)?(?(1)\d{7}|\d{3}-\d{2}-\d{4})\b"
Dim input As String = "01-9999999 020-333333 777-88-9999"
Console.WriteLine("Matches for {0}:", pattern)
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(" {0} at position {1}", match.Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' Matches for \b(\d{2}-)?(?(1)\d{7}|\d{3}-\d{2}-\d{4})\b:
' 01-9999999 at position 0
' 777-88-9999 at position 22
Vea también
Lenguaje de expresiones regulares: referencia rápida
Sustituciones en expresiones regulares
16/09/2020 • 28 minutes to read • Edit Online
Las sustituciones son elementos del lenguaje que se reconocen solo dentro de patrones de reemplazo. Usan un
patrón de expresión regular para definir todo o parte del texto que reemplazará el texto coincidente en la cadena
de entrada. El patrón de reemplazo puede estar compuesto de una o más sustituciones junto con caracteres
literales. Los patrones de reemplazo se proporcionan a las sobrecargas del método Regex.Replace que tiene un
parámetro replacement y al método Match.Result . Los métodos reemplazan el patrón que coincide con el patrón
que define el parámetro replacement .
.NET Framework define los elementos de sustitución que se enumeran en la siguiente tabla.
NOTE
Para obtener una funcionalidad similar a la de un patrón de reemplazo dentro de una expresión regular, use una referencia
inversa. Para obtener más información acerca de las referencias inversas, vea Construcciones de referencia inversa.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "\p{Sc}*(\s?\d+[.,]?\d*)\p{Sc}*"
Dim replacement As String = "$1"
Dim input As String = "$16.32 12.19 £16.29 €18.29 €18,29"
Dim result As String = Regex.Replace(input, pattern, replacement)
Console.WriteLine(result)
End Sub
End Module
' The example displays the following output:
' 16.32 12.19 16.29 18.29 18,29
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\p{Sc}*(?<amount>\s?\d+[.,]?\d*)\p{Sc}*"
Dim replacement As String = "${amount}"
Dim input As String = "$16.32 12.19 £16.29 €18.29 €18,29"
Dim result As String = Regex.Replace(input, pattern, replacement)
Console.WriteLine(result)
End Sub
End Module
' The example displays the following output:
' 16.32 12.19 16.29 18.29 18,29
using System;
using System.Globalization;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
' Define array of decimal values.
Dim values() As String = {"16.35", "19.72", "1234", "0.99"}
' Determine whether currency precedes (True) or follows (False) number.
Dim precedes As Boolean = (NumberFormatInfo.CurrentInfo.CurrencyPositivePattern Mod 2 = 0)
' Get decimal separator.
Dim cSeparator As String = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator
' Get currency symbol.
Dim symbol As String = NumberFormatInfo.CurrentInfo.CurrencySymbol
' If symbol is a "$", add an extra "$".
If symbol = "$" Then symbol = "$$"
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "^(\w+\s?)+$"
Dim titles() As String = {"A Tale of Two Cities", _
"The Hound of the Baskervilles", _
"The Protestant Ethic and the Spirit of Capitalism", _
"The Origin of Species"}
Dim replacement As String = """$&"""
For Each title As String In titles
Console.WriteLine(Regex.Replace(title, pattern, replacement))
Next
End Sub
End Module
' The example displays the following output:
' "A Tale of Two Cities"
' "The Hound of the Baskervilles"
' "The Protestant Ethic and the Spirit of Capitalism"
' "The Origin of Species"
El patrón de reemplazo "$&" agrega una comilla literal al principio y al final de cada coincidencia.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim input As String = "aa1bb2cc3dd4ee5"
Dim pattern As String = "\d+"
Dim substitution As String = "$`"
Console.WriteLine("Matches:")
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(" {0} at position {1}", match.Value, match.Index)
Next
Console.WriteLine("Input string: {0}", input)
Console.WriteLine("Output string: " + _
Regex.Replace(input, pattern, substitution))
End Sub
End Module
' The example displays the following output:
' Matches:
' 1 at position 2
' 2 at position 5
' 3 at position 8
' 4 at position 11
' 5 at position 14
' Input string: aa1bb2cc3dd4ee5
' Output string: aaaabbaa1bbccaa1bb2ccddaa1bb2cc3ddeeaa1bb2cc3dd4ee
En este ejemplo, la cadena de entrada "aa1bb2cc3dd4ee5" contiene cinco coincidencias. En la siguiente tabla se
muestra cómo la sustitución $` hace que el motor de expresiones regulares reemplace cada coincidencia en la
cadena de entrada. El texto insertado se muestra en negrita en la columna de resultados.
C A DEN A A N T ES DE L A
C O IN C IDIR C O N P O SIC IÓ N C O IN C IDEN C IA C A DEN A DE RESULTA DO
1 2 aa aaaa bb2cc3dd4ee5
3 8 aa1bb2cc aaaabbaa1bbccaa1bb2ccdd
4ee5
4 11 aa1bb2cc3dd aaaabbaa1bbccaa1bb2ccdda
a1bb2cc3dd ee5
5 14 aa1bb2cc3dd4ee aaaabbaa1bbccaa1bb2ccdda
a1bb2cc3ddeeaa1bb2cc3d
d4ee
En el ejemplo siguiente, se usa el patrón de expresión regular \d+ para que coincida con una secuencia de uno o
más dígitos decimales en la cadena de entrada. La cadena de reemplazo $' reemplaza estos dígitos por el texto
que sigue a la coincidencia.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "aa1bb2cc3dd4ee5"
Dim pattern As String = "\d+"
Dim substitution As String = "$'"
Console.WriteLine("Matches:")
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(" {0} at position {1}", match.Value, match.Index)
Next
Console.WriteLine("Input string: {0}", input)
Console.WriteLine("Output string: " + _
Regex.Replace(input, pattern, substitution))
End Sub
End Module
' The example displays the following output:
' Matches:
' 1 at position 2
' 2 at position 5
' 3 at position 8
' 4 at position 11
' 5 at position 14
' Input string: aa1bb2cc3dd4ee5
' Output string: aabb2cc3dd4ee5bbcc3dd4ee5ccdd4ee5ddee5ee
En este ejemplo, la cadena de entrada "aa1bb2cc3dd4ee5" contiene cinco coincidencias. En la siguiente tabla se
muestra cómo la sustitución $' hace que el motor de expresiones regulares reemplace cada coincidencia en la
cadena de entrada. El texto insertado se muestra en negrita en la columna de resultados.
2 5 cc3dd4ee5 aabb2cc3dd4ee5bbcc3dd4
ee5 cc3dd4ee5
3 8 dd4ee5 aabb2cc3dd4ee5bbcc3dd4e
e5ccdd4ee5 dd4ee5
4 11 ee5 aabb2cc3dd4ee5bbcc3dd4e
e5ccdd4ee5ddee5 ee5
5 14 String.Empty aabb2cc3dd4ee5bbcc3dd4e
e5ccdd4ee5ddee5ee
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b(\w+)\s\1\b"
Dim substitution As String = "$+"
Dim input As String = "The the dog jumped over the fence fence."
Console.WriteLine(Regex.Replace(input, pattern, substitution, _
RegexOptions.IgnoreCase))
End Sub
End Module
' The example displays the following output:
' The dog jumped over the fence.
El patrón de expresión regular \b(\w+)\s\1\b se define como se muestra en la tabla siguiente.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "ABC123DEF456"
Dim pattern As String = "\d+"
Dim substitution As String = "$_"
Console.WriteLine("Original string: {0}", input)
Console.WriteLine("String with substitution: {0}", _
Regex.Replace(input, pattern, substitution))
End Sub
End Module
' The example displays the following output:
' Original string: ABC123DEF456
' String with substitution: ABCABC123DEF456DEFABC123DEF456
En este ejemplo, la cadena de entrada "ABC123DEF456" contiene dos coincidencias. En la siguiente tabla se muestra
cómo la sustitución $_ hace que el motor de expresiones regulares reemplace cada coincidencia en la cadena de
entrada. El texto insertado se muestra en negrita en la columna de resultados.
2 5 456 ABCABC123DEF456DEFAB
C123DEF456
Vea también
Lenguaje de expresiones regulares: referencia rápida
Opciones de expresiones regulares
16/09/2020 • 72 minutes to read • Edit Online
De forma predeterminada, al comparar una cadena de entrada con los caracteres literales de un patrón de
expresión regular, se distinguen mayúsculas de minúsculas, los espacios en blanco del patrón de expresión
regular se interpretan como caracteres de espacio en blanco literales, y los grupos de captura de la
expresión regular se denominan tanto implícita como explícitamente. Estos y otros aspectos del
comportamiento predeterminado de la expresión regular se pueden modificar mediante la especificación
de las opciones de la expresión regular. Estas opciones —que aparecen enumeradas en la tabla siguiente
— se pueden insertar como parte del patrón de expresión regular, o se pueden suministrar a un
constructor de clases System.Text.RegularExpressions.Regex o a un método estático de coincidencia de
patrones como valor de enumeración de System.Text.RegularExpressions.RegexOptions.
Especificación de opciones
Las opciones de las expresiones regulares se pueden especificar de tres modos:
En el parámetro options de un constructor de clases System.Text.RegularExpressions.Regex o
método de coincidencia de patrones estático ( Shared en Visual Basic), como Regex(String,
RegexOptions) o Regex.Match(String, String, RegexOptions). El parámetro options es una
combinación OR bit a bit de valores enumerados de System.Text.RegularExpressions.RegexOptions.
Cuando se proporcionan opciones a una instancia Regex mediante el parámetro options de un
constructor de clase, las opciones se asignan a la propiedad
System.Text.RegularExpressions.RegexOptions. Sin embargo, la propiedad
System.Text.RegularExpressions.RegexOptions no refleja las opciones insertadas en el mismo
patrón de expresión regular.
Esto se muestra en el ejemplo siguiente. Usa el parámetro options del método Regex.Match(String,
String, RegexOptions) para habilitar la coincidencia sin distinción entre mayúsculas y minúsculas,
así como para ignorar el espacio en blanco del patrón a la hora de identificar palabras que
empiecen por la letra “d”.
string pattern = @"d \w+ \s";
string input = "Dogs are decidedly good pets.";
RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace;
Con la aplicación de las opciones insertadas en un patrón de expresión regular con la sintaxis
(?imnsx-imnsx) . La opción se aplica al patrón a partir del punto en que la opción se define hasta el
final del patrón o hasta el punto en que otra opción insertada deja a la opción sin definir. Tenga en
cuenta que la propiedad System.Text.RegularExpressions.RegexOptions de una instancia Regex no
refleja estas opciones insertadas. Para más información, consulte el tema Construcciones
misceláneas.
Esto se muestra en el ejemplo siguiente. Usa opciones insertadas para habilitar la coincidencia sin
distinción entre mayúsculas y minúsculas, así como para ignorar el espacio en blanco del patrón a
la hora de identificar palabras que empiecen por la letra “d”.
Si las opciones están insertadas, un signo menos ( - ) antes de una opción o conjunto de opciones
desactiva dichas opciones. Por ejemplo, la construcción insertada (?ix-ms) activa las opciones
RegexOptions.IgnoreCase y RegexOptions.IgnorePatternWhitespace y desactiva las opciones
RegexOptions.Multiline y RegexOptions.Singleline. Todas las opciones de expresión regular están
desactivadas de forma predeterminada.
NOTE
Si las opciones de expresión regular especificadas en el parámetro options de un constructor o llamada a método
están en conflicto con las opciones insertadas en un patrón de expresión regular, se usan las opciones insertadas.
Las siguientes opciones de expresión regular se pueden establecer con el parámetro y mediante inserción:
RegexOptions.IgnoreCase
RegexOptions.Multiline
RegexOptions.Singleline
RegexOptions.ExplicitCapture
RegexOptions.IgnorePatternWhitespace
Las siguientes cinco opciones de expresión regular se pueden establecer con el parámetro options , pero
no mediante inserción:
RegexOptions.None
RegexOptions.Compiled
RegexOptions.RightToLeft
RegexOptions.CultureInvariant
RegexOptions.ECMAScript
Determinación de opciones
Se pueden determinar las opciones que se proporcionaron a un objeto Regex al crearse su instancia; para
ello, recupere el valor de la propiedad Regex.Options de solo lectura. Esta propiedad resulta especialmente
útil para determinar las opciones definidas para una expresión regular compilada que se ha creado con el
método Regex.CompileToAssembly.
Para probar la presencia de cualquier opción, excepto RegexOptions.None, realice una operación AND con
el valor de la propiedad Regex.Options y el valor de RegexOptions en el que esté interesado. Después,
pruebe si el resultado es igual a ese valor RegexOptions. En el ejemplo siguiente se prueba si se ha
establecido la opción RegexOptions.IgnoreCase.
if (rgx.Options == RegexOptions.None)
Console.WriteLine("No options have been set.");
En las secciones siguientes se enumeran las opciones admitidas en una expresión regular de .NET.
Opciones predeterminadas
La opción RegexOptions.None indica que no se ha especificado ninguna opción y que el motor de
expresiones regulares sigue su comportamiento predeterminado. Entre estas estructuras se incluyen las
siguientes:
El patrón se interpreta como un canónico en vez de como una expresión regular ECMAScript.
El patrón de expresión regular se compara con la cadena de entrada de izquierda a derecha.
Las comparaciones distinguen entre mayúsculas y minúsculas.
Los elementos de lenguaje ^ y $ coinciden con el principio y el final de la cadena de entrada.
El elemento de lenguaje . coincide con todos los caracteres excepto \n .
Un espacio en blanco en un patrón de expresión regular se interpreta como un carácter de espacio
literal.
Las convenciones de la referencia cultural actual se usan al comparar el patrón con la cadena de
entrada.
Los grupos de captura del patrón de expresión regular son implícitos y explícitos.
NOTE
La opción RegexOptions.None no tiene ningún equivalente de inserción. Cuando las opciones de expresión regular
se aplican mediante inserción, el comportamiento predeterminado se restaura opción por opción, mediante la
desactivación de una opción concreta. Por ejemplo, (?i) activa la comparación sin distinción entre mayúsculas y
minúsculas, y (?-i) restaura la comparación con distinción entre mayúsculas y minúsculas.
using System;
using System.Text.RegularExpressions;
Console.WriteLine();
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.IgnoreCase))
Console.WriteLine("Found {0} at index {1}.", match.Value, match.Index);
}
}
// The example displays the following output:
// Found then at index 8.
// Found them at index 18.
//
// Found The at index 0.
// Found then at index 8.
// Found them at index 18.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\bthe\w*\b"
Dim input As String = "The man then told them about that event."
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("Found {0} at index {1}.", match.Value, match.Index)
Next
Console.WriteLine()
For Each match As Match In Regex.Matches(input, pattern, _
RegexOptions.IgnoreCase)
Console.WriteLine("Found {0} at index {1}.", match.Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' Found then at index 8.
' Found them at index 18.
'
' Found The at index 0.
' Found then at index 8.
' Found them at index 18.
En el ejemplo siguiente se modifica el patrón de expresión regular del ejemplo anterior a fin de usar las
opciones insertadas en vez del parámetro options para realizar la comparación sin distinción entre
mayúsculas y minúsculas. El primer parámetro define la opción sin distinción entre mayúsculas y
minúsculas en una construcción de agrupamiento que se aplica solo a la letra “t” de la cadena “the”. Como
la construcción de opciones ocurre al principio del patrón, el segundo patrón aplica la opción sin
distinción entre mayúsculas y minúsculas a toda la expresión regular.
using System;
using System.Text.RegularExpressions;
Console.WriteLine();
pattern = @"(?i)\bthe\w*\b";
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.IgnoreCase))
Console.WriteLine("Found {0} at index {1}.", match.Value, match.Index);
}
}
// The example displays the following output:
// Found The at index 0.
// Found then at index 8.
// Found them at index 18.
//
// Found The at index 0.
// Found then at index 8.
// Found them at index 18.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b(?i:t)he\w*\b"
Dim input As String = "The man then told them about that event."
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("Found {0} at index {1}.", match.Value, match.Index)
Next
Console.WriteLine()
pattern = "(?i)\bthe\w*\b"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("Found {0} at index {1}.", match.Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' Found The at index 0.
' Found then at index 8.
' Found them at index 18.
'
' Found The at index 0.
' Found then at index 8.
' Found them at index 18.
Modo multilínea
La opción RegexOptions.Multiline, o la opción insertada m , habilita al motor de expresiones regulares
para que controle una cadena de entrada que consta de varias líneas. Cambia la interpretación de los
elementos de lenguaje ^ y $ de modo que coincidan con el inicio y el final de una línea, en vez de con el
inicio y el final de la cadena de entrada.
De forma predeterminada, $ coincide solo con el final de la cadena de entrada. Si se especifica la opción
RegexOptions.Multiline, coincide con el carácter de nueva línea ( \n ) o con el final de la cadena de
entrada. Sin embargo, no coincide con la combinación de caracteres de retorno de carro y salto de línea.
Para que coincida correctamente, use la subexpresión \r?$ en vez de simplemente $ .
En el ejemplo siguiente se extraen los nombres y las puntuaciones de los jugadores de bolos y se agregan
a una colección SortedList<TKey,TValue> que los ordena en sentido descendente. Se realizan dos
llamadas al método Matches. En la primera llamada a método, la expresión regular es ^(\w+)\s(\d+)$ y
no se establece ninguna opción. Como muestra el resultado, no se encuentran coincidencias, ya que el
motor de expresiones regulares no puede hacer coincidir el patrón de entrada junto con el principio y el
final de la cadena de entrada. En la segunda llamada a método, la expresión regular se cambia a
^(\w+)\s(\d+)\r?$ y las opciones se establecen en RegexOptions.Multiline. Como muestra el resultado,
los nombres y las puntuaciones coinciden correctamente, y las puntuaciones aparecen en orden
descendente.
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim scores As New SortedList(Of Integer, String)(New DescendingComparer(Of Integer)())
El ejemplo siguiente es equivalente al anterior, a excepción de que usa la opción insertada (?m) para
establecer la opción multilínea.
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim scores As New SortedList(Of Integer, String)(New DescendingComparer(Of Integer)())
Console.WriteLine();
foreach (Match match in Regex.Matches(input, pattern, RegexOptions.Singleline))
Console.WriteLine(Regex.Escape(match.Value));
}
}
// The example displays the following output:
// This\ is\ one\ line\ and\r
//
// This\ is\ one\ line\ and\r\nthis\ is\ the\ second\.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "^.+"
Dim input As String = "This is one line and" + vbCrLf + "this is the second."
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(Regex.Escape(match.Value))
Next
Console.WriteLine()
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.SingleLine)
Console.WriteLine(Regex.Escape(match.Value))
Next
End Sub
End Module
' The example displays the following output:
' This\ is\ one\ line\ and\r
'
' This\ is\ one\ line\ and\r\nthis\ is\ the\ second\.
El ejemplo siguiente es equivalente al anterior, a excepción de que usa la opción insertada (?s) para
habilitar el modo de una sola línea.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "(?s)^.+"
Dim input As String = "This is one line and" + vbCrLf + "this is the second."
Su única finalidad es extraer de un documento las oraciones que finalizan con un punto, signo de
exclamación o signo de interrogación, y solo la oración resultante (representada mediante el objeto
Match) es de interés. En cambio, las palabras individuales de la colección no son de interés.
Los grupos de captura que no se usan posteriormente pueden ser costosos, ya que el motor de
expresiones regulares debe rellenar los objetos de colección GroupCollection y CaptureCollection. Como
alternativa, se puede usar la opción RegexOptions.ExplicitCapture o la opción insertada n para especificar
que las únicas capturas válidas son grupos con nombre o número explícitos que se designan mediante la
construcción (?< nombre > subexpresión ) .
En el ejemplo siguiente se muestra información sobre las coincidencias devueltas por el patrón de
expresión regular \b\(?((\w+),?\s?)+[\.!?]\)? cuando se llama al método Match con y sin la opción
RegexOptions.ExplicitCapture. Tal como muestra el resultado de la primera llamada a método, el motor de
expresiones regulares rellena totalmente los objetos de colección GroupCollection y CaptureCollection
con información sobre las subcadenas capturadas. Dado que el segundo método se llama con options
establecido en RegexOptions.ExplicitCapture, no se captura información en grupos.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "This is the first sentence. Is it the beginning " + _
"of a literary masterpiece? I think not. Instead, " + _
"it is a nonsensical paragraph."
Dim pattern As String = "\b\(?((?>\w+),?\s?)+[\.!?]\)?"
Console.WriteLine("With implicit captures:")
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("The match: {0}", match.Value)
Dim groupCtr As Integer = 0
For Each group As Group In match.Groups
Console.WriteLine(" Group {0}: {1}", groupCtr, group.Value)
groupCtr += 1
Dim captureCtr As Integer = 0
For Each capture As Capture In group.Captures
Console.WriteLine(" Capture {0}: {1}", captureCtr, capture.Value)
captureCtr += 1
Next
Next
Next
Next
Console.WriteLine()
Console.WriteLine("With explicit captures only:")
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.ExplicitCapture)
Console.WriteLine("The match: {0}", match.Value)
Dim groupCtr As Integer = 0
For Each group As Group In match.Groups
Console.WriteLine(" Group {0}: {1}", groupCtr, group.Value)
groupCtr += 1
Dim captureCtr As Integer = 0
For Each capture As Capture In group.Captures
Console.WriteLine(" Capture {0}: {1}", captureCtr, capture.Value)
captureCtr += 1
Next
Next
Next
End Sub
End Module
' The example displays the following output:
' With implicit captures:
' The match: This is the first sentence.
' Group 0: This is the first sentence.
' Capture 0: This is the first sentence.
' Group 1: sentence
' Capture 0: This
' Capture 1: is
' Capture 2: the
' Capture 3: first
' Capture 4: sentence
' Group 2: sentence
' Capture 0: This
' Capture 1: is
' Capture 2: the
' Capture 3: first
' Capture 4: sentence
' The match: Is it the beginning of a literary masterpiece?
' Group 0: Is it the beginning of a literary masterpiece?
' Capture 0: Is it the beginning of a literary masterpiece?
' Group 1: masterpiece
' Capture 0: Is
' Capture 1: it
' Capture 2: the
' Capture 3: beginning
' Capture 4: of
' Capture 5: a
' Capture 6: literary
' Capture 7: masterpiece
' Group 2: masterpiece
' Capture 0: Is
' Capture 1: it
' Capture 2: the
' Capture 3: beginning
' Capture 4: of
' Capture 5: a
' Capture 6: literary
' Capture 7: masterpiece
' The match: I think not.
' Group 0: I think not.
' Capture 0: I think not.
' Group 1: not
' Capture 0: I
' Capture 1: think
' Capture 2: not
' Group 2: not
' Capture 0: I
' Capture 1: think
' Capture 2: not
' The match: Instead, it is a nonsensical paragraph.
' Group 0: Instead, it is a nonsensical paragraph.
' Capture 0: Instead, it is a nonsensical paragraph.
' Group 1: paragraph
' Capture 0: Instead,
' Capture 1: it
' Capture 2: is
' Capture 3: a
' Capture 4: nonsensical
' Capture 5: paragraph
' Group 2: paragraph
' Capture 0: Instead
' Capture 1: it
' Capture 2: is
' Capture 3: a
' Capture 4: nonsensical
' Capture 5: paragraph
'
' With explicit captures only:
' The match: This is the first sentence.
' Group 0: This is the first sentence.
' Capture 0: This is the first sentence.
' The match: Is it the beginning of a literary masterpiece?
' Group 0: Is it the beginning of a literary masterpiece?
' Capture 0: Is it the beginning of a literary masterpiece?
' The match: I think not.
' Group 0: I think not.
' Capture 0: I think not.
' The match: Instead, it is a nonsensical paragraph.
' Group 0: Instead, it is a nonsensical paragraph.
' Capture 0: Instead, it is a nonsensical paragraph.
También se puede usar el elemento insertado (?n) para suprimir las capturas automáticas. En el ejemplo
siguiente se modifica el patrón de expresión regular anterior para usar el elemento insertado (?n) en vez
de la opción RegexOptions.ExplicitCapture.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim input As String = "This is the first sentence. Is it the beginning " + _
"of a literary masterpiece? I think not. Instead, " + _
"it is a nonsensical paragraph."
Dim pattern As String = "(?n)\b\(?((?>\w+),?\s?)+[\.!?]\)?"
Finalmente, se puede usar el elemento de grupo insertado (?n:) para suprimir las capturas automáticas
grupo a grupo. En el ejemplo siguiente se modifica el patrón anterior para suprimir las capturas sin
nombre en el grupo externo, ((?>\w+),?\s?) . Observe que, de este modo, también se suprimen las
capturas sin nombre en el grupo interno.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim input As String = "This is the first sentence. Is it the beginning " + _
"of a literary masterpiece? I think not. Instead, " + _
"it is a nonsensical paragraph."
Dim pattern As String = "\b\(?(?n:(?>\w+),?\s?)+[\.!?]\)?"
NOTE
Una expresión regular solo se puede compilar si se suministra el valor RegexOptions.Compiled al parámetro
options de un constructor de clases Regex o de un método estático de coincidencia de patrones. No está
disponible como opción insertada.
Las expresiones regulares compiladas se pueden usar en llamadas a expresiones regulares estáticas y de
instancias. En expresiones regulares estáticas, la opción RegexOptions.Compiled se pasa al parámetro
options del método de coincidencia de patrones de expresión regular. En expresiones regulares de
instancia, se pasa al parámetro options del constructor de clases Regex. En ambos casos, tiene como
resultado una mejora en el rendimiento.
No obstante, esta mejora en el rendimiento solo se produce bajo las condiciones siguientes:
Un objeto Regex que representa una expresión regular concreta se usa en varias llamadas a
métodos de coincidencia de patrones de expresión regular.
El objeto Regex no puede estar fuera del ámbito y, por tanto, se puede volver a usar.
Una expresión regular estática se usa en varias llamadas a métodos de coincidencia de patrones de
expresión regular. (La mejora de rendimiento es posible porque el motor de expresiones regulares
almacena en la caché las expresiones regulares que se usan en llamadas de métodos estáticos).
NOTE
La opción RegexOptions.Compiled no está relacionada con el método Regex.CompileToAssembly, que crea un
ensamblado para fines especiales y que contiene expresiones regulares compiladas predefinidas.
El modelo es similar al modelo definido en la sección Solo capturas explícitas a excepción de que utiliza la
opción RegexOptions.IgnorePatternWhitespace para ignorar el espacio en blanco del modelo.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "This is the first sentence. Is it the beginning " + _
"of a literary masterpiece? I think not. Instead, " + _
"it is a nonsensical paragraph."
Dim pattern As String = "\b \(? ( (?>\w+) ,?\s? )+ [\.!?] \)? # Matches an entire sentence."
En el ejemplo siguiente se usa la opción insertada (?x) para ignorar el espacio en blanco del patrón.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "This is the first sentence. Is it the beginning " + _
"of a literary masterpiece? I think not. Instead, " + _
"it is a nonsensical paragraph."
Dim pattern As String = "(?x)\b \(? ( (?>\w+) ,?\s? )+ [\.!?] \)? # Matches an entire
sentence."
NOTE
El modo de patrón de derecha a izquierda solo está disponible si se suministra el valor RegexOptions.RightToLeft al
parámetro options de un constructor de clases Regex o de un método estático de coincidencia de patrones. No
está disponible como opción insertada.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\bb\w+\s"
Dim input As String = "builder rob rabble"
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.RightToLeft)
Console.WriteLine("'{0}' found at position {1}.", match.Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' 'builder ' found at position 0.
Observe también que la aserción de búsqueda anticipada (el elemento de lenguaje (?= subexpresión ) )
y la aserción de búsqueda tardía (el elemento de lenguaje (?<= subexpresión ) ) no cambian de dirección.
Las aserciones de búsqueda anticipada miran hacia la derecha, mientras que las de búsqueda tardía lo
hacen hacia la izquierda. Por ejemplo, la expresión regular (?<=\d{1,2}\s)\w+,?\s\d{4} usa la aserción de
búsqueda tardía para probar una fecha que precede al nombre de un mes. Después, la expresión regular
busca coincidencias con el mes y el año. Para información sobre aserciones de búsqueda anticipada y
tardía, consulte Construcciones de agrupamiento.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim inputs() As String = {"1 May 1917", "June 16, 2003"}
Dim pattern As String = "(?<=\d{1,2}\s)\w+,?\s\d{4}"
NOTE
El comportamiento conforme a ECMAScript solo está disponible si se suministra el valor RegexOptions.ECMAScript
al parámetro options de un constructor de clases Regex o de un método estático de coincidencia de patrones.
No está disponible como opción insertada.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim values() As String = {"целый мир", "the whole world"}
Dim pattern As String = "\b(\w+\s*)+"
For Each value In values
Console.Write("Canonical matching: ")
If Regex.IsMatch(value, pattern)
Console.WriteLine("'{0}' matches the pattern.", value)
Else
Console.WriteLine("{0} does not match the pattern.", value)
End If
Grupos de captura con autorreferencia. Una clase de captura de expresión regular con una
referencia inversa a sí misma debe actualizarse con cada iteración de captura. Como se muestra en
el ejemplo siguiente, esta característica permite a la expresión regular ((a+)(\1) ?)+ coincidir con
la cadena “ aa aaaa aaaaaa ” si se usa ECMAScript, pero no si se usa la búsqueda de coincidencias
canónica.
using System;
using System.Text.RegularExpressions;
Module Example
Dim pattern As String
C O M P O RTA M IEN TO DE
EXP RESIÓ N REGUL A R C O M P O RTA M IEN TO C A N Ó N IC O EC M A SC RIP T
\ seguido de un dígito de 1 a 9, Interprete los dígitos como un Para interpretarlo como una
seguido de dígitos decimales valor decimal. Si existe ese grupo referencia inversa, convierta todos
adicionales de captura, interprete la expresión los dígitos posibles en un valor
como una referencia inversa. decimal que pueda hacer
referencia a una captura. Si no se
De lo contrario, interprete los puede convertir ningún dígito,
dígitos octales iniciales hasta el interprételo como un octal; para
octal 377; es decir, tenga en ello, use los dígitos octales
cuenta solo los 8 bits inferiores iniciales hasta el octal 377 e
del valor. Interprete el resto de interprete el resto de dígitos
dígitos como literales. Por como literales.
ejemplo, en la expresión \3000 ,
si existe el grupo de captura 300,
interprételo como referencia
inversa de 300; si el grupo de
captura 300 no existe,
interprételo como un octal 300
seguido de 0.
Thread.CurrentThread.CurrentCulture = defaultCulture;
// The example displays the following output:
// Culture-sensitive matching (tr-TR culture)...
// Access to file://c:/Documents.MyReport.doc is allowed.
Thread.CurrentThread.CurrentCulture = defaultCulture
' The example displays the following output:
' Culture-sensitive matching (tr-TR culture)...
' Access to file://c:/Documents.MyReport.doc is allowed.
NOTE
Para obtener más información sobre la comparación de cadenas con distinción entre mayúsculas y minúsculas, y
con la referencia cultural de todos los idiomas, consulte Procedimientos recomendados para el uso de cadenas.
En vez de usar comparaciones sin distinción entre mayúsculas y minúsculas de la referencia cultural
actual, se puede especificar la opción RegexOptions.CultureInvariant para ignorar las diferencias culturales
de idioma y usar las convenciones de la referencia cultural de todos los idiomas.
NOTE
La comparación con la referencia cultural de todos los idiomas solo está disponible si se suministra el valor
RegexOptions.CultureInvariant al parámetro options de un constructor de clases Regex o de un método estático
de coincidencia de patrones. No está disponible como opción insertada.
El ejemplo siguiente es idéntico al anterior, excepto que se llama al método Regex.IsMatch(String, String,
RegexOptions) estático con opciones que incluyen RegexOptions.CultureInvariant. A pesar de que la
referencia cultural actual está establecida en turco (Turquía), el motor de expresiones regulares es capaz de
encontrar coincidencias con “FILE” y “file” y bloquear el acceso al recurso de archivo.
Console.WriteLine("Culture-insensitive matching...");
if (Regex.IsMatch(input, pattern,
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
Console.WriteLine("URLs that access files are not allowed.");
else
Console.WriteLine("Access to {0} is allowed.", input);
Thread.CurrentThread.CurrentCulture = defaultCulture;
// The example displays the following output:
// Culture-insensitive matching...
// URLs that access files are not allowed.
Console.WriteLine("Culture-insensitive matching...")
If Regex.IsMatch(input, pattern, _
RegexOptions.IgnoreCase Or RegexOptions.CultureInvariant) Then
Console.WriteLine("URLs that access files are not allowed.")
Else
Console.WriteLine("Access to {0} is allowed.", input)
End If
Thread.CurrentThread.CurrentCulture = defaultCulture
' The example displays the following output:
' Culture-insensitive matching...
' URLs that access files are not allowed.
Vea también
Lenguaje de expresiones regulares: referencia rápida
Construcciones misceláneas en expresiones regulares
16/09/2020 • 11 minutes to read • Edit Online
Las expresiones regulares en .NET incluyen tres construcciones de lenguaje misceláneas. Una permite habilitar o
deshabilitar opciones de coincidencia determinadas en medio de un patrón de expresión regular. Las otras dos
permiten incluir comentarios en una expresión regular.
Opciones insertadas
Puede establecer o deshabilitar opciones de coincidencia de patrones específicas para una parte de una expresión
regular mediante la sintaxis
(?imnsx-imnsx)
Indique las opciones que quiere habilitar después del signo de interrogación y las opciones que quiere
deshabilitar después del signo menos. En la siguiente tabla se describe cada una de las opciones. Para obtener
más información sobre cada opción, consulte Opciones de expresiones regulares.
O P C IÓ N DESC RIP C IÓ N
m Modo multilínea.
Cualquier cambio en las opciones de expresión regular definido mediante la construcción (?imnsx-imnsx)
permanece en vigor hasta el final del grupo envolvente.
NOTE
La construcción de agrupamiento (?imnsx-imnsx: subexpresión ) proporciona una funcionalidad idéntica para una
subexpresión. Para obtener más información, consulte Construcciones de agrupamiento.
En el ejemplo siguiente se usan las opciones i , n y x para habilitar las capturas explícitas y la opción que no
hace distinción entre mayúsculas y minúsculas, y para omitir el espacio en blanco del patrón de expresión regular
en medio de una expresión regular.
using System;
using System.Text.RegularExpressions;
pattern = @"\b(D\w+)\s(d\w+)\b";
// Match pattern using default options.
foreach (Match match in Regex.Matches(input, pattern))
{
Console.WriteLine(match.Value);
if (match.Groups.Count > 1)
for (int ctr = 1; ctr < match.Groups.Count; ctr++)
Console.WriteLine(" Group {0}: {1}", ctr, match.Groups[ctr].Value);
}
Console.WriteLine();
Module Example
Public Sub Main()
Dim pattern As String
Dim input As String = "double dare double Double a Drooling dog The Dreaded Deep"
pattern = "\b(D\w+)\s(d\w+)\b"
' Match pattern using default options.
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(match.Value)
If match.Groups.Count > 1 Then
For ctr As Integer = 1 To match.Groups.Count - 1
Console.WriteLine(" Group {0}: {1}", ctr, match.Groups(ctr).Value)
Next
End If
Next
Console.WriteLine()
En el ejemplo se definen dos expresiones regulares. La primera, \b(D\w+)\s(d\w+)\b , coincide con dos palabras
consecutivas que empiezan con una "D" mayúscula y una "d" minúscula. La segunda expresión regular,
\b(D\w+)(?ixn) \s (d\w+) \b , usa opciones insertadas para modificar este patrón, como se describe en la tabla
siguiente. Una comparación de los resultados confirma los efectos de la construcción (?ixn) .
Comentario alineado
La construcción (?# comment ) permite incluir un comentario alineado en una expresión regular. El motor de
expresiones regulares no usa ninguna parte del comentario en la coincidencia de patrones, aunque el comentario
se incluye en la cadena devuelta por el método Regex.ToString. El comentario termina en el primer paréntesis de
cierre.
En el ejemplo siguiente se repite el primer patrón de expresión regular del ejemplo de la sección anterior. Se
agregan dos comentarios alineados en la expresión regular para indicar si la comparación distingue entre
mayúsculas y minúsculas. El patrón de expresión regular,
\b((?# case-sensitive comparison)D\w+)\s(?ixn)((?#case-insensitive comparison)d\w+)\b , se define como se indica
a continuación.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b((?# case-sensitive comparison)D\w+)\s(?ixn)((?#case-insensitive
comparison)d\w+)\b"
Dim rgx As New Regex(pattern)
Dim input As String = "double dare double Double a Drooling dog The Dreaded Deep"
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "\{\d+(,-*\d+)*(\:\w{1,4}?)*\}(?x) # Looks for a composite format item."
Dim input As String = "{0,-3:F}"
Console.WriteLine("'{0}':", input)
If Regex.IsMatch(input, pattern) Then
Console.WriteLine(" contains a composite format item.")
Else
Console.WriteLine(" does not contain a composite format item.")
End If
End Sub
End Module
' The example displays the following output:
' '{0,-3:F}':
' contains a composite format item.
Tenga en cuenta que, en lugar de proporcionar la construcción (?x) en la expresión regular, el comentario
también podía haberse reconocido llamando al método Regex.IsMatch(String, String, RegexOptions) y pasando el
valor de enumeración RegexOptions.IgnorePatternWhitespace.
Vea también
Lenguaje de expresiones regulares: referencia rápida
Procedimientos recomendados con expresiones
regulares en .NET
16/09/2020 • 64 minutes to read • Edit Online
El motor de expresiones regulares de .NET es una herramienta eficaz y completa que procesa texto basándose en
coincidencias de patrones en lugar de comparar y buscar coincidencias con texto literal. En la mayoría de los casos,
realiza la coincidencia de modelos de manera rápida y eficaz. Sin embargo, en algunos casos, puede parecer que el
motor de expresiones regulares es muy lento. En casos extremos, incluso puede parecer que deja de responder
mientras procesa una entrada relativamente pequeña a lo largo de las horas o incluso los días.
En este tema se describen algunos de los procedimientos recomendados que los desarrolladores pueden adoptar
para garantizar que sus expresiones regulares alcancen un rendimiento óptimo.
WARNING
Cuando se usa System.Text.RegularExpressions para procesar entradas que no son de confianza, pase un tiempo de
expiración. Un usuario malintencionado puede proporcionar entradas a RegularExpressions y provocar un ataque por
denegación de servicio. Las API del marco ASP.NET Core en las que se usa RegularExpressions pasan un tiempo de
expiración.
Por ejemplo, considere una expresión regular de uso muy frecuente pero sumamente problemática para validar el
alias de una dirección de correo electrónico. Se escribe la expresión regular ^[0-9A-Z]([-.\w]*[0-9A-Z])*$ para
procesar qué se considera una dirección de correo electrónico válida, que consta de un carácter alfanumérico
seguido de cero o más caracteres que pueden ser alfanuméricos, puntos o guiones. La expresión regular debe
finalizar con un carácter alfanumérico. Sin embargo, como se muestra en el ejemplo siguiente, aunque esta
expresión regular trata la entrada válida fácilmente, su rendimiento es muy ineficaz cuando está procesando datos
de entrada casi válidos.
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
Imports System.Diagnostics
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim sw As Stopwatch
Dim addresses() As String = {"AAAAAAAAAAA@contoso.com",
"AAAAAAAAAAaaaaaaaaaa!@contoso.com"}
' The following regular expression should not actually be used to
' validate an email address.
Dim pattern As String = "^[0-9A-Z]([-.\w]*[0-9A-Z])*$"
Dim input As String
Como muestra el resultado del ejemplo, el motor de expresiones regulares procesa el alias válido de correo
electrónico casi en el mismo intervalo de tiempo independientemente de su longitud. Por otra parte, cuando la
dirección de correo electrónico casi válida tiene más de cinco caracteres, el tiempo de procesamiento se duplica
aproximadamente por cada carácter de la cadena. Esto significa que una cadena casi válida de 28 caracteres
tardaría más de una hora en procesarse y una cadena casi válida de 33 caracteres tardaría casi un día en
procesarse.
Como esta expresión regular se desarrolló teniendo en cuenta solamente el formato de entrada que había que
hacer coincidir, no tiene en cuenta los datos de entrada que no coinciden con el patrón. A su vez, esto puede
permitir que unos datos de entrada sin restricciones que casi coinciden con el patrón de expresión regular reduzcan
considerablemente el rendimiento.
Para resolver este problema, puede hacer lo siguiente:
A la hora de desarrollar un modelo, debe considerar cómo puede afectar el retroceso al rendimiento del
motor de expresiones regulares, especialmente si la expresión regular está diseñada para procesar datos de
entrada sin restricciones. Para obtener más información, consulte la sección Controlar el retroceso.
Probar exhaustivamente la expresión regular usando datos de entrada no válidos y casi válidos, así como
datos de entrada válidos. Para generar de forma aleatoria la entrada para una expresión regular
determinada, puede usar Rex, que es una herramienta de exploración de expresiones regulares de Microsoft
Research.
Puede acoplar el motor de expresiones regulares con un determinado patrón de expresión regular y, a
continuación, usar el motor para buscar coincidencias con texto de varias maneras:
Puede llamar a un método estático de coincidencia de patrones como Regex.Match(String, String). Para ello
no es necesario crear instancias de un objeto de expresión regular.
Puede crear instancias de un objeto Regex y llamar a una instancia de un método de coincidencia de
modelos de una expresión regular interpretada. Este es el método predeterminado para enlazar el motor de
expresiones regulares a un patrón de expresión regular. Se produce cuando se crea una instancia de un
objeto Regex sin un argumento options que incluya la marca Compiled.
Puede crear instancias de un objeto Regex y llamar a una instancia de un método de coincidencia de
modelos de una expresión regular compilada. Los objetos de expresiones regulares representan modelos
compilados cuando se crea una instancia de un objeto Regex con un argumento options que incluye la
marca Compiled.
Puede crear un objeto Regex especial que esté acoplado estrechamente con un determinado patrón de
expresión regular, compilarlo y guardarlo en un ensamblado independiente. Puede hacerlo llamando al
método Regex.CompileToAssembly.
La forma de llamar a los métodos de coincidencia de expresiones regulares puede tener un impacto significativo en
la aplicación. En las próximas secciones se explica cómo usar llamadas a métodos estáticos, expresiones regulares
interpretadas y expresiones regulares compiladas para mejorar el rendimiento de la aplicación.
IMPORTANT
El formato de la llamada al método (estático, interpretado o compilado) afecta al rendimiento si la misma expresión regular se
usa repetidamente en llamadas a métodos o si una aplicación usa muchos objetos de expresiones regulares.
En el ejemplo siguiente se muestra una implementación muy poco eficaz del método IsValidCurrency . Observe
que cada llamada al método vuelve a crear una instancia de un objeto Regex con el mismo modelo. Esto, a su vez,
significa que el patrón de expresión regular se debe volver a compilar cada vez que se llama al método.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Debe reemplazar este código ineficaz con una llamada al método estático Regex.IsMatch(String, String). Esto
elimina la necesidad de crear instancias de un objeto Regex cada vez que desea llamar a un método de coincidencia
de modelos y permite que el motor de expresiones regulares recupere una versión compilada de la expresión
regular de su memoria caché.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
De forma predeterminada, se almacenan en caché los 15 últimos patrones de expresiones regulares estáticas
usados recientemente. En el caso de las aplicaciones que necesitan un mayor número de expresiones regulares
estáticas almacenadas en caché, el tamaño de la memoria caché se puede ajustar estableciendo la propiedad
Regex.CacheSize.
La expresión regular \p{Sc}+\s*\d+ que se usa en este ejemplo comprueba que la cadena de entrada consta de un
símbolo de moneda y al menos un dígito decimal. El patrón se define como se muestra en la tabla siguiente.
using System;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
Imports System.Diagnostics
Imports System.IO
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b(\w+((\r?\n)|,?\s))*\w+[.?:;!]"
Dim sw As Stopwatch
Dim match As Match
Dim ctr As Integer
using System;
using System.Reflection;
using System.Text.RegularExpressions;
Imports System.Reflection
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim SentencePattern As New RegexCompilationInfo("\b(\w+((\r?\n)|,?\s))*\w+[.?:;!]",
RegexOptions.Multiline,
"SentencePattern",
"Utilities.RegularExpressions",
True)
Dim regexes() As RegexCompilationInfo = {SentencePattern}
Dim assemName As New AssemblyName("RegexLib, Version=1.0.0.1001, Culture=neutral, PublicKeyToken=null")
Regex.CompileToAssembly(regexes, assemName)
End Sub
End Module
Cuando el ejemplo se compila en un ejecutable y se ejecuta, crea un ensamblado denominado RegexLib.dll . La
expresión regular se representa mediante una clase denominada Utilities.RegularExpressions.SentencePattern
que se deriva de Regex. En el ejemplo siguiente, se usa después la expresión regular compilada para extraer las
frases del texto The Financier de Theodore Dreiser.
using System;
using System.IO;
using System.Text.RegularExpressions;
using Utilities.RegularExpressions;
Imports System.IO
Imports System.Text.RegularExpressions
Imports Utilities.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As New SentencePattern()
Dim inFile As New StreamReader(".\Dreiser_TheFinancier.txt")
Dim input As String = inFile.ReadToEnd()
inFile.Close()
Controlar el retroceso
Normalmente, el motor de expresiones regulares usa la progresión lineal para desplazarse a través de una cadena
de entrada y compararla con un patrón de expresión regular. Sin embargo, cuando en un patrón de expresión
regular se usan cuantificadores indeterminados como * , + y ? , el motor de expresiones regulares puede
abandonar una parte de las coincidencias parciales correctas y volver a un estado guardado previamente para
buscar una coincidencia correcta de todo el patron. Este proceso se denomina retroceso.
NOTE
Para obtener más información acerca del retroceso, consulte Detalles del comportamiento de expresiones regulares y
Retroceso. Para obtener una explicación detallada del retroceso, vea Optimizing Regular Expression Performance, Part II:
(Optimización del rendimiento de expresiones regulares, Parte II: Control del retroceso) en el blog del equipo de BCL.
La compatibilidad con el retroceso aporta a las expresiones regulares eficacia y flexibilidad. También deja la
responsabilidad de controlar el funcionamiento del motor de expresiones regulares en manos de los
desarrolladores de expresiones regulares. Puesto que los desarrolladores no suelen ser conscientes de esta
responsabilidad, su uso incorrecto del retroceso o su dependencia de un retroceso excesivo suele desempeñar el
rol más significativo en la degradación del rendimiento de las expresiones regulares. En un escenario de caso peor,
el tiempo de ejecución puede duplicarse por cada carácter adicional de la cadena de entrada. De hecho, usando
excesivamente el retroceso, es fácil crear el equivalente en programación de un bucle infinito si la entrada coincide
casi con el patrón de expresiones regulares; el motor de expresiones regulares puede tardar horas o incluso días en
procesar una cadena de entrada relativamente corta.
A menudo, las aplicaciones sufren una reducción del rendimiento por usar el retroceso a pesar de que el retroceso
no es esencial para una coincidencia. Por ejemplo, la expresión regular \b\p{Lu}\w*\b busca una coincidencia con
todas las palabras que comienzan por un carácter en mayúsculas, como se muestra en la tabla siguiente.
Puesto que un límite de palabra no es igual, o un subconjunto de, que un carácter alfabético, no hay ninguna
posibilidad de que el motor de expresiones regulares cruce un límite de palabra cuando busca coincidencias con
caracteres alfabéticos. Esto significa que para esta expresión regular, el retroceso nunca puede contribuir al éxito
global de cualquier coincidencia; solo puede degradar el rendimiento, ya que se fuerza que el motor de expresiones
regulares guarde su estado para cada coincidencia preliminar correcta de un carácter alfabético.
Si determina que la vuelta atrás (backtracking) no es necesaria, puede deshabilitarla mediante el elemento de
lenguaje (?>subexpression) , conocido como grupo atómico. En el ejemplo siguiente se analiza una cadena de
entrada usando dos expresiones regulares. La primera, \b\p{Lu}\w*\b , se basa en el retroceso. La segunda,
\b\p{Lu}(?>\w*)\b , deshabilita el retroceso. Como muestra el resultado del ejemplo, ambas producen el mismo
resultado.
using System;
using System.Text.RegularExpressions;
Console.WriteLine();
pattern = @"\b\p{Lu}(?>\w*)\b";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// This
// Sentence
// Capital
//
// This
// Sentence
// Capital
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "This this word Sentence name Capital"
Dim pattern As String = "\b\p{Lu}\w*\b"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(match.Value)
Next
Console.WriteLine()
pattern = "\b\p{Lu}(?>\w*)\b"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(match.Value)
Next
End Sub
End Module
' The example displays the following output:
' This
' Sentence
' Capital
'
' This
' Sentence
' Capital
En muchos casos, el retroceso es esencial para buscar una coincidencia de un patrón de expresión regular con el
texto de entrada. Sin embargo, el retroceso excesivo puede degradar gravemente el rendimiento y dar la impresión
de que una aplicación ha dejado de responder. En concreto, esto ocurre cuando se anidan los cuantificadores y el
texto que coincide con la subexpresión externa es un subconjunto del texto que coincide con la subexpresión
interna.
WARNING
Además de evitar el retroceso excesivo, se debe utilizar la característica de tiempo de espera para garantizar que el retroceso
excesivo no reduzca gravemente el rendimiento de la expresión regular. Para obtener más información, consulte la sección
Usar valores de tiempo de espera.
Por ejemplo, el patrón de expresión regular ^[0-9A-Z]([-.\w]*[0-9A-Z])*\$$ está diseñado para buscar
coincidencias con un número de pieza que contiene al menos un carácter alfanumérico. Cualquier carácter
adicional puede constar de un carácter alfanumérico, un guión, un carácter de subrayado o un punto, aunque el
último carácter debe ser alfanumérico. El número de pieza termina con un signo de dólar. En algunos casos, este
patrón de expresión regular puede presentar un rendimiento muy deficiente porque los cuantificadores están
anidados y porque la subexpresión [0-9A-Z] es un subconjunto de la subexpresión [-.\w]* .
En estos casos, puede optimizar el rendimiento de la expresión regular quitando los cuantificadores anidados y
reemplazando la subexpresión externa con una aserción de búsqueda anticipada o de búsqueda tardía de ancho
cero. Las aserciones de búsqueda anticipada y de búsqueda tardía son delimitadores; no mueven el puntero en la
cadena de entrada, sino que realizan una búsqueda hacia delante o hacia atrás para comprobar si se cumple una
condición especificada. Por ejemplo, la expresión regular de número de pieza se puede volver a escribir como
^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$ . Este patrón de expresión regular se define como se muestra en la tabla
siguiente.
(?<=[0-9A-Z]) Realizar una búsqueda anticipada del signo de dólar final para
asegurarse de que el carácter anterior es alfanumérico.
En el ejemplo siguiente se muestra el uso de esta expresión regular para encontrar una matriz que contiene los
números de pieza posibles.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$"
Dim partNos() As String = {"A1C$", "A4", "A4$", "A1603D$",
"A1603D#"}
El lenguaje de expresiones regulares de .NET incluye los elementos del lenguaje siguientes, que puede usar para
eliminar cuantificadores anidados. Para obtener más información, consulte Construcciones de agrupamiento.
(?<= subexpression ) Búsqueda tardía positiva de ancho cero. Realizar una búsqueda
tardía de la posición actual para determinar si
subexpression coincide con la cadena de entrada.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
List<string> exclusions = new List<string>( new string[] { "a", "an", "the" });
int[] wordLengths = new int[29]; // Allocate an array of more than ample size.
string input = null;
StreamReader sr = null;
try {
sr = new StreamReader(filename);
input = sr.ReadToEnd();
}
catch (FileNotFoundException e) {
string msg = String.Format("Unable to find the file '{0}'", filename);
throw new IOException(msg, e);
}
catch (IOException e) {
throw new IOException(e.Message, e);
}
finally {
if (sr != null) sr.Close();
}
indexPos += m.Length + 1;
}
}
}
catch (RegexMatchTimeoutException e) {
if (e.MatchTimeout.TotalMilliseconds < MAX_TIMEOUT) {
timeoutInterval += INCREMENT;
init = false;
}
else {
// Rethrow the exception.
throw;
}
}
} while (m.Success);
Imports System.Collections.Generic
Imports System.IO
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim util As New RegexUtilities()
Dim title As String = "Doyle - The Hound of the Baskervilles.txt"
Try
Dim info = util.GetWordData(title)
Console.WriteLine("Words: {0:N0}", info.Item1)
Console.WriteLine("Average Word Length: {0:N2} characters", info.Item2)
Catch e As IOException
Console.WriteLine("IOException reading file '{0}'", title)
Console.WriteLine(e.Message)
Catch e As RegexMatchTimeoutException
Console.WriteLine("The operation timed out after {0:N0} milliseconds",
e.MatchTimeout.TotalMilliseconds)
End Try
End Sub
End Module
' If regex completed successfully, calculate number of words and average length.
Dim nWords As Integer
Dim totalLength As Long
Como se muestra en el ejemplo siguiente, cuando se encuentra una coincidencia, los objetos GroupCollection y
CaptureCollection se rellenan con capturas de la coincidencia. En este caso, el grupo de captura (\w+[;,]?\s?)
existe para que se le pueda aplicar el cuantificador + , que permite que el patrón de expresión regular coincida con
cada palabra de una frase. De lo contrario, coincidiría con la última palabra de una frase.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim input As String = "This is one sentence. This is another."
Dim pattern As String = "\b(\w+[;,]?\s?)+[.?!]"
Cuando use subexpresiones solo para aplicarles cuantificadores y no le interese el texto capturado, debe
deshabilitar las capturas de grupo. Por ejemplo, el elemento de lenguaje (?:subexpression) impide al grupo al que
se aplica que capture subcadenas coincidentes. En el ejemplo siguiente, el patrón de expresión regular del ejemplo
anterior se cambia a \b(?:\w+[;,]?\s?)+[.?!] . Como muestra el resultado, evita que el motor de expresiones
regulares rellene las colecciones GroupCollection y CaptureCollection.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim input As String = "This is one sentence. This is another."
Dim pattern As String = "\b(?:\w+[;,]?\s?)+[.?!]"
Temas relacionados
T IT L E DESC RIP C IÓ N
Detalles del comportamiento de expresiones regulares Examina la implementación del motor de expresiones regulares
de .NET. El tema se centra en la flexibilidad de las expresiones
regulares y explica la responsabilidad del desarrollador para
garantizar un funcionamiento eficaz y sólido del motor de
expresiones regulares.
Lenguaje de expresiones regulares: referencia rápida Describe los elementos del lenguaje de expresiones regulares
de .NET y proporciona vínculos a documentación detallada
sobre cada elemento del lenguaje.
El modelo de objetos de expresión regular
16/09/2020 • 45 minutes to read • Edit Online
En este tema se describe el modelo de objetos usado para trabajar con expresiones regulares de .NET. Contiene las
siguientes secciones:
Motor de expresiones regulares
Objetos MatchCollection y Match
Colección de grupos
Grupo capturado
Colección de capturas
Captura individual
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim values() As String = {"111-22-3333", "111-2-3333"}
Dim pattern As String = "^\d{3}-\d{2}-\d{4}$"
For Each value As String In values
If Regex.IsMatch(value, pattern) Then
Console.WriteLine("{0} is a valid SSN.", value)
Else
Console.WriteLine("{0}: Invalid", value)
End If
Next
End Sub
End Module
' The example displays the following output:
' 111-22-3333 is a valid SSN.
' 111-2-3333: Invalid
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "This is a a farm that that raises dairy cattle."
Dim pattern As String = "\b(\w+)\W+(\1)\b"
Dim match As Match = Regex.Match(input, pattern)
Do While match.Success
Console.WriteLine("Duplicate '{0}' found at position {1}.", _
match.Groups(1).Value, match.Groups(2).Index)
match = match.NextMatch()
Loop
End Sub
End Module
' The example displays the following output:
' Duplicate 'a' found at position 10.
' Duplicate 'that' found at position 22.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "This is a a farm that that raises dairy cattle."
Dim pattern As String = "\b(\w+)\W+(\1)\b"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("Duplicate '{0}' found at position {1}.", _
match.Groups(1).Value, match.Groups(2).Index)
Next
End Sub
End Module
' The example displays the following output:
' Duplicate 'a' found at position 10.
' Duplicate 'that' found at position 22.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b\d+\.\d{2}\b"
Dim replacement As String = "$$$&"
Dim input As String = "Total Cost: 103.64"
Console.WriteLine(Regex.Replace(input, pattern, replacement))
End Sub
End Module
' The example displays the following output:
' Total Cost: $103.64
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "1. Eggs 2. Bread 3. Milk 4. Coffee 5. Tea"
Dim pattern As String = "\b\d{1,2}\.\s"
For Each item As String In Regex.Split(input, pattern)
If Not String.IsNullOrEmpty(item) Then
Console.WriteLine(item)
End If
Next
End Sub
End Module
' The example displays the following output:
' Eggs
' Bread
' Milk
' Coffee
' Tea
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim matches As MatchCollection
Dim results As New List(Of String)
Dim matchposition As New List(Of Integer)
' Create a new Regex object and define the regular expression.
Dim r As New Regex("abc")
' Use the Matches method to find all matches in the input string.
matches = r.Matches("123abc4abcd")
' Enumerate the collection to retrieve all matches and positions.
For Each match As Match In matches
' Add the match string to the string array.
results.Add(match.Value)
' Record the character position where the match was found.
matchposition.Add(match.Index)
Next
' List the results.
For ctr As Integer = 0 To results.Count - 1
Console.WriteLine("'{0}' found at position {1}.", _
results(ctr), matchposition(ctr))
Next
End Sub
End Module
' The example displays the following output:
' 'abc' found at position 3.
' 'abc' found at position 7.
La coincidencia
La clase Match representa el resultado de una coincidencia de expresión regular única. Puede tener acceso a los
objetos Match de dos formas:
Recuperándolos del objeto MatchCollection devuelto por el método Regex.Matches. Para recuperar objetos
Match individuales, itere la colección utilizando una construcción foreach (en C#) o For Each ... Next (en
Visual Basic) o use la propiedad MatchCollection.Item[] para recuperar un determinado objeto Match por
índice o por nombre. También puede recuperar objetos Match individuales de la colección iterando la
colección por índice, desde cero a uno menos que el número de objetos de la colección. Sin embargo, este
método no saca partido de la evaluación diferida, porque tiene acceso a la propiedad
MatchCollection.Count.
En el siguiente ejemplo se recuperan objetos Match individuales de un objeto MatchCollection iterando la
colección mediante la construcción foreach o For Each ... Next . La expresión regular simplemente
coincide con la cadena "abc" de la cadena de entrada.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "abc"
Dim input As String = "abc123abc456abc789"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("{0} found at position {1}.", _
match.Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' abc found at position 0.
' abc found at position 6.
' abc found at position 12.
Llamando al método Regex.Match, que devuelve un objeto Match que representa la primera coincidencia
en una cadena o una parte de una cadena. Puede determinar si la coincidencia se ha encontrado
recuperando el valor de la propiedad Match.Success . Para recuperar objetos Match que representan las
coincidencias subsiguientes, llame repetidamente al método Match.NextMatch, hasta que la propiedad
Success del objeto Match devuelto sea false .
En el siguiente ejemplo se utilizan los métodos Regex.Match(String, String) y Match.NextMatch para buscar
coincidencias con la cadena "abc" en la cadena de entrada.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "abc"
Dim input As String = "abc123abc456abc789"
Dim match As Match = Regex.Match(input, pattern)
Do While match.Success
Console.WriteLine("{0} found at position {1}.", _
match.Value, match.Index)
match = match.NextMatch()
Loop
End Sub
End Module
' The example displays the following output:
' abc found at position 0.
' abc found at position 6.
' abc found at position 12.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b\d+(,\d{3})*\.\d{2}\b"
Dim input As String = "16.32" + vbCrLf + "194.03" + vbCrLf + "1,903,672.08"
El patrón de reemplazo $$ $& indica que la subcadena debe ser reemplazada por un símbolo de signo de dólar
($) (el patrón $$ ), un espacio y el valor de la coincidencia (el patrón $& ).
Volver al principio
La colección de grupos
La propiedad Match.Groups devuelve un objeto GroupCollection que contiene objetos Group que representan los
grupos capturados en una coincidencia única. El primer objeto Group de la colección (en el índice 0) representa la
coincidencia completa. Cada objeto que sigue representa los resultados de un grupo de captura único.
Puede recuperar objetos Group individuales en la colección utilizando la propiedad GroupCollection.Item[]. Puede
recuperar los grupos sin nombre por su posición ordinal en la colección y recuperar grupos con nombre por
nombre o por posición ordinal. Las capturas sin nombre aparecen primero en la colección y se indizan de
izquierda a derecha en el orden en el que aparecen en el patrón de expresión regular. Las capturas con nombre se
indizan después de las capturas sin nombre, de izquierda a derecha en el orden en el que aparecen en el patrón de
expresión regular. Para determinar qué grupos numerados están disponibles en la colección devuelta para un
determinado método de coincidencia de expresión regular, puede llamar al método Regex.GetGroupNumbers de
instancia. Para determinar qué grupos con nombre están disponibles en la colección, puede llamar al método
Regex.GetGroupNames de instancia. Ambos métodos son especialmente útiles en rutinas de uso general que
analizan las coincidencias encontradas por cualquier expresión regular.
La propiedad GroupCollection.Item[] es el indizador de la colección en C# y la propiedad predeterminada del
objeto de la colección en Visual Basic. Esto significa que se puede tener acceso a los objetos Group individuales
por índice (o por nombre, en el caso de grupos con nombre) del modo siguiente:
En el siguiente ejemplo se define una expresión regular que usa construcciones de agrupación para capturar el
mes, día y año de una fecha.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b(\w+)\s(\d{1,2}),\s(\d{4})\b"
Dim input As String = "Born: July 28, 1989"
Dim match As Match = Regex.Match(input, pattern)
If match.Success Then
For ctr As Integer = 0 To match.Groups.Count - 1
Console.WriteLine("Group {0}: {1}", ctr, match.Groups(ctr).Value)
Next
End If
End Sub
End Module
' The example displays the following output:
' Group 0: July 28, 1989
' Group 1: July
' Group 2: 28
' Group 3: 1989
Volver al principio
El grupo capturado
La clase Group representa el resultado de un único grupo de captura. La propiedad Item[] del objeto
GroupCollection devuelto por la propiedad Match.Groups devuelve objetos de grupo que representan grupos de
captura definidos en una expresión regular. La propiedad Item[] es el indizador (en C#) y la propiedad
predeterminada (en Visual Basic) de la clase Group. También puede recuperar miembros individuales mediante la
iteración de la colección con la construcción foreach o For Each . Para obtener un ejemplo, vea la sección
anterior.
En el siguiente ejemplo se utilizan construcciones de agrupación anidadas para capturar subcadenas en grupos. El
patrón de expresión regular (a(b))c coincide con la cadena "abc". Asigna la subcadena "ab" al primer grupo de
captura y la subcadena "b" al segundo grupo de captura.
En el siguiente ejemplo se utilizan construcciones de agrupación con nombre para capturar subcadenas de una
cadena que contiene datos en el formato "NOMBREDATOS:VALOR" que la expresión regular divide por el signo de
dos puntos (:).
Success false
Value String.Empty
Length 0
Esto se muestra en el ejemplo siguiente. En el patrón de expresión regular aaa(bbb)*ccc , se pueden buscar
coincidencias para el primer grupo de captura (la subcadena "bbb") cero o varias veces. Dado que la cadena
de entrada "aaaccc" coincide con el patrón, el grupo de captura no tiene una coincidencia.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "aaa(bbb)*ccc"
Dim input As String = "aaaccc"
Dim match As Match = Regex.Match(input, pattern)
Console.WriteLine("Match value: {0}", match.Value)
If match.Groups(1).Success Then
Console.WriteLine("Group 1 value: {0}", match.Groups(1).Value)
Else
Console.WriteLine("The first capturing group has no match.")
End If
End Sub
End Module
' The example displays the following output:
' Match value: aaaccc
' The first capturing group has no match.
Los cuantificadores pueden coincidir con varias apariciones de un patrón definido por un grupo de captura.
En este caso, las propiedades Value y Length de un objeto Group solo contienen información sobre la
última subcadena capturada. Por ejemplo, la siguiente coincidencia de expresión regular coincide con una
frase única que finaliza con un punto. La expresión utiliza dos construcciones de agrupación: la primera
captura palabras individuales junto con un carácter de espacio en blanco; la segunda captura palabras
individuales. Como lo muestra el resultado del ejemplo, aunque la expresión regular logre capturar una
frase completa, el segundo grupo de captura solo captura la última palabra.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "\b((\w+)\s?)+\."
Dim input As String = "This is a sentence. This is another sentence."
Dim match As Match = Regex.Match(input, pattern)
If match.Success Then
Console.WriteLine("Match: " + match.Value)
Console.WriteLine("Group 2: " + match.Groups(2).Value)
End If
End Sub
End Module
' The example displays the following output:
' Match: This is a sentence.
' Group 2: sentence
Volver al principio
La colección de capturas
El objeto Group solo contiene información sobre la última captura. Sin embargo, el conjunto completo de capturas
realizado por un grupo de captura sigue aún disponible en el objeto CaptureCollection devuelto por la propiedad
Group.Captures. Cada miembro de la colección es un objeto Capture que representa una captura realizada por
este grupo de captura, en el orden en el que se capturaron (y, por consiguiente, en el orden en el que las cadenas
capturadas coincidían de izquierda a derecha en la cadena de entrada). Puede recuperar objetos Capture
individuales de la colección de estas dos formas:
Mediante la iteración de la colección con el uso de una construcción como foreach (en C#) o For Each (en
Visual Basic).
Utilizando la propiedad CaptureCollection.Item[] para recuperar un objeto específico por índice. La
propiedad Item[] es la propiedad predeterminada del objeto CaptureCollection (en Visual Basic) o el
indizador (en C#).
Si no se aplica un cuantificador a un grupo de captura, el objeto CaptureCollection contiene un objeto Capture
único que es de escaso interés, porque proporciona información sobre la misma coincidencia que su objeto
Group. Si se aplica un cuantificador a un grupo de captura, el objeto CaptureCollection contiene todas las capturas
realizadas por el grupo de captura y el último miembro de la colección representa la misma captura que el objeto
Group.
Si utiliza, por ejemplo, el patrón de expresión regular ((a(b))c)+ (donde el cuantificador + indica una o varias
coincidencias) para capturar coincidencias de la cadena "abcabcabc", el objeto CaptureCollection para cada objeto
Group contiene tres miembros.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim pattern As String = "((a(b))c)+"
Dim input As STring = "abcabcabc"
En el siguiente ejemplo se utiliza la expresión regular (Abc)+ para buscar una o más ejecuciones consecutivas de
la cadena "Abc" en la cadena "XYZAbcAbcAbcXYZAbcAb". El ejemplo ilustra la forma en que se utiliza la propiedad
Group.Captures para que devuelva varios grupos de subcadenas capturadas.
int counter;
Match m;
CaptureCollection cc;
GroupCollection gc;
Volver al principio
La captura individual
La clase Capture contiene el resultado de una única captura de subexpresiones. La propiedad Capture.Value
contiene el texto coincidente y la propiedad Capture.Index indica la posición basada en cero en la cadena de
entrada en la que la comienza la subcadena coincidente.
En el siguiente ejemplo se analiza una cadena de entrada para la temperatura de las ciudades seleccionadas. Se
utiliza una coma (",") para separar una ciudad y su temperatura, y un punto y coma (";"), para separar los datos de
cada ciudad. La cadena de entrada completa representa una coincidencia única. En el patrón de expresión regular
((\w+(\s\w+)*),(\d+);)+ , que se utiliza para analizar la cadena, se asigna el nombre de la ciudad al segundo
grupo de captura y la temperatura al cuarto grupo de captura.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "Miami,78;Chicago,62;New York,67;San Francisco,59;Seattle,58;"
Dim pattern As String = "((\w+(\s\w+)*),(\d+);)+"
Dim match As Match = Regex.Match(input, pattern)
If match.Success Then
Console.WriteLine("Current temperatures:")
For ctr As Integer = 0 To match.Groups(2).Captures.Count - 1
Console.WriteLine("{0,-20} {1,3}", match.Groups(2).Captures(ctr).Value, _
match.Groups(4).Captures(ctr).Value)
Next
End If
End Sub
End Module
' The example displays the following output:
' Current temperatures:
' Miami 78
' Chicago 62
' New York 67
' San Francisco 59
' Seattle 58
(\w+(\s\w+)*) Coincide con uno o varios caracteres que se usan para formar
palabras seguidos de cero o más apariciones de un carácter
de espacio en blanco seguidos de uno o varios caracteres que
se usan para formar palabras. Este es el segundo grupo de
captura.
Vea también
System.Text.RegularExpressions
Expresiones regulares de .NET
Lenguaje de expresiones regulares: referencia rápida
Detalles del comportamiento de expresiones
regulares
16/09/2020 • 30 minutes to read • Edit Online
NOTE
Para obtener información sobre la reducción del rendimiento que causa un retroceso excesivo y sobre las maneras de crear
una expresión regular para solucionarlo, consulte Retroceso.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim greedyPattern As String = ".+(\d+)\."
Dim lazyPattern As String = ".+?(\d+)\."
Dim input As String = "This sentence ends with the number 107325."
Dim match As Match
Las versiones expansiva y diferida de esta expresión regular se definen como se muestra en la tabla
siguiente:
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b[A-Z]+\b(?=\P{P})"
Dim input As String = "If so, what comes next?"
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
Console.WriteLine(match.Value)
Next
End Sub
End Module
' The example displays the following output:
' If
' what
' comes
Para obtener más información sobre las aserciones de búsqueda anticipada positiva, consulte
Construcciones de agrupamiento.
Búsqueda anticipada negativa: (?! subexpresión ) . Esta característica permite coincidir con una expresión
solo si no se produce una coincidencia con una subexpresión. Esto es eficaz a fin de restringir una
búsqueda, ya que a menudo resulta más sencillo proporcionar una expresión para un caso que se debe
eliminar, en lugar de una expresión para los casos que se deben incluir. Por ejemplo, es difícil escribir una
expresión para buscar palabras que no comienzan por "non". En el ejemplo siguiente se usa la búsqueda
anticipada negativa para excluirlas.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b(?!non)\w+\b"
Dim input As String = "Nonsense is not always non-functional."
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
Console.WriteLine(match.Value)
Next
End Sub
End Module
' The example displays the following output:
' is
' not
' always
' functional
Para obtener más información sobre las aserciones de búsqueda anticipada negativa, consulte
Construcciones de agrupamiento.
Evaluación condicional: (?( expresión ) sí | no ) y (?( nombre ) sí | no ) , donde expresión es una
subexpresión que debe coincidir, nombre es el nombre de un grupo de capturas, sí es la cadena que debe
coincidir si expresión coincide o nombre es un grupo capturado válido y no vacío, y no es la subexpresión
que debe coincidir si expresión no coincide o si nombre no es un grupo capturado válido y no vacío. Esta
característica permite al motor buscar mediante más de un patrón alternativo, según el resultado de una
búsqueda de coincidencia de subexpresión anterior o el resultado de una aserción de ancho cero. Esto
posibilita una forma más eficaz de referencia inversa que permite, por ejemplo, coincidir con una
subexpresión en función de si se produjo una coincidiencia con una subexpresión anterior. La expresión
regular del ejemplo siguiente coincide con párrafos que están pensados tanto para un uso público como
interno. Los párrafos destinados únicamente al uso interno empiezan con una etiqueta <PRIVATE> . El
patrón de expresión regular ^(?<Pvt>\<PRIVATE\>\s)?(?(Pvt)((\w+\p{P}?\s)+)|((\w+\p{P}?\s)+))\r?$ usa la
evaluación condicional para asignar el contenido de los párrafos pensados para el uso público y para el uso
interno a grupos de capturas independientes. Después, estos párrafos se pueden tratar de forma diferente.
using System;
using System.Text.RegularExpressions;
Console.WriteLine("Private Document:");
Console.WriteLine(privateDocument);
Console.WriteLine("Public Document:");
Console.WriteLine(publicDocument);
}
}
// The example displays the following output:
// Private Document:
// This is not for public consumption.
// But this is for public consumption.
// Again, this is confidential.
//
// Public Document:
// But this is for public consumption.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "<PRIVATE> This is not for public consumption." + vbCrLf + _
"But this is for public consumption." + vbCrLf + _
"<PRIVATE> Again, this is confidential." + vbCrLf
Dim pattern As String = "^(?<Pvt>\<PRIVATE\>\s)?(?(Pvt)((\w+\p{P}?\s)+)|((\w+\p{P}?\s)+))\r?$"
Dim publicDocument As String = Nothing
Dim privateDocument As String = Nothing
Console.WriteLine("Private Document:")
Console.WriteLine(privateDocument)
Console.WriteLine("Public Document:")
Console.WriteLine(publicDocument)
End Sub
End Module
' The example displays the following output:
' Private Document:
' This is not for public consumption.
' But this is for public consumption.
' Again, this is confidential.
'
' Public Document:
' But this is for public consumption.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim inputs() As String = {"aaaaa", "aaaaab"}
Dim backtrackingPattern As String = "(a+)\w"
Dim match As Match
La expresión regular ((?>a+))\w impide este comportamiento. Dado que todos los caracteres "a"
consecutivos se buscan sin retroceso, el primer grupo de capturas incluye todos los caracteres "a"
consecutivos. Si los caracteres "a" no van seguidos de al menos otro carácter que no sea "a", se produce un
error de coincidencia.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim inputs() As String = {"aaaaa", "aaaaab"}
Dim nonbacktrackingPattern As String = "((?>a+))\w"
Dim match As Match
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim greedyPattern As String = ".+(\d+)\."
Dim input As String = "This sentence ends with the number 107325."
Dim match As Match
Para obtener más información sobre la búsqueda de coincidencias de derecha a izquierda, consulte
Opciones de expresiones regulares.
Búsqueda tardía positiva y negativa: (?<= subexpresión ) para la búsqueda tardía positiva y (?<!
subexpresión ) para la búsqueda tardía negativa. Esta característica es parecida a la búsqueda anticipada,
que se describe anteriormente en este tema. Dado que el motor de expresiones regulares permite una
búsqueda de coincidencias completa de derecha a izquierda, las expresiones regulares permiten búsquedas
tardías sin restricciones. La búsqueda tardía positiva y negativa también se puede usar para evitar anidar
los cuantificadores cuando la subexpresión anidada es un superconjunto de una expresión exterior. Las
expresiones regulares con cuantificadores anidados suelen ofrecer un rendimiento bajo. Por ejemplo, en el
ejemplo siguiente se comprueba que una cadena empieza y acaba con un carácter alfanumérico y que
cualquier otro carácter de la cadena es de un subconjunto más grande. Forma parte de la expresión regular
usada para validar direcciones de correo electrónico. Para obtener más información, vea Procedimiento:
Comprobación de que las cadenas están en un formato de correo electrónico válido.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim inputs() As String = {"jack.sprat", "dog#", "dog#1", "me.myself",
"me.myself!"}
Dim pattern As String = "^[A-Z0-9]([-!#$%&'.*+/=?^`{}|~\w])*(?<=[A-Z0-9])$"
For Each input As String In inputs
If Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase) Then
Console.WriteLine("{0}: Valid", input)
Else
Console.WriteLine("{0}: Invalid", input)
End If
Next
End Sub
End Module
' The example displays the following output:
' jack.sprat: Valid
' dog#: Invalid
' dog#1: Valid
' me.myself: Valid
' me.myself!: Invalid
Para obtener más información sobre la búsqueda tardía positiva y negativa, consulte Construcciones de
agrupamiento.
Artículos relacionados
T IT L E DESC RIP C IÓ N
Expresiones regulares de .NET Framework Proporciona información general sobre el aspecto del lenguaje
de programación de expresiones regulares.
Modelo de objetos de expresión regular Proporciona información y ejemplos de código que muestran
cómo usar las clases de expresiones regulares.
Lenguaje de expresiones regulares: referencia rápida Ofrece información sobre el conjunto de caracteres,
operadores y construcciones que se pueden usar para definir
expresiones regulares.
Referencia
System.Text.RegularExpressions
Retroceso en expresiones regulares
16/09/2020 • 37 minutes to read • Edit Online
NOTE
En general, un motor NFA (autómata finito no determinista), como el motor de expresiones regulares de .NET, se encarga
de crear expresiones regulares eficaces y rápidas para el desarrollador.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim input As String = "needing a reed"
Dim pattern As String = "e{2}\w\b"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("{0} found at position {1}", _
match.Value, match.Index)
Next
End Sub
End Module
' The example displays the following output:
' eed found at position 11
Aunque esta expresión regular incluye el cuantificador {2} , se evalúa de manera lineal. El motor de expresiones
regulares no retrocede porque {2} no es un cuantificador opcional, ya que especifica un número exacto y no
un número variable de veces que la subexpresión anterior debe coincidir. Como resultado, el motor de
expresiones regulares intenta hacer coincidir el patrón de expresión regular con la cadena de entrada como se
muestra en la tabla siguiente.
using System;
using System.Text.RegularExpressions;
Module Example
Public Sub Main()
Dim input As String = "Essential services are provided by regular expressions."
Dim pattern As String = ".*(es)"
Dim m As Match = Regex.Match(input, pattern, RegexOptions.IgnoreCase)
If m.Success Then
Console.WriteLine("'{0}' found at position {1}", _
m.Value, m.Index)
Console.WriteLine("'es' found at position {0}", _
m.Groups(1).Index)
End If
End Sub
End Module
' 'Essential services are provided by regular expres' found at position 0
' 'es' found at position 47
Imports System.Diagnostics
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "^(a+)+$"
Dim inputs() As String = {"aaaaaa", "aaaaa!"}
Dim rgx As New Regex(pattern)
Dim sw As Stopwatch
Como muestra el resultado del ejemplo, el motor de expresiones regulares tardó en descubrir que una cadena
de entrada no coincidía con el patrón aproximadamente el doble que en identificar una cadena coincidente. Esto
se debe a que una coincidencia infructuosa siempre representa un escenario de caso peor. El motor de
expresiones regulares debe usar la expresión regular para seguir todas las rutas posibles a través de los datos
antes de poder concluir que la coincidencia no es correcta y los paréntesis anidados crean muchas rutas de
acceso adicionales a través de los datos. El motor de expresiones regulares concluye que la segunda cadena no
coincide con el patrón; para ello, hace lo siguiente:
Comprueba que está al principio de la cadena y, a continuación, busca una coincidencia de los cinco
primeros caracteres de la cadena con el patrón a+ . A continuación, determina que no hay ningún grupo
adicional de caracteres "a" en la cadena. Por último, comprueba que está al final de la cadena. Como en la
cadena queda un carácter adicional, la coincidencia produce un error. Esta coincidencia errónea necesita 9
comparaciones. El motor de expresiones regulares también guarda información de estado de las
coincidencias de "a" (que llamaremos coincidencia 1), "aa" (coincidencia 2), "aaa" (coincidencia 3) y "aaaa"
(coincidencia 4).
Vuelve a la coincidencia 4 guardada previamente. Determina que hay un carácter "a" adicional para
asignar a un grupo capturado adicional. Por último, comprueba que está al final de la cadena. Como en la
cadena queda un carácter adicional, la coincidencia produce un error. Esta coincidencia errónea necesita 4
comparaciones. Hasta ahora, se han realizado un total de 13 comparaciones.
Vuelve a la coincidencia 3 guardada previamente. Determina que hay dos caracteres "a" adicionales para
asignar a un grupo capturado adicional. Sin embargo, se produce un error en la prueba de fin de cadena.
Vuelva a la coincidencia 3 e intenta hacer coincidir los dos caracteres "a" adicionales en dos grupos
capturados adicionales. Se sigue produciendo un error en la prueba de fin de cadena. Estas coincidencias
con error necesitan 12 comparaciones. Hasta ahora, se ha realizado un total de 25 comparaciones.
La comparación de la cadena de entrada con la expresión regular continúa de esta manera hasta que el motor
de expresiones regulares ha intentado todas las posibles combinaciones de coincidencias y, a continuación,
concluye que no hay ninguna coincidencia. Debido a los cuantificadores anidados, esta comparación es O(2n ) o
una operación exponencial, donde n es el número de caracteres de la cadena de entrada. Esto significa que, en el
peor de los casos, una cadena de entrada de 30 caracteres necesita aproximadamente 1.073.741.824
comparaciones y una cadena de entrada de 40 caracteres necesita aproximadamente 1.099.511.627.776
comparaciones. Si usa cadenas de estas longitudes o incluso mayores, los métodos de expresión regular pueden
tardar mucho tiempo en completarse cuando procesan datos de entrada que no coinciden con el patrón de
expresión regular.
Controlar el retroceso
El retroceso permite crear expresiones regulares eficaces y flexibles. Sin embargo, como se ha mostrado en la
sección anterior, estas ventajas pueden conllevar un bajo rendimiento inaceptable. Para evitar el retroceso
excesivo, se debe definir un intervalo de tiempo de espera cuando se instancie un objeto Regex o se llame a un
método estático de coincidencia de expresión regular. Esta técnica se analiza en la sección siguiente. Además,
.NET admite tres elementos del lenguaje de expresiones regulares que limitan o suprimen la vuelta atrás
(backtracking) y que admiten expresiones regulares complejas con poca o ninguna reducción del rendimiento:
grupos atómicos, aserciones de búsqueda retrasada (lookbehind) y aserciones de búsqueda anticipada
(lookahead). Para obtener más información sobre cada elemento del lenguaje, vea Construcciones de
agrupamiento.
Definición de un intervalo de tiempo de espera
A partir de .NET Framework 4.5, se puede establecer un valor de tiempo de espera que representa el intervalo
más largo en el que el motor de expresión regular buscará una coincidencia única antes de abandonar el intento
y generar una excepción RegexMatchTimeoutException. El intervalo de tiempo de espera se especifica al
proporcionar un valor TimeSpan al constructor Regex(String, RegexOptions, TimeSpan) para las expresiones
regulares de instancias. Además, cada método estático de coincidencia de patrones tiene una sobrecarga con un
parámetro TimeSpan que permite especificar un valor de tiempo de espera. De forma predeterminada, el
intervalo de tiempo de espera se establece en Regex.InfiniteMatchTimeout y el motor de expresiones regulares
no agota dicho tiempo.
IMPORTANT
Recomendamos que se establezca siempre un intervalo de tiempo de espera si la expresión regular se basa en el
retroceso.
Una excepción RegexMatchTimeoutException indica que el motor de expresiones regulares no pudo encontrar
una coincidencia en el intervalo de tiempo de espera especificado, pero no indica por qué se produjo la
excepción. La razón puede ser un retroceso excesivo, aunque también es posible que el intervalo de tiempo de
espera establecido fuera demasiado bajo, dada la carga del sistema en el momento en que se produjo la
excepción. Cuando se controla la excepción, se puede elegir entre abandonar otras coincidencias con la cadena
de entrada o incrementar el intervalo de tiempo de espera y reintentar la operación de coincidencia.
Por ejemplo, el código siguiente llama al constructor Regex(String, RegexOptions, TimeSpan) para crear
instancias de un objeto Regex con un valor de tiempo de espera de un segundo. El patrón de expresión regular
(a+)+$ , que coincide con una o más secuencias de uno o varios caracteres "a" al final de una línea, está sujeto a
un retroceso excesivo. Si se produce una excepción RegexMatchTimeoutException , el ejemplo incrementa el
valor de tiempo de espera hasta un intervalo máximo de tres segundos. Después, abandona el intento de
coincidir con el patrón.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Security;
using System.Text.RegularExpressions;
using System.Threading;
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Security
Imports System.Text.RegularExpressions
Imports System.Threading
Module Example
Const MaxTimeoutInSeconds As Integer = 3
Grupos atómicos
El elemento de lenguaje (?> subexpresión ) suprime la vuelta atrás (backtracking) en una subexpresión. Una
vez que coincida correctamente, no abandonará ninguna parte de su coincidencia a la vuelta atrás
(backtracking) posterior. Por ejemplo, en el patrón (?>\w*\d*)1 , si no se puede hacer coincidir 1 , \d* no
abandonará ninguna coincidencia, aunque esto signifique que permita que 1 coincida correctamente. Los
grupos atómicos pueden ayudar a evitar los problemas de rendimiento asociados a las coincidencias con error.
En el ejemplo siguiente se muestra cómo la supresión del retroceso mejora el rendimiento cuando se usan
cuantificadores anidados. Mide el tiempo necesario para que el motor de expresiones regulares determine que
una cadena de entrada no coincide con dos expresiones regulares. La primera expresión regular usa el retroceso
para intentar buscar una coincidencia de una cadena que contiene una o más apariciones de uno o más dígitos
hexadecimales, seguidas de un signo de dos puntos, seguido de uno o más dígitos hexadecimales, seguido de
dos signos de dos puntos. La segunda expresión regular es idéntica a la primera, salvo que deshabilita el
retroceso. Como muestra el resultado del ejemplo, la mejora de rendimiento que supone deshabilitar el
retroceso es significativa.
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
Console.WriteLine("With backtracking:");
string backPattern = "^(([0-9a-fA-F]{1,4}:)*([0-9a-fA-F]{1,4}))*(::)$";
sw = Stopwatch.StartNew();
matched = Regex.IsMatch(input, backPattern);
sw.Stop();
Console.WriteLine("Match: {0} in {1}", Regex.IsMatch(input, backPattern), sw.Elapsed);
Console.WriteLine();
Console.WriteLine("Without backtracking:");
string noBackPattern = "^((?>[0-9a-fA-F]{1,4}:)*(?>[0-9a-fA-F]{1,4}))*(::)$";
sw = Stopwatch.StartNew();
matched = Regex.IsMatch(input, noBackPattern);
sw.Stop();
Console.WriteLine("Match: {0} in {1}", Regex.IsMatch(input, noBackPattern), sw.Elapsed);
}
}
// The example displays output like the following:
// With backtracking:
// Match: False in 00:00:27.4282019
//
// Without backtracking:
// Match: False in 00:00:00.0001391
Imports System.Diagnostics
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "b51:4:1DB:9EE1:5:27d60:f44:D4:cd:E:5:0A5:4a:D24:41Ad:"
Dim matched As Boolean
Dim sw As Stopwatch
Console.WriteLine("With backtracking:")
Dim backPattern As String = "^(([0-9a-fA-F]{1,4}:)*([0-9a-fA-F]{1,4}))*(::)$"
sw = Stopwatch.StartNew()
matched = Regex.IsMatch(input, backPattern)
sw.Stop()
Console.WriteLine("Match: {0} in {1}", Regex.IsMatch(input, backPattern), sw.Elapsed)
Console.WriteLine()
Console.WriteLine("Without backtracking:")
Dim noBackPattern As String = "^((?>[0-9a-fA-F]{1,4}:)*(?>[0-9a-fA-F]{1,4}))*(::)$"
sw = Stopwatch.StartNew()
matched = Regex.IsMatch(input, noBackPattern)
sw.Stop()
Console.WriteLine("Match: {0} in {1}", Regex.IsMatch(input, noBackPattern), sw.Elapsed)
End Sub
End Module
' The example displays the following output:
' With backtracking:
' Match: False in 00:00:27.4282019
'
' Without backtracking:
' Match: False in 00:00:00.0001391
Module Example
Public Sub Main()
Dim sw As Stopwatch
Dim input As String = "test@contoso.com"
Dim result As Boolean
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
Imports System.Diagnostics
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "aaaaaaaaaaaaaaaaaaaaaa."
Dim result As Boolean
Dim sw As Stopwatch
Vea también
Expresiones regulares de .NET
Lenguaje de expresiones regulares: referencia rápida
Cuantificadores
Construcciones de alternancia
Construcciones de agrupamiento
Compilar y volver a utilizar en expresiones regulares
16/09/2020 • 4 minutes to read • Edit Online
Puede optimizar el rendimiento de aplicaciones que usan en gran medida las expresiones regulares al comprender
cómo compila expresiones el motor de expresiones regulares y cómo se almacenan en caché las expresiones
regulares. En este tema, se describen la compilación y el almacenamiento en caché.
La clase Regex es en sí misma segura para subprocesos e inmutable (de solo lectura). Es decir, se pueden crear
objetos Regex en cualquier subproceso y compartirlos entre varios subprocesos; los métodos de coincidencia
pueden llamarse desde cualquier subproceso y no modifican nunca el estado global.
Pero los objetos de resultado (Match y MatchCollection) que devuelve Regex deben usarse en un único
subproceso. Aunque muchos de estos objetos son lógicamente inmutables, sus implementaciones pueden retrasar
el cálculo de algunos resultados para mejorar el rendimiento y, en consecuencia, los llamadores deben serializar el
acceso a ellos.
Si es necesario compartir objetos de resultado de Regex en varios subprocesos, estos objetos se pueden convertir
en instancias seguras para subprocesos llamando a sus métodos sincronizados. A excepción de los enumeradores,
todas las clases de expresiones regulares son seguras para subprocesos o pueden convertirse en objetos seguros
para subprocesos mediante un método sincronizado.
Los enumeradores son la única excepción. Las aplicaciones debe serializar las llamadas a enumeradores de
colecciones. La regla es que, si una colección puede enumerarse simultáneamente en más de un subproceso, se
deben sincronizar los métodos de enumerador en el objeto raíz de la colección que recorre el enumerador.
Vea también
Expresiones regulares de .NET
Ejemplo de expresiones regulares: Buscar etiquetas
HREF
16/09/2020 • 6 minutes to read • Edit Online
En el ejemplo siguiente se busca una cadena de entrada y se muestran todos los valores href="…" y sus ubicaciones
en la cadena.
WARNING
Cuando se usa System.Text.RegularExpressions para procesar entradas que no son de confianza, pase un tiempo de
expiración. Un usuario malintencionado puede proporcionar entradas a RegularExpressions y provocar un ataque por
denegación de servicio. Las API del marco ASP.NET Core en las que se usa RegularExpressions pasan un tiempo de
expiración.
El objeto Regex
Dado que el método DumpHRefs puede llamarse varias veces desde el código de usuario, usa el método static (
Shared en Visual Basic) Regex.Match(String, String, RegexOptions). Esto permite que el motor de expresiones
regulares almacene en caché la expresión regular y evita la sobrecarga que se produciría al crear instancias de un
nuevo objeto Regex cada vez que se llamara al método. Después, se usa un objeto Match para iterar todas las
coincidencias de la cadena.
try
{
m = Regex.Match(inputString, HRefPattern,
RegexOptions.IgnoreCase | RegexOptions.Compiled,
TimeSpan.FromSeconds(1));
while (m.Success)
{
Console.WriteLine("Found href " + m.Groups[1] + " at "
+ m.Groups[1].Index);
m = m.NextMatch();
}
}
catch (RegexMatchTimeoutException)
{
Console.WriteLine("The matching operation timed out.");
}
}
Private Sub DumpHRefs(inputString As String)
Dim m As Match
Dim HRefPattern As String = "href\s*=\s*(?:[""'](?<1>[^""']*)[""']|(?<1>\S+))"
Try
m = Regex.Match(inputString, HRefPattern, _
RegexOptions.IgnoreCase Or RegexOptions.Compiled,
TimeSpan.FromSeconds(1))
Do While m.Success
Console.WriteLine("Found href {0} at {1}.", _
m.Groups(1), m.Groups(1).Index)
m = m.NextMatch()
Loop
Catch e As RegexMatchTimeoutException
Console.WriteLine("The matching operation timed out.")
End Try
End Sub
Vea también
Expresiones regulares de .NET
Ejemplo de expresiones regulares: Cambiar formatos
de fecha
16/09/2020 • 3 minutes to read • Edit Online
En el siguiente ejemplo de código, se usa el método Regex.Replace para reemplazar las fechas con el formato
mm/dd/aa con fechas que tienen el formato dd-mm-aa.
WARNING
Cuando se usa System.Text.RegularExpressions para procesar entradas que no son de confianza, pase un tiempo de
expiración. Un usuario malintencionado puede proporcionar entradas a RegularExpressions y provocar un ataque por
denegación de servicio. Las API del marco ASP.NET Core en las que se usa RegularExpressions pasan un tiempo de
expiración.
Ejemplo
static string MDYToDMY(string input)
{
try {
return Regex.Replace(input,
@"\b(?<month>\d{1,2})/(?<day>\d{1,2})/(?<year>\d{2,4})\b",
"${day}-${month}-${year}", RegexOptions.None,
TimeSpan.FromMilliseconds(150));
}
catch (RegexMatchTimeoutException) {
return input;
}
}
El código siguiente muestra cómo se puede llamar al método MDYToDMY en una aplicación.
using System;
using System.Globalization;
using System.Text.RegularExpressions;
Imports System.Globalization
Imports System.Text.RegularExpressions
Module DateFormatReplacement
Public Sub Main()
Dim dateString As String = Date.Today.ToString("d", _
DateTimeFormatInfo.InvariantInfo)
Dim resultString As String = MDYToDMY(dateString)
Console.WriteLine("Converted {0} to {1}.", dateString, resultString)
End Sub
Comentarios
El patrón de la expresión regular \b(?<month>\d{1,2})/(?<day>\d{1,2})/(?<year>\d{2,4})\b se interpreta como se
muestra en la tabla siguiente.
M O DELO DESC RIP T IO N
- Agregar un guión.
- Agregar un guión.
Vea también
Expresiones regulares de .NET
Cómo: Extraer un protocolo y un número de puerto
de una dirección URL
16/09/2020 • 3 minutes to read • Edit Online
En los siguientes ejemplos se extrae un protocolo y un número de puerto de una dirección URL.
WARNING
Cuando se usa System.Text.RegularExpressions para procesar entradas que no son de confianza, pase un tiempo de
expiración. Un usuario malintencionado puede proporcionar entradas a RegularExpressions y provocar un ataque por
denegación de servicio. Las API del marco ASP.NET Core en las que se usa RegularExpressions pasan un tiempo de
expiración.
Ejemplo
El ejemplo usa el método Match.Result para devolver el protocolo seguido de dos puntos y del número de puerto.
using System;
using System.Text.RegularExpressions;
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim url As String = "http://www.contoso.com:8080/letters/readme.html"
Dim r As New Regex("^(?<proto>\w+)://[^/]+?(?<port>:\d+)?/",
RegexOptions.None, TimeSpan.FromMilliseconds(150))
(?<port>:\d+)? Buscar una coincidencia con cero o una aparición de una coma
seguida de uno o más caracteres decimales. Asigne a este
grupo el nombre port .
El método Match.Result expande la secuencia de reemplazo ${proto}${port} , que concatena el valor de los dos
grupos con nombre capturados en el patrón de expresión regular. Resulta cómodo concatenar explícitamente las
cadenas recuperadas del objeto de la colección devueltas por la propiedad Match.Groups.
El ejemplo usa el método Match.Result con dos sustituciones, ${proto} y ${port} , para incluir los grupos
capturados en la cadena de salida. En su lugar, puede recuperar los grupos capturados del objeto de coincidencia
GroupCollection, como se muestra en el siguiente código.
Console.WriteLine(m.Groups["proto"].Value + m.Groups["port"].Value);
Console.WriteLine(m.Groups("proto").Value + m.Groups("port").Value)
Vea también
Expresiones regulares de .NET
Procedimiento para quitar caracteres no válidos de
una cadena
16/09/2020 • 2 minutes to read • Edit Online
En el ejemplo siguiente se usa el método estático Regex.Replace para quitar caracteres no válidos de una cadena.
WARNING
Cuando se usa System.Text.RegularExpressions para procesar entradas que no son de confianza, pase un tiempo de
expiración. Un usuario malintencionado puede proporcionar entradas a RegularExpressions y provocar un ataque por
denegación de servicio. Las API del marco ASP.NET Core en las que se usa RegularExpressions pasan un tiempo de
expiración.
Ejemplo
Puede usar el método CleanInput definido en este ejemplo para quitar caracteres potencialmente perjudiciales
que se hayan escrito en un campo de texto que acepta datos del usuario. En este caso, CleanInput elimina todos
los caracteres no alfanuméricos excepto puntos (.), símbolos de arroba (@) y guiones (-), y devuelve la cadena
restante. Pero puede modificar el patrón de expresión regular para que elimine todos los caracteres que no deban
incluirse en una cadena de entrada.
using System;
using System.Text.RegularExpressions;
Module Example
Function CleanInput(strIn As String) As String
' Replace invalid characters with empty strings.
Try
Return Regex.Replace(strIn, "[^\w\.@-]", "")
' If we timeout when replacing invalid characters,
' we should return String.Empty.
Catch e As RegexMatchTimeoutException
Return String.Empty
End Try
End Function
End Module
El patrón de expresión regular [^\w\.@-] coincide con cualquier carácter que no sea un carácter de palabra, un
punto, un símbolo @ o un guion. Un carácter de palabra es cualquier letra, dígito decimal o conector de
puntuación, como un guion bajo. Cualquier carácter que coincida con este patrón se sustituye por String.Empty,
que es la cadena definida por el modelo de reemplazo. Para permitir caracteres adicionales en la entrada de
usuario, agregue esos caracteres a la clase de caracteres en el patrón de la expresión regular. Por ejemplo, el patrón
de expresión regular [^\w\.@-\\%] también permite un símbolo de porcentaje y una barra diagonal inversa en la
cadena de entrada.
Vea también
Expresiones regulares de .NET
Cómo comprobar si las cadenas tienen un formato
de correo electrónico válido
16/09/2020 • 11 minutes to read • Edit Online
En el ejemplo siguiente se usa una expresión regular para comprobar que una cadena tiene un formato de correo
electrónico válido.
WARNING
Cuando se usa System.Text.RegularExpressions para procesar entradas que no son de confianza, pase un tiempo de
expiración. Un usuario malintencionado puede proporcionar entradas a RegularExpressions y provocar un ataque por
denegación de servicio. Las API del marco ASP.NET Core en las que se usa RegularExpressions pasan un tiempo de
expiración.
Ejemplo
En el ejemplo se define un método IsValidEmail , que devuelve true si la cadena contiene una dirección de
correo electrónico válida y false si no es válida, pero no realiza ninguna otra acción.
Para comprobar que la dirección de correo electrónico es válida, el método IsValidEmail llama al método
Regex.Replace(String, String, MatchEvaluator) con el patrón de expresión regular (@)(.+)$ para separar el
nombre de dominio de la dirección de correo electrónico. El tercer parámetro es un delegado MatchEvaluator que
representa el método que procesa y reemplaza el texto coincidente. El patrón de expresión regular se interpreta
de esta manera:
El nombre de dominio junto con el carácter @ se pasa al método DomainMapper , que usa la clase IdnMapping
para convertir los caracteres Unicode fuera del intervalo de caracteres EE.UU. - ASCII a Punycode. El método
también establece la marca invalid en True si el método IdnMapping.GetAscii detecta cualquier carácter no
válido en el nombre del dominio. El método devuelve el nombre de dominio Punycode precedido por el símbolo
@ al método IsValidEmail .
A continuación, el método IsValidEmail llama al método Regex.IsMatch(String, String) para comprobar que la
dirección se ajusta a un patrón de expresión regular.
Tenga en cuenta que el método IsValidEmail no realiza la autenticación para validar la dirección de correo
electrónico. Se limita a determinar si su formato es válido para una dirección de correo electrónico. Asimismo, el
método IsValidEmail no comprueba que el nombre del dominio de nivel superior sea un nombre válido
enumerado en la base de datos de la zona de la raíz IANA, lo cual requeriría una operación de búsqueda. En su
lugar, la expresión regular comprueba simplemente que el nombre de dominio de nivel superior conste de entre
dos y veinticuatro caracteres ASCII alfanuméricos, que los caracteres primero y último sean alfanuméricos y que
el resto de caracteres sean alfanuméricos o un guión (-).
using System;
using System.Globalization;
using System.Text.RegularExpressions;
try
{
// Normalize the domain
email = Regex.Replace(email, @"(@)(.+)$", DomainMapper,
RegexOptions.None, TimeSpan.FromMilliseconds(200));
try
{
return Regex.IsMatch(email,
@"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-
z])@))" +
@"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}
[a-z0-9]))$",
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
}
catch (RegexMatchTimeoutException)
{
return false;
}
}
}
Imports System.Globalization
Imports System.Text.RegularExpressions
End Function
Catch e As RegexMatchTimeoutException
Return False
Catch e As ArgumentException
Return False
End Try
Try
Return Regex.IsMatch(email,
"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\
{\}\|~\w])*)(?<=[0-9a-z])@))" +
"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9]
[\-a-z0-9]{0,22}[a-z0-9]))$",
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250))
Catch e As RegexMatchTimeoutException
Return False
End Try
End Function
End Class
Compilar el código
Los métodos IsValidEmail y DomainMapper pueden estar incluidos en una biblioteca de métodos de la utilidad de
expresiones regulares o pueden incluirse como métodos estáticos o de instancia privados en la clase de
aplicación.
También puede usar el método Regex.CompileToAssembly para incluir esta expresión regular en una biblioteca de
expresiones regulares.
Si se utilizan en una biblioteca de expresiones regulares, puede llamarlos utilizando código como el siguiente:
class Program
{
static void Main(string[] args)
{
string[] emailAddresses = { "david.jones@proseware.com", "d.j@server1.proseware.com",
"jones@ms1.proseware.com", "j.@server1.proseware.com",
"j@proseware.com9", "js#internal@proseware.com",
"j_9@[129.126.118.1]", "j..s@proseware.com",
"js*@proseware.com", "js@proseware..com",
"js@proseware.com9", "j.s@server1.proseware.com",
"\"j\\\"s\\\"\"@proseware.com", "js@contoso.中国" };
Console.ReadKey();
}
}
// The example displays the following output:
// Valid: david.jones@proseware.com
// Valid: d.j@server1.proseware.com
// Valid: jones@ms1.proseware.com
// Invalid: j.@server1.proseware.com
// Valid: j@proseware.com9
// Valid: js#internal@proseware.com
// Valid: j_9@[129.126.118.1]
// Invalid: j..s@proseware.com
// Invalid: js*@proseware.com
// Invalid: js@proseware..com
// Valid: js@proseware.com9
// Valid: j.s@server1.proseware.com
// Valid: "j\"s\""@proseware.com
// Valid: js@contoso.中国
Public Class Application
Public Shared Sub Main()
Dim emailAddresses() As String = {"david.jones@proseware.com", "d.j@server1.proseware.com",
"jones@ms1.proseware.com", "j.@server1.proseware.com",
"j@proseware.com9", "js#internal@proseware.com",
"j_9@[129.126.118.1]", "j..s@proseware.com",
"js*@proseware.com", "js@proseware..com",
"js@proseware.com9", "j.s@server1.proseware.com",
"""j\""s\""""@proseware.com", "js@contoso.中国"}
Vea también
Expresiones regulares de .NET Framework
Analizar cadenas en .NET
16/09/2020 • 2 minutes to read • Edit Online
Una operación de análisis convierte una cadena que representa un tipo base de .NET en dicho tipo base. Por
ejemplo, se usa una operación de análisis para convertir una cadena en un número de punto flotante o un valor
de fecha y hora. El método que se usa normalmente para realizar una operación de análisis es el método Parse .
Dado que el análisis es la operación inversa del formato (lo que implica convertir un tipo base en su
representación de cadena), se aplican muchas de las mismas reglas y convenciones. Del mismo modo que el
formato usa un objeto que implementa la interfaz IFormatProvider para proporcionar información de formato
según la referencia cultural, el análisis también usa un objeto que implementa la interfaz IFormatProvider para
determinar cómo se interpreta una representación de cadena. Para obtener más información, consulte Aplicar
formato a tipos.
En esta sección
Análisis de cadenas numéricas
Se describe cómo convertir cadenas en tipos numéricos de .NET.
Análisis de cadenas de fecha y hora
Se describe cómo convertir cadenas en tipos DateTime de .NET.
Análisis de otras cadenas
Se describe cómo convertir cadenas en tipos carácter , booleano y enumeración .
Secciones relacionadas
Aplicación de formato a tipos
Se describen los conceptos de formato básicos, como especificadores de formato y proveedores de formato.
Conversión de tipos en .NET
Se describe cómo convertir tipos.
Analizar cadenas numéricas en .NET
16/09/2020 • 14 minutes to read • Edit Online
Todos los tipos numéricos tienen dos métodos de análisis estáticos, Parse y TryParse , que puede usar para
convertir la representación de cadena de un número en un tipo numérico. Estos métodos permiten analizar
cadenas generadas mediante el uso de las cadenas de formato que se documentan en Cadenas con formato
numérico estándar y Cadenas con formato numérico personalizado. De forma predeterminada, los métodos Parse
y TryParse pueden convertir correctamente las cadenas que contienen dígitos decimales enteros solo en valores
enteros. Pueden convertir correctamente las cadenas que contienen dígitos decimales enteros y fraccionarios,
separadores de grupos y un separador decimal en valores de punto flotante. El método Parse produce una
excepción si se produce un error en la operación, mientras que el método TryParse devuelve false .
Module Example
Public Sub Main()
Dim values() As String = {"1,304.16", "$1,456.78", "1,094", "152",
"123,45 €", "1 304,16", "Ae9f"}
Dim number As Double
Dim culture As CultureInfo = Nothing
using System;
using System.Globalization;
Imports System.Globalization
Module Example
Public Sub Main()
Dim value As String = "1,304"
Dim number As Integer
Dim provider As IFormatProvider = CultureInfo.CreateSpecificCulture("en-US")
If Int32.TryParse(value, number) Then
Console.WriteLine("{0} --> {1}", value, number)
Else
Console.WriteLine("Unable to convert '{0}'", value)
End If
WARNING
La operación de análisis siempre usa las convenciones de formato de una referencia cultural determinada. Si no pasa un
objeto CultureInfo o NumberFormatInfo para especificar una referencia cultural, se usa la referencia cultural asociada al
subproceso actual.
En la tabla siguiente se enumeran los miembros de la enumeración NumberStyles y se describe el efecto que
tienen en la operación de análisis.
VA LO R N UM B ERST Y L ES EF EC TO EN L A C A DEN A Q UE SE VA A A N A L IZ A R
Además, la enumeración NumberStyles proporciona los siguientes estilos compuestos, que incluyen varias marcas
NumberStyles.
Vea también
NumberStyles
Analizar cadenas
Aplicación de formato a tipos
Análisis de cadenas de fecha y hora en .NET
16/09/2020 • 13 minutes to read • Edit Online
El análisis de cadenas para convertirlas en objetos DateTime requiere que se especifique información sobre cómo
se representan las fechas y horas en forma de texto. Las distintas referencias culturales usan distintos órdenes de
día, mes y año. Algunas representaciones horarias usan el reloj de 24 horas y otras especifican "a. m." y "p. m.".
Algunas aplicaciones solo necesitan la fecha. Otras solo necesitan la hora. Pero otras necesitan especificar la fecha y
la hora. Los métodos que convierten cadenas a objeto DateTime permiten especificar información detallada sobre
los formatos esperados y los elementos de fecha y hora que precisa la aplicación. Hay tres subtareas para convertir
correctamente el texto en un objeto DateTime:
1. Debe especificarse el formato esperado del texto que representa una fecha y hora.
2. Es posible especificar la referencia cultural del formato de fecha y hora.
3. Puede especificarse cómo se establecen los componentes que faltan en la representación de texto en la fecha y
hora.
Los métodos Parse y TryParse convierten varias representaciones comunes de fecha y hora. Los métodos
ParseExact y TryParseExact convierten una representación de cadena que se ajusta al modelo especificado por una
cadena de formato de fecha y hora. (Para obtener más información, vea los artículos sobre cadenas con formato de
fecha y hora estándar y cadenas con formato de fecha y hora personalizado).
El objeto DateTimeFormatInfo actual proporciona más control sobre la forma en que debe interpretarse el texto
como fecha y hora. Las propiedades de un objeto DateTimeFormatInfo describen los separadores de fecha y hora,
los nombres de los meses, días y eras, así como el formato de las designaciones "a. m." y "p. m.". La referencia
cultural del subproceso actual proporciona un objeto DateTimeFormatInfo que representa la referencia cultural
actual. Si quiere una referencia cultural específica o una configuración personalizada, especifique el parámetro
IFormatProvider de un método de análisis. Para el parámetro IFormatProvider, especifique un objeto CultureInfo,
que representa una referencia cultural, o un objeto DateTimeFormatInfo.
Es posible que al texto que representa una fecha y hora le falte alguna información. Por ejemplo, la mayoría de los
usuarios supondría que la fecha "12 de marzo" representa el año actual. Del mismo modo, "Marzo de 2018"
representa el mes de marzo del año 2018. El texto que representa la hora a menudo solo incluye horas, minutos y
una designación "a. m."/"p. m.". Los métodos de análisis controlan esta información que falta mediante valores
predeterminados razonables:
Cuando solo se indica la hora, la parte de fecha utiliza la fecha actual.
Cuando solo se indica la fecha, la parte de hora es la medianoche.
Cuando no se especifica el año en una fecha, se usa el año actual.
Cuando no se especifica el día del mes, se usa el primero del mes.
Si la fecha se indica en la cadena, debe incluir el mes y uno de los dos valores, el día o el año. Si se indica la hora,
debe incluir la hora y los minutos o el designador "a. m."/"p. m.".
Se puede especificar la constante NoCurrentDateDefault para invalidar los valores predeterminados. Cuando se
usa esta constante, las propiedades de año, mes o día se establecen en el valor 1 . El último ejemplo con Parse
muestra este comportamiento.
Además de un componente de fecha y hora, la representación de cadena de una fecha y hora puede incluir una
diferencia horaria que indica cuánto difiere la hora respecto de la hora universal coordinada (UTC). Por ejemplo, la
cadena "14/2/2007 5:32:00 -7:00" define una hora que es siete horas anterior a la hora UTC. Si se omite la
diferencia horaria de la representación de cadena de una hora, el análisis devuelve un objeto DateTime con su
propiedad Kind establecida en DateTimeKind.Unspecified. Si se especifica la diferencia horaria, el análisis devuelve
un objeto DateTime con su propiedad Kind establecida en DateTimeKind.Local y su valor ajustado a la zona horaria
local del equipo. Puede modificar este comportamiento usando un valor DateTimeStyles con el método de análisis.
El proveedor de formato también se usa para interpretar una fecha numérica ambigua. No queda claro qué
componentes de la fecha representada por la cadena "02/03/04" son el mes, el día y el año. Los componentes se
interpretan según el orden de formatos de fecha similares del proveedor de formato.
Parse
En el ejemplo siguiente se muestra el uso del método DateTime.Parse para convertir un objeto string en un valor
DateTime. En este ejemplo se usa la referencia cultural asociada al subproceso actual. Si el objeto CultureInfo
asociado a la referencia cultural actual no puede analizar la cadena de entrada, se produce una excepción
FormatException.
TIP
Todos los ejemplos de C# en este artículo se ejecutan en el explorador. Presione el botón Ejecutar para ver el resultado.
También puede modificarlos para experimentar.
NOTE
Estos ejemplos están disponibles en el repositorio de documentos de GitHub para C# y Visual Basic.
Además, puede definir explícitamente la referencia cultural cuyas convenciones de formato se utilizan cuando se
analiza una cadena. Especifique uno de los objetos DateTimeFormatInfo estándar devueltos por la propiedad
CultureInfo.DateTimeFormat. En el ejemplo siguiente se usa un proveedor de formato para analizar una cadena en
alemán como valor DateTime. Crea un objeto CultureInfo que representa la referencia cultural de-DE . El objeto
CultureInfo garantiza el análisis correcto de esta cadena concreta. Esto impide cualquier opción que se encuentre
en CurrentCulture de CurrentThread.
Pero, aunque puede usar sobrecargas del método Parse para especificar proveedores de formato personalizados, el
método no admite el análisis de formatos no estándar. Para analizar una fecha y hora expresadas en un formato no
estándar, use en su lugar el método ParseExact.
En el ejemplo siguiente se usa la enumeración DateTimeStyles para especificar que no debe agregarse la
información de fecha y hora actual al valor DateTime en los campos no especificados.
ParseExact
Si se ajusta a uno de los patrones de cadena especificados, el método DateTime.ParseExact convierte una cadena en
un objeto DateTime. Cuando se pasa a este método una cadena que no tiene uno de las formas especificadas, se
genera una excepción FormatException. Puede definirse uno de los especificadores de formato de fecha y hora
estándar o una combinación de los especificadores de formato de fecha y hora personalizados. Con el uso de
especificadores de formato personalizados, es posible construir una cadena de reconocimiento personalizada. Para
obtener una explicación de los especificadores, consulte los temas sobre cadenas con formato de fecha y hora
estándar y cadenas con formato de fecha y hora personalizado.
En el ejemplo siguiente, se pasa al método DateTime.ParseExact un objeto de cadena para que lo analice, seguido
de un especificador de formato y luego de un objeto CultureInfo. Este método ParseExact solo puede analizar
cadenas que sigan el patrón de fecha larga en la referencia cultural en-US .
var cultureInfo = new CultureInfo("en-US");
string[] dateStrings = { " Friday, April 10, 2009", "Friday, April 10, 2009" };
foreach (string dateString in dateStrings)
{
try
{
var dateTime = DateTime.ParseExact(dateString, "D", cultureInfo);
Console.WriteLine(dateTime);
}
catch (FormatException)
{
Console.WriteLine("Unable to parse '{0}'", dateString);
}
}
// The example displays the following output:
// Unable to parse ' Friday, April 10, 2009'
// 4/10/2009 00:00:00
Cada sobrecarga de los métodos Parse y ParseExact tiene también un parámetro IFormatProvider que proporciona
información específica de la referencia cultural sobre el formato de la cadena. Este objeto IFormatProvider es un
objeto CultureInfo que representa una referencia cultural estándar o un objeto DateTimeFormatInfo devuelto por la
propiedad CultureInfo.DateTimeFormat. ParseExact usa también una cadena o un argumento de matriz de cadena
adicional que define uno o más formatos de fecha y hora personalizados.
Vea también
Análisis de cadenas
Aplicar formato a tipos
Conversión de tipos en .NET
Cadenas con formato de fecha y hora estándar
Cadenas con formato de fecha y hora personalizado
Analizar otras cadenas en .NET
16/09/2020 • 3 minutes to read • Edit Online
Además de cadenas numéricas y DateTime, puede analizar cadenas que representan los tipos Char, Boolean y
Enum en tipos de datos.
Char
El método de análisis estático asociado con el tipo de datos Char es útil para convertir una cadena que contiene un
único carácter en su valor Unicode. En el ejemplo de código siguiente se analiza una cadena en un carácter
Unicode.
Booleano
El tipo de datos Boolean contiene un método Parse que se puede usar para convertir una cadena que representa
un valor Boolean en un tipo Boolean real. Este método no distingue mayúsculas de minúsculas y puede analizar
correctamente una cadena que contenga "True" o "False". El método Parse asociado al tipo Boolean también
puede analizar cadenas que estén rodeadas por espacios en blanco. Si se pasa otra cadena, se produce una
excepción FormatException.
En el ejemplo de código siguiente se usa el método Parse para convertir una cadena en un valor Boolean.
Vea también
Analizar cadenas
Aplicación de formato a tipos
Conversión de tipos en .NET
Extender metadatos mediante atributos
16/09/2020 • 2 minutes to read • Edit Online
Common Language Runtime permite agregar declaraciones descriptivas a modo de palabras clave, conocidas
como atributos, para anotar elementos de programación como tipos, campos, métodos y propiedades. Cuando
compila el código para runtime, este se convierte al Lenguaje Intermedio de Microsoft (MSIL) y se coloca dentro
de un archivo portable ejecutable (PE) junto con los metadatos generados por el compilador. Los atributos
permiten colocar información descriptiva adicional en los metadatos que se puede extraer usando servicios de
reflexión en tiempo de ejecución. El compilador crea atributos cuando se declaran instancias de clases especiales
que derivan de System.Attribute.
.NET Framework usa atributos por distintos motivos y para tratar diversos problemas. Los atributos describen
cómo serializar los datos, especifican las características que se usan para aplicar la seguridad y limita las
optimizaciones del compilador Just-In-Time (JIT) para que el código siga siendo fácil de depurar. Los atributos
también pueden registrar el nombre de un archivo o el autor del código, o controlar la visibilidad de controles y
los miembros durante el desarrollo de formularios.
Temas relacionados
T IT L E DESC RIP C IÓ N
Recuperar la información almacenada en atributos Describe cómo recuperar los atributos personalizados del
código que se carga en el contexto de ejecución.
Cómo: Cargar ensamblados en el contexto de solo reflexión Explica cómo recuperar la información de los atributos
personalizados en el contexto de solo reflexión.
Referencia
System.Attribute
Aplicar atributos
16/09/2020 • 5 minutes to read • Edit Online
Para aplicar un atributo a un elemento del código se puede utilizar el proceso siguiente:
1. Definir un atributo nuevo o utilizar uno existente importando su espacio de nombres de .NET Framework.
2. Aplique el atributo al elemento de código colocándolo inmediatamente antes del elemento.
Cada lenguaje tiene su propia sintaxis de atributo. En C++ y C#, el atributo está incluido entre corchetes y
separado del elemento por un espacio en blanco, que puede incluir un salto de línea. En Visual Basic, el
atributo está incluido entre corchetes angulares y debe estar en la misma línea lógica; se puede utilizar el
carácter de continuación de línea si se desea un salto de línea.
3. Especifique parámetros posicionales y parámetros con nombre para el atributo.
Los parámetros posicionales son obligatorios y preceden a cualquier parámetro con nombre; corresponden
a los parámetros de uno de los constructores del atributo. Los parámetros con nombre son opcionales y
corresponden a las propiedades de lectura y escritura del atributo. En C++ y C#, especifique name = value
para cada parámetro opcional, donde name es el nombre de la propiedad. En Visual Basic, especifique name
:= value .
El atributo se emite en metadatos al compilar el código y queda disponible para Common Language Runtime y
cualquier aplicación o herramienta personalizada a través de los servicios de reflexión en tiempo de ejecución.
Por convención, todos los nombres de atributos terminan con la palabra Attribute. Sin embargo, algunos lenguajes
orientados a Common Language Runtime, como Visual Basic y C#, no requieren que se especifique el nombre
completo de los atributos. Por ejemplo, si desea inicializar System.ObsoleteAttribute, solo es necesario hacer
referencia al mismo como Obsolete .
int main()
{
Test::Main();
}
class Test
{
public static void Main()
{
// This generates a compile-time warning.
int i = Example.Add(2, 2);
}
}
Class Test
Public Shared Sub Main()
' This generates a compile-time warning.
Dim i As Integer = Example.Add(2, 2)
End Sub
End Class
Aplicar atributos a ensamblados
Si desea aplicar un atributo a un ensamblado, utilice la palabra clave assembly ( Assembly en Visual Basic). El
código siguiente muestra el atributoAssemblyTitleAttribute aplicado al ensamblado.
using System.Reflection;
[assembly:AssemblyTitle("My Assembly")]
Imports System.Reflection
<Assembly: AssemblyTitle("My Assembly")>
Cuando se aplica este atributo, la cadena "My Assembly" se coloca en el manifiesto del ensamblado, en la parte de
metadatos del archivo. Se puede ver el atributo utilizando el Desensamblador de MSIL (Ildasm.exe) o creando un
programa personalizado que recupere el atributo.
Vea también
Atributos
Retrieving Information Stored in Attributes (Recuperar la información almacenada en atributos)
Conceptos
Atributos (C#)
Información general sobre los atributos (Visual Basic)
Escribir atributos personalizados
16/09/2020 • 15 minutes to read • Edit Online
Para diseñar sus propios atributos personalizados, no necesitará dominar muchos conceptos nuevos. Si está
familiarizado con la programación orientada a objetos y sabe cómo diseñar clases, ya tiene la mayoría de los
conocimientos necesarios. Los atributos personalizados son esencialmente clases tradicionales que se derivan
directa o indirectamente de System.Attribute. Como sucede con las clases tradicionales, los atributos
personalizados contienen métodos que almacenan y recuperan datos.
Los pasos principales para diseñar correctamente clases de atributos personalizados son los siguientes:
Aplicar AttributeUsageAttribute
Declarar la clase de atributo
Declarar constructores
Declarar propiedades
En esta sección se describe cada uno de estos pasos y, para finalizar, se muestra un ejemplo de atributo
personalizado.
Aplicar AttributeUsageAttribute
La declaración de un atributo personalizado empieza con System.AttributeUsageAttribute, que define algunas de
las características clave de la clase de atributo. Por ejemplo, puede especificar si el atributo lo pueden heredar otras
clases o especificar los elementos a los que se puede aplicar el atributo. En el fragmento de código siguiente se
muestra cómo usar AttributeUsageAttribute.
AttributeUsageAttribute tiene tres miembros que son importantes para la creación de atributos personalizados:
AttributeTargets, Inherited y AllowMultiple.
Miembro AttributeTargets
En el ejemplo anterior, se especifica AttributeTargets.All, lo que indica que este atributo se puede aplicar a todos los
elementos de programa. Como alternativa, puede especificar AttributeTargets.Class, que indica que el atributo solo
se puede aplicar a una clase, o AttributeTargets.Method, que indica que el atributo solo se puede aplicar a un
método. De esta manera, un atributo personalizado puede marcar para descripción todos los elementos del
programa.
También puede pasar varios valores AttributeTargets. El fragmento de código siguiente especifica que un atributo
personalizado se puede aplicar a cualquier clase o método.
[AttributeUsage(AttributeTargets::Class | AttributeTargets::Method)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Method)>
Public Class SomeOtherClass
Inherits Attribute
'...
End Class
Propiedad Inherited
La propiedad AttributeUsageAttribute.Inherited indica si el atributo lo pueden heredar clases derivadas de las
clases a las que se aplica el atributo. Esta propiedad acepta una marca true (valor predeterminado) o false . En el
ejemplo siguiente, MyAttribute tiene un valor Inherited predeterminado de true , mientras que YourAttribute
tiene un valor Inherited de false .
<AttributeUsage(AttributeTargets.Method, Inherited:=False)>
Public Class YourAttribute
Inherits Attribute
'...
End Class
Por último, la clase YourClass se hereda de la clase base MyClass . El método MyMethod muestra MyAttribute ,
pero no YourAttribute .
};
End Class
Propiedad AllowMultiple
La propiedad AttributeUsageAttribute.AllowMultiple indica si pueden existir varias instancias del atributo en un
elemento. Si establece en true , se permiten varias instancias; si se establece en false (el valor predeterminado),
solo se permite una instancia.
En el ejemplo siguiente, MyAttribute tiene un valor AllowMultiple predeterminado de false , mientras que
YourAttribute tiene un valor de true .
<AttributeUsage(AttributeTargets.Method, AllowMultiple:=true)>
Public Class YourAttribute
Inherits Attribute
End Class
Si se aplican varias instancias de estos atributos, MyAttribute produce un error del compilador. En el ejemplo de
código siguiente se muestra el uso válido de YourAttribute y el uso no válido de MyAttribute .
public ref class MyClass
{
public:
// This produces an error.
// Duplicates are not allowed.
[MyAttribute]
[MyAttribute]
void MyMethod()
{
//...
}
// This is valid.
[YourAttribute]
[YourAttribute]
void YourMethod()
{
//...
}
};
// This is valid.
[YourAttribute]
[YourAttribute]
public void YourMethod()
{
//...
}
}
Si tanto la propiedad AllowMultiple como la propiedad Inherited se establecen en true , una clase que se hereda
de otra clase puede heredar un atributo y tiene otra instancia del mismo atributo aplicada en la misma clase
secundaria. Si se establece AllowMultiple en false , las instancias nuevas del mismo atributo de la clase secundaria
sobrescribirán los valores de los atributos de la clase primaria.
Declarar la clase de atributo
Después de aplicar AttributeUsageAttribute, puede empezar a definir los detalles del atributo. La declaración de
una clase de atributo es similar a la declaración de una clase tradicional, como se muestra en el código siguiente.
[AttributeUsage(AttributeTargets::Method)]
public ref class MyAttribute : Attribute
{
// . . .
};
[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute : Attribute
{
// . . .
}
<AttributeUsage(AttributeTargets.Method)>
Public Class MyAttribute
Inherits Attribute
' . . .
End Class
Declarar constructores
Los atributos se inicializan con constructores del mismo modo que las clases tradicionales. En el siguiente
fragmento de código se muestra un constructor de atributos típico. Este constructor público acepta un parámetro y
establece una variable de miembro igual a su valor.
MyAttribute(bool myvalue)
{
this->myvalue = myvalue;
}
Puede sobrecargar el constructor para adaptarse a distintas combinaciones de valores. Si también define una
propiedad para la clase de atributo personalizado, puede utilizar una combinación de parámetros con nombre y de
posición al inicializar el atributo. Normalmente, se definen todos los parámetros necesarios como de posición y
todos parámetros opcionales como con nombre. En este caso, el atributo no se puede inicializar sin el parámetro
obligatorio. Todos los demás parámetros son opcionales. Tenga en cuenta que en Visual Basic, los constructores de
una clase de atributo no deben utilizar un argumento ParamArray.
En el ejemplo de código siguiente, se muestra cómo se puede aplicar un atributo que utiliza el constructor anterior
mediante parámetros obligatorios y opcionales. Se supone que el atributo tiene un valor booleano obligatorio y
una propiedad de cadena opcional.
// One required (positional) and one optional (named) parameter are applied.
[MyAttribute(false, OptionalParameter = "optional data")]
public ref class SomeClass
{
//...
};
// One required (positional) parameter is applied.
[MyAttribute(false)]
public ref class SomeOtherClass
{
//...
};
// One required (positional) and one optional (named) parameter are applied.
[MyAttribute(false, OptionalParameter = "optional data")]
public class SomeClass
{
//...
}
// One required (positional) parameter is applied.
[MyAttribute(false)]
public class SomeOtherClass
{
//...
}
' One required (positional) and one optional (named) parameter are applied.
<MyAttribute(false, OptionalParameter:="optional data")>
Public Class SomeClass
'...
End Class
Declarar propiedades
Si quiere definir un parámetro con nombre o proporcionar un método sencillo para devolver los valores
almacenados por el atributo, declare una propiedad. Las propiedades de atributo deben declararse como entidades
públicas con una descripción del tipo de datos que se devolverá. Defina la variable que contendrá el valor de la
propiedad y asóciela con los métodos get y set . En el ejemplo de código siguiente se muestra cómo implementar
una propiedad simple en el atributo.
public:
// This constructor defines two required parameters: name and level.
' This constructor defines two required parameters: name and level.
Puede aplicar este atributo con el nombre completo, DeveloperAttribute , o el nombre abreviado, Developer , de
una de las maneras siguientes.
-or-
-or-
-or-
En el primer ejemplo se muestra el atributo aplicado solo con los parámetros con nombre obligatorios, mientras
que en el segundo ejemplo se muestra el atributo aplicado con los parámetros obligatorios y opcionales.
Vea también
System.Attribute
System.AttributeUsageAttribute
Atributos
Recuperar información almacenada en atributos
16/09/2020 • 13 minutes to read • Edit Online
La recuperación de un atributo personalizado es un proceso sencillo. En primer lugar, declare una instancia del
atributo que desea recuperar. A continuación, utilice el método Attribute.GetCustomAttribute para inicializar el
atributo nuevo en el valor del atributo que desea recuperar. Una vez inicializado el nuevo atributo, basta con usar
sus propiedades para obtener los valores.
IMPORTANT
En este tema se describe cómo recuperar los atributos personalizados del código que se carga en el contexto de ejecución.
Para recuperar los atributos del código que se carga en el contexto de solo reflexión, se debe usar la clase
CustomAttributeData, como se muestra en Cómo: Cargar ensamblados en el contexto de solo reflexión.
if (MyAttribute == nullptr)
{
Console::WriteLine("The attribute was not found.");
}
else
{
// Get the Name value.
Console::WriteLine("The Name Attribute is: {0}." , MyAttribute->Name);
// Get the Level value.
Console::WriteLine("The Level Attribute is: {0}." , MyAttribute->Level);
// Get the Reviewed value.
Console::WriteLine("The Reviewed Attribute is: {0}." , MyAttribute->Reviewed);
}
}
};
using System;
using System.Reflection;
using CustomCodeAttributes;
if (MyAttribute == null)
{
Console.WriteLine("The attribute was not found.");
}
else
{
// Get the Name value.
Console.WriteLine("The Name Attribute is: {0}." , MyAttribute.Name);
// Get the Level value.
Console.WriteLine("The Level Attribute is: {0}." , MyAttribute.Level);
// Get the Reviewed value.
Console.WriteLine("The Reviewed Attribute is: {0}." , MyAttribute.Reviewed);
}
}
}
Imports System.Reflection
Imports CustomCodeAttributes
Si no se encuentra el atributo, el método GetCustomAttribute inicializa MyAttribute con un valor null. En este
ejemplo se comprueba si en MyAttribute se encuentra dicha instancia y notifica al usuario si no se encuentra
ningún atributo. Si DeveloperAttribute no se encuentra en el ámbito de clase, aparece el mensaje siguiente en la
consola.
En este ejemplo se da por supuesto que la definición de atributo está en el espacio de nombres actual. No olvide
importar el espacio de nombres en el que reside la definición de atributo si no está en el espacio de nombres
actual.
public:
static void GetAttribute(Type^ t)
{
array<DeveloperAttribute^>^ MyAttributes =
(array<DeveloperAttribute^>^) Attribute::GetCustomAttributes(t, DeveloperAttribute::typeid);
if (MyAttributes->Length == 0)
{
Console::WriteLine("The attribute was not found.");
}
else
{
for (int i = 0 ; i < MyAttributes->Length; i++)
{
// Get the Name value.
Console::WriteLine("The Name Attribute is: {0}." , MyAttributes[i]->Name);
// Get the Level value.
Console::WriteLine("The Level Attribute is: {0}." , MyAttributes[i]->Level);
// Get the Reviewed value.
Console::WriteLine("The Reviewed Attribute is: {0}.", MyAttributes[i]->Reviewed);
}
}
}
public static void GetAttribute(Type t)
{
DeveloperAttribute[] MyAttributes =
(DeveloperAttribute[]) Attribute.GetCustomAttributes(t, typeof (DeveloperAttribute));
if (MyAttributes.Length == 0)
{
Console.WriteLine("The attribute was not found.");
}
else
{
for (int i = 0 ; i < MyAttributes.Length ; i++)
{
// Get the Name value.
Console.WriteLine("The Name Attribute is: {0}." , MyAttributes[i].Name);
// Get the Level value.
Console.WriteLine("The Level Attribute is: {0}." , MyAttributes[i].Level);
// Get the Reviewed value.
Console.WriteLine("The Reviewed Attribute is: {0}.", MyAttributes[i].Reviewed);
}
}
}
If MyAttributes.Length = 0 Then
Console.WriteLine("The attribute was not found.")
Else
For i As Integer = 0 To MyAttributes.Length - 1
' Get the Name value.
Console.WriteLine("The Name Attribute is: {0}.", MyAttributes(i).Name)
' Get the Level value.
Console.WriteLine("The Level Attribute is: {0}.", MyAttributes(i).Level)
' Get the Reviewed value.
Console.WriteLine("The Reviewed Attribute is: {0}.", MyAttributes(i).Reviewed)
Next i
End If
End Sub
Si no se encuentra ningún atributo, este código avisa al usuario. En caso contrario, se muestra la información
contenida en ambas instancias de DeveloperAttribute .
// Put the instance of the attribute on the class level in the att object.
att = (DeveloperAttribute^) Attribute::GetCustomAttribute (t, DeveloperAttribute::typeid);
if (att == nullptr)
{
Console::WriteLine("No attribute in class {0}.\n", t->ToString());
}
else
{
Console::WriteLine("The Name Attribute on the class level is: {0}.", att->Name);
Console::WriteLine("The Level Attribute on the class level is: {0}.", att->Level);
Console::WriteLine("The Reviewed Attribute on the class level is: {0}.\n", att->Reviewed);
}
// Put the instance of the attribute on the class level in the att object.
att = (DeveloperAttribute) Attribute.GetCustomAttribute (t, typeof (DeveloperAttribute));
if (att == null)
{
Console.WriteLine("No attribute in class {0}.\n", t.ToString());
}
else
{
Console.WriteLine("The Name Attribute on the class level is: {0}.", att.Name);
Console.WriteLine("The Level Attribute on the class level is: {0}.", att.Level);
Console.WriteLine("The Reviewed Attribute on the class level is: {0}.\n", att.Reviewed);
}
' Put the instance of the attribute on the class level in the att object.
att = CType(Attribute.GetCustomAttribute(t, GetType(DeveloperAttribute)), DeveloperAttribute)
If att Is Nothing
Console.WriteLine("No attribute in class {0}.\n", t.ToString())
Else
Console.WriteLine("The Name Attribute on the class level is: {0}.", att.Name)
Console.WriteLine("The Level Attribute on the class level is: {0}.", att.Level)
Console.WriteLine("The Reviewed Attribute on the class level is: {0}.\n", att.Reviewed)
End If
' Loop through all methods in this class that are in the
' MyMemberInfo array.
For i As Integer = 0 To MyMemberInfo.Length - 1
att = CType(Attribute.GetCustomAttribute(MyMemberInfo(i), _
GetType(DeveloperAttribute)), DeveloperAttribute)
If att Is Nothing Then
Console.WriteLine("No attribute in member function {0}.\n", MyMemberInfo(i).ToString())
Else
Console.WriteLine("The Name Attribute for the {0} member is: {1}.",
MyMemberInfo(i).ToString(), att.Name)
Console.WriteLine("The Level Attribute for the {0} member is: {1}.",
MyMemberInfo(i).ToString(), att.Level)
Console.WriteLine("The Reviewed Attribute for the {0} member is: {1}.\n",
MyMemberInfo(i).ToString(), att.Reviewed)
End If
Next
End Sub
Vea también
System.Type
Attribute.GetCustomAttribute
Attribute.GetCustomAttributes
Atributos
Elección entre tipos de tupla y anónimos
16/09/2020 • 7 minutes to read • Edit Online
A la hora de elegir el tipo adecuado, hay que tener en cuenta su usabilidad, su rendimiento y sus compensaciones
en comparación con otros tipos. Los tipos anónimos están disponibles desde la versión 3.0 de C#, mientras que los
tipos System.Tuple<T1,T2> genéricos se introdujeron con .NET Framework 4.0. Ya que se han incorporado nuevas
opciones con compatibilidad de lenguaje, como System.ValueTuple<T1,T2>, que, como indica su nombre,
proporciona un tipo de valor con la flexibilidad de los tipos anónimos. En este artículo aprenderá cuándo es
pertinente elegir un tipo en lugar de otro.
Usabilidad y funcionalidad
Los tipos anónimos se introdujeron en la versión 3.0 de C# con expresiones de Language Integrated Query (LINQ).
Con LINQ, los desarrolladores suelen proyectar los resultados de consultas a tipos anónimos que contienen
algunas propiedades concretas de los objetos con los que están trabajando. Fíjese en el ejemplo siguiente, en el que
se crea una instancia de una matriz de objetos DateTime y se itera a través de ellos proyectándose en un tipo
anónimo con dos propiedades.
Se crea una instancia de los tipos anónimos usando el operador new , y los nombres y tipos de propiedades se
infieren a partir de la declaración. Si dos o más inicializadores de objeto anónimo en el mismo ensamblado
especifican una secuencia de propiedades que están en el mismo orden y que tienen los mismos nombres y tipos,
el compilador trata el objeto como instancias del mismo tipo. Comparten la misma información de tipo generada
por el compilador.
El fragmento de C# anterior proyecta un tipo anónimo con dos propiedades, de forma similar a esta clase de C#
generada por el compilador:
Con System.Tuple<T1,T2>, la instancia expone propiedades de elementos numeradas, como Item1 y Item2 . Estos
nombres de propiedades pueden hacer que resulte más complicado entender la intención de los valores de
propiedad, ya que el nombre propiedad solo proporciona el ordinal. Además, los tipos System.Tuple son tipos
class de referencia. Sin embargo, System.ValueTuple<T1,T2> es un tipo struct de valor. En el fragmento de C#
siguiente se usa ValueTuple<string, long> para hacer la proyección. Al hacerlo, se asigna mediante una sintaxis
literal.
Para obtener más información sobre las tuplas, consulte Tipos de tupla (referencia de C#) o Tuplas (Visual Basic).
Todos los ejemplos anteriores son equivalentes funcionalmente; sin embargo, hay pequeñas diferencias en cuanto a
su usabilidad y sus implementaciones subyacentes.
Compromisos
Es recomendable que use siempre ValueTuple en lugar de Tuple, además de los tipos anónimos, pero hay
compensaciones que debería tener en cuenta. Los tipos de ValueTuple son mutables, mientras que los de Tuple son
de solo lectura. Los tipos anónimos se pueden usar en árboles de expresión; en cambio, las tuplas no. En la tabla
siguiente se muestra información general sobre algunas de las principales diferencias.
Principales diferencias
C O M PAT IB IL IDA D
N O M B RE DEL C ON LA C O M PAT IB IL IDA D
M O DIF IC A DO R M IEM B RO DEC O N ST RUC C IÓ C O N Á RB O L ES DE
N O M B RE DE A C C ESO T IP O P ERSO N A L IZ A DO N EXP RESIÓ N
Serialización
Un elemento importante que hay que tener en cuenta al elegir un tipo es si se tendrá que serializar o no. La
serialización es el proceso de convertir el estado de un objeto en un formato que se pueda almacenar o transportar.
Para obtener más información, vea el artículo sobre serialización. Cuando la serialización resulta importante, crear
un valor class o struct es preferible a los tipos anónimos o los de tupla.
Rendimiento
El rendimiento entre estos tipos depende de cada escenario. Un mayor impacto supone la compensación entre las
asignaciones y las copias. En la mayoría de los escenarios, el impacto es pequeño. Cuando pudieran surgir impactos
mayores, se tendrían que tomar medidas para tomar una decisión fundamentada.
Conclusión
Al elegir entre los tipos de tupla y los anónimos, como desarrollador es necesario tener en cuenta varios factores.
En general, si no trabaja con árboles de expresión y le parece bien usar la sintaxis de tupla, elija ValueTuple, ya que
proporcionan un tipo de valor con la flexibilidad de poner nombre a las propiedades. Si trabaja con árboles de
expresión y prefiere poner nombre a las propiedades, elija los tipos anónimos. En otros casos, use Tuple.
Vea también
Tipos anónimos (Guía de programación de C#).
Árboles de expresión
Tipos de tupla (referencia de C#)
Tuplas (Visual Basic)
Instrucciones de diseño de tipos
Información general de LINQ
16/09/2020 • 12 minutes to read • Edit Online
Language Integrated Query (LINQ) proporciona funcionalidades de consulta de nivel del lenguaje y una API de
función de orden superior para C# y Visual Basic, que le permiten escribir código expresivo y declarativo.
LINQ es expresivo
Imagine que tiene una lista de mascotas, pero desea convertirla en un diccionario en el que pueda tener acceso a
cada mascota directamente por su valor RFID .
Este es código imperativo tradicional:
La intención de este código no es crear un elemento Dictionary<int, Pet> y agregarle elementos por medio de un
bucle, sino convertir una lista existente en un diccionario. LINQ conserva la intención, a diferencia del código
imperativo.
Esta es la expresión LINQ equivalente:
El código con LINQ tiene la ventaja de poner al mismo nivel la intención y el código cuando se razona como
programador. Otra ventaja es la brevedad de código. Imagínese poder reducir gran parte de un código base en 1/3,
como hemos visto más arriba. No estaría mal, ¿verdad?
Escribir código a fin de recorrer manualmente el documento XML para realizar esta tarea sería bastante más
complicado.
Interactuar con XML no es lo único que puede hacer con los proveedores LINQ. LINQ to SQL es un asignador
relacional de objetos (ORM) bastante básico para una base de datos del servidor MSSQL. La biblioteca JSON.NET
proporciona una forma eficiente de recorrer documentos JSON mediante LINQ. Además, si no hay una biblioteca
que haga lo que necesita, también puede escribir un proveedor LINQ propio.
LINQ básico
Para obtener una lista realmente completa de ejemplos de LINQ, visite 101 ejemplos de LINQ.
Los ejemplos siguientes son una demostración rápida de algunas de las piezas básicas de LINQ. No pretende ser
exhaustivo, ya que LINQ ofrece más funcionalidad que la que se muestra aquí.
Las herramientas esenciales: Where , Select ,y Aggregate
// Filtering a list.
var germanShepherds = dogs.Where(dog => dog.Breed == DogBreed.GermanShepherd);
' Transforms the list of kennels into a list of all their dogs.
Dim allDogsFromKennels = kennels.SelectMany(Function(kennel) kennel.Dogs)
...
' Gets all the short-haired dogs between two different kennels.
Dim allShortHairedDogs = kennel1.Dogs.Union(kennel2.Dogs, New DogHairLengthComparer())
// Gets the volunteers who spend share time with two humane societies.
var volunteers = humaneSociety1.Volunteers.Intersect(humaneSociety2.Volunteers,
new VolunteerTimeComparer());
' Gets the volunteers who spend share time with two humane societies.
Dim volunteers = humaneSociety1.Volunteers.Intersect(humaneSociety2.Volunteers,
New VolunteerTimeComparer())
Ordenación
// Get driving directions, ordering by if it's toll-free before estimated driving time.
var results = DirectionsProcessor.GetDirections(start, end)
.OrderBy(direction => direction.HasNoTolls)
.ThenBy(direction => direction.EstimatedTime);
' Get driving directions, ordering by if it's toll-free before estimated driving time.
Dim results = DirectionsProcessor.GetDirections(start, end).
OrderBy(Function(direction) direction.HasNoTolls).
ThenBy(Function(direction) direction.EstimatedTime)
public static bool PublicInstancePropertiesEqual<T>(this T self, T to, params string[] ignore) where T : class
{
if (self == null || to == null)
{
return self == to;
}
// Selects the properties which have unequal values into a sequence of those properties.
var unequalProperties = from property in typeof(T).GetProperties(BindingFlags.Public |
BindingFlags.Instance)
where !ignore.Contains(property.Name)
let selfValue = property.GetValue(self, null)
let toValue = property.GetValue(to, null)
where !Equals(selfValue, toValue)
select property;
return !unequalProperties.Any();
}
<System.Runtime.CompilerServices.Extension()>
Public Function PublicInstancePropertiesEqual(Of T As Class)(self As T, [to] As T, ParamArray ignore As
String()) As Boolean
If self Is Nothing OrElse [to] Is Nothing Then
Return self Is [to]
End If
' Selects the properties which have unequal values into a sequence of those properties.
Dim unequalProperties = From [property] In GetType(T).GetProperties(BindingFlags.Public Or
BindingFlags.Instance)
Where Not ignore.Contains([property].Name)
Let selfValue = [property].GetValue(self, Nothing)
Let toValue = [property].GetValue([to], Nothing)
Where Not Equals(selfValue, toValue) Select [property]
Return Not unequalProperties.Any()
End Function
PLINQ
PLINQ, o Parallel LINQ, es un motor de ejecución en paralelo para expresiones de LINQ. En otras palabras, se puede
paralelizar una expresión normal de LINQ de forma trivial en cualquier número de subprocesos. Para hacerlo, se
emplea una llamada a AsParallel() delante de la expresión.
Tenga en cuenta lo siguiente.
return facebookUsers.AsParallel()
.Aggregate(seed, threadAccumulator, threadResultAccumulator, resultSelector);
}
Return facebookUsers.AsParallel().
Aggregate(seed, threadAccumulator, threadResultAccumulator, resultSelector)
}
Este código repartirá facebookUsers en subprocesos del sistema según sea necesario, sumará el total de "Me
gusta" de cada subproceso en paralelo, sumará los resultados calculados por cada subproceso y devolverá ese
resultado en una bonita cadena.
En forma de diagrama:
Las tareas paralelizables vinculadas a la CPU que se pueden expresar fácilmente con LINQ (es decir, que son
funciones puras y no tienen efectos secundarios) son un candidato excelente para PLINQ. Para tareas que sí tienen
efectos secundarios, considere el uso de la Biblioteca TLP.
Más recursos
Ejemplos de LINQ 101
Linqpad, un entorno de área de juegos y motor de consultas a bases de datos para C#/F#/Visual Basic
EduLinq, un libro electrónico para aprender cómo se implementa LINQ to Objects
Documentos y datos XML
16/09/2020 • 6 minutes to read • Edit Online
.NET Framework proporciona un conjunto de clases completo e integrado que permiten crear, de forma sencilla,
aplicaciones preparadas para XML. Las clases de los espacios de nombres siguientes admiten análisis y escritura
XML, edición de datos XML en memoria, validación de datos y transformación XSLT.
System.Xml
System.Xml.XPath
System.Xml.Xsl
System.Xml.Schema
System.Xml.Linq
Para obtener una lista completa, busque "System.Xml" en el explorador de API de .NET.
Las clases de estos espacios de nombres admiten las recomendaciones del World Wide Web Consortium (W3C).
Por ejemplo:
La clase System.Xml.XmlDocument implementa las recomendaciones de la parte principal del nivel 1 de
Document Object Model (DOM) y de la parte principal del nivel 2 de DOM del W3C.
Las clases System.Xml.XmlReader y System.Xml.XmlWriter admiten las recomendaciones del W3C sobre
XML 1.0 y los espacios de nombres de XML.
Los esquemas de la clase System.Xml.Schema.XmlSchemaSet admiten las recomendaciones del W3C sobre
Esquema XML, parte 1: estructuras y en Esquema XML, parte 2: tipos de datos.
Las clases del espacio de nombres System.Xml.Xsl admiten transformaciones XSLT compatibles con las
recomendaciones del W3C sobre XSLT versión 1.0.
Las clases XML de .NET Framework proporcionan estas ventajas:
Productividad. Gracias a LINQ to XML (C#) y LINQ to XML (Visual Basic) resulta más sencillo programar
con XML y proporciona una experiencia de consulta similar a SQL.
Extensibilidad. Las clases XML en .NET Framework se pueden extender mediante el uso de clases base
abstractas y métodos virtuales. Por ejemplo, puede crear una clase derivada de la clase XmlUrlResolver que
almacene el flujo caché en el disco local.
Arquitectura conectable. .NET Framework proporciona una arquitectura en la que los componentes se
pueden usar unos con otros y se puede hacer streaming de los datos entre componentes. Por ejemplo, un
almacén de datos, como un objeto XPathDocument o XmlDocument, se puede transformar con la clase
XslCompiledTransform y, posteriormente, se pueden hacer streaming de los resultados a otro almacén o
devolverse como flujo desde un servicio web.
Rendimiento. Para obtener un mejor rendimiento de la aplicación, algunas de las clases XML de .NET
Framework admiten un modelo basado en streaming con las características siguientes:
Almacenamiento en caché mínimo para el análisis de modelos de incorporación de cambios solo
hacia delante (XmlReader).
Validación solo hacia delante con (XmlReader).
Navegación al estilo de cursores que reduce la creación de nodos a un único nodo virtual, a la vez
que proporciona acceso aleatorio al documento (XPathNavigator).
Para obtener un mejor rendimiento cuando se requiera un procesamiento XSLT, puede usar la clase
XPathDocument, que es un almacén optimizado de solo lectura para consultas XPath diseñadas para
funcionar, de forma eficiente, con la clase XslCompiledTransform.
Integración con ADO.NET. Las clases XML y ADO.NET están estrechamente integradas para reunir datos
relacionales y XML. La clase DataSet es una caché almacenada en memoria de datos devueltos desde una
base de datos. La clase DataSet puede leer y escribir XML mediante las clases XmlReader y XmlWriter, con el
fin de almacenar su estructura de esquema relacional interna como esquemas XML (XSD) y para deducir la
estructura de esquema de un documento XML.
En esta sección
En Opciones de procesamiento XML se describen las opciones para procesar datos XML.
En Procesamiento de datos XML en memoria se describen los tres modelos para procesar datos XML en memoria:
LINQ to XML (C#) y LINQ to XML (Visual Basic), la clase XmlDocument (basada en Document Object Model del
W3C) y la clase XPathDocument (basada en el modelo de datos XPath).
Transformaciones XSLT
Describe cómo utilizar el procesador XSLT.
Modelo de objetos de esquema XML (SOM)
Describe las clases que se usan para crear y tratar esquemas XML (XSD) mediante una clase XmlSchema que carga
y modifica un esquema.
Integración de XML con datos relacionales y ADO.NET
Describe cómo habilita .NET Framework el acceso sincrónico en tiempo real a las representaciones relacional y
jerárquica de los datos mediante los objetos DataSet y XmlDataDocument.
Administración de espacios de nombres en un documento XML
Describe cómo se usa la clase XmlNamespaceManager para almacenar y mantener la información sobre espacios
de nombres.
Compatibilidad de tipos en las clases System.Xml
Describe cómo se asignan los tipos de datos XML a los tipos CLR, cómo se convierten los tipos de datos XML y
otras características de compatibilidad de tipos de las clases System.Xml.
Secciones relacionadas
ADO.NET
Proporciona información sobre cómo acceder a los datos mediante ADO.NET.
Seguridad
Ofrece información general sobre todo el sistema de seguridad de .NET Framework.
Introducción a Microsoft.Data.Sqlite
16/09/2020 • 2 minutes to read • Edit Online
Microsoft.Data.Sqlite es un proveedor ADO.NET ligero para SQLite. El proveedor Entity Framework Core para
SQLite se basa en esta biblioteca. Sin embargo, también se puede usar de forma independiente o con otras
bibliotecas de acceso a datos.
Instalación
La versión estable más reciente está disponible en NuGet.
CLI de .NET Core
Visual Studio
Uso
Esta biblioteca implementa las abstracciones de ADO.NET comunes para conexiones, comandos, lectores de datos,
etc.
Console.WriteLine($"Hello, {name}!");
}
}
}
Vea también
Cadenas de conexión
Referencia de API
Sintaxis de SQL
Procesamiento paralelo, simultaneidad y
programación asincrónica en .NET
16/09/2020 • 2 minutes to read • Edit Online
.NET proporciona varias maneras de escribir código asincrónico para que la aplicación responda mejor al usuario y
escribir código paralelo que usa varios subprocesos de ejecución para optimizar el rendimiento del equipo del
usuario.
En esta sección
Programación asincrónica
Describe los mecanismos para la programación asincrónica que proporciona .NET.
Programación en paralelo
Describe un modelo de programación basado en tareas que simplifica el desarrollo en paralelo, de modo que le
permite escribir código paralelo eficaz, específico y escalable de forma natural sin tener que trabajar directamente
con subprocesos ni el grupo de subprocesos.
Subprocesamiento
Describe los mecanismos de simultaneidad y sincronización básicos que proporciona .NET.
Información general de Async
18/03/2020 • 4 minutes to read • Edit Online
No hace tanto tiempo, las aplicaciones funcionaban más rápido simplemente al comprar un PC o servidor nuevo y
después se detuvo esa tendencia. De hecho, se ha invertido. Aparecieron teléfonos móviles con chips ARM de
núcleo único de 1 GHz y cargas de trabajo de servidor que pasaron a máquinas virtuales. Los usuarios seguían
queriendo una interfaz de usuario dinámica y los propietarios de empresas querían servidores que se ajustaran a
su negocio. La transición al móvil y a la nube y una población conectada a Internet de más de 3 mil millones de
usuarios ha generado un nuevo conjunto de patrones de software.
Se espera que las aplicaciones cliente estén siempre activadas y conectadas, y que respondan adecuadamente a
la interacción del usuario (p. ej., función táctil) con altas calificaciones en la tienda de aplicaciones.
Se espera que los servicios controlen los picos de tráfico al escalar y reducir verticalmente con facilidad.
La programación de Async es una técnica clave que facilita controlar las operaciones simultáneas y de E/S de
bloqueo en varios núcleos. .NET proporciona la funcionalidad de que servicios y aplicaciones sean dinámicos y
elásticos con modelos de programación asincrónicos de nivel de lenguaje y fáciles de usar en C#, Visual Basic y F#.
Pasos adicionales
Para obtener más información, vea el tema Async en profundidad.
En el tema Modelos para la programación asincrónica, se proporciona una introducción de los tres modelos de
programación asincrónica que se admiten en .NET:
Asynchronous Programming Model (APM) [Modelo de programación asincrónica (APM)] (heredado)
Modelo asincrónico basado en eventos (EAP) (heredado)
Task-based Asynchronous Pattern (TAP) [Modelo asincrónico basado en tareas (TAP)] (recomendado para
nuevos desarrollos)
Para obtener más información sobre el modelo de programación basado en tareas recomendado, consulte el tema
Programación asincrónica basada en tareas.
Async en profundidad
18/03/2020 • 19 minutes to read • Edit Online
La escritura de código asincrónico enlazado a E/S y CPU es sencilla al usar el modelo asincrónico basado en tareas
de .NET. El modelo se expone mediante los tipos Task y Task<T> y las palabras claves async y await en C# y
Visual Basic. (Los recursos específicos del idioma se encuentran en la sección Vea también). En este artículo, se
explica cómo usar Async de .NET y se proporciona información sobre el marco de trabajo de Async usado en
segundo plano.
Task y Task<T>
Las tareas son construcciones que se usan para implementar lo que se conoce como el modelo de promesa de
simultaneidad. En resumen, le ofrecen una "promesa" de que el trabajo se completará en un momento posterior, lo
que le permite coordinarse con la promesa con una API limpia.
Task representa una única operación que no devuelve un valor.
Task<T> representa una única operación que devuelve un valor de tipo T .
Es importante razonar sobre las tareas como abstracciones de trabajo que se producen de forma asincrónica y no
una abstracción sobre subprocesos. De manera predeterminada, las tareas se ejecutan en el trabajo de subproceso
y delegado actual del sistema operativo, según corresponda. De forma opcional, se puede solicitar de forma
explícita que se ejecuten las tareas en un subproceso independiente mediante la API Task.Run .
Las tareas exponen un protocolo de API para supervisar, esperar y acceder al valor del resultado (en el caso de
Task<T> ) de una tarea. La integración de lenguajes, con la palabra clave await , proporciona una abstracción de
alto nivel para usar tareas.
Mediante await , su aplicación o servicio puede realizar trabajo útil mientras se ejecuta una tarea al ceder el
control a su llamador hasta que se realiza la tarea. El código no tiene que depender de las devoluciones de llamada
ni eventos para seguir ejecutándose una vez completada la tarea. La integración de la API de tareas y lenguajes se
encarga de ello. Si está usando Task<T> , la palabra clave await "desencapsulará" también el valor devuelto
cuando se completa la tarea. Más adelante se explican los detalles sobre cómo funciona esto.
Puede obtener más información sobre las tareas y las distintas formas de interactuar con ellas en el tema Modelo
asincrónico basado en tareas (TAP).
return client.GetStringAsync("https://www.dotnetfoundation.org");
}
En el segundo ejemplo, se agrega el uso de las palabras clave async y await para que funcionen en la tarea.
La llamada a GetStringAsync() se realiza a través de bibliotecas de .NET de nivel inferior (es posible que se llame a
otros métodos asincrónicos) hasta que se alcanza una llamada de interoperabilidad de P/Invoke en una biblioteca
de red nativa. La biblioteca nativa puede realizar posteriormente una llamada API del sistema (como write() a un
socket en Linux). Se creará un objeto de tarea en el límite nativo o administrado, posiblemente mediante
TaskCompletionSource. El objeto de tarea se pasará por las capas, funcionará en ellas o se devolverá directamente
y, finalmente, se devuelve al llamador inicial.
En este segundo ejemplo, se devolverá un objeto Task<T> de GetStringAsync . El uso de la palabra clave await
hace que el método devuelva un objeto de tarea recién creado. El control vuelve al llamador de esta ubicación en el
método GetFirstCharactersCountAsync . Los métodos y propiedades del objeto Task<T> permiten que los
llamadores supervisen el progreso de la tarea, que se completará cuando se haya ejecutado el código restante en
GetFirstCharactersCountAsync.
Después de la llamada API del sistema, la solicitud ahora está en el espacio del kernel, que avanza hacia el
subsistema de red del sistema operativo (como /net en Linux Kernel). Aquí, el sistema operativo controlará la
solicitud de red de forma asincrónica. Los detalles pueden ser diferentes según el sistema operativo usado (la
llamada al controlador de dispositivo puede programarse como una señal devuelta al tiempo de ejecución o una
llamada al controlador de dispositivo puede realizarse y después se devuelve una señal), pero, finalmente, el
tiempo de ejecución recibirá la información de que la solicitud de red está en curso. En este momento, el trabajo del
controlador de dispositivo estará programado, en curso o ya estará terminado (la solicitud ya estará "en la
conexión"), pero como esto se produce de forma asincrónica, el controlador de dispositivo es capaz de controlar
otra cosa de forma inmediata.
Por ejemplo, en Windows, un subproceso de sistema operativo realiza una llamada al controlador de dispositivo
de red y le pide que realice la operación de red a través de un paquete de petición de interrupción (IRP) que
representa la operación. El controlador de dispositivo recibe el IRP, realiza la llamada a la red, marca el IRP como
"pendiente" y vuelve al sistema operativo. Ya que el subproceso de sistema operativo ahora sabe que el IRP está
"pendiente", no tiene nada más que hacer en este trabajo y "vuelve", de modo que se puede usar para realizar otro
trabajo.
Cuando se haya realizado la solicitud y regresen los datos a través del controlador de dispositivo, notifica a la CPU
que se han recibido nuevos datos mediante una interrupción. La forma en que se controla esta interrupción varía
según el sistema operativo, pero, finalmente, los datos pasarán a través del sistema operativo hasta que lleguen a
una llamada de interoperabilidad del sistema (por ejemplo, en Linux un controlador de interrupciones programará
la mitad inferior de la IRQ para pasar los datos a través del sistema operativo de forma asincrónica). Tenga en
cuenta que esto también se produce de manera asincrónica. El resultado se pone en cola hasta que el siguiente
subproceso disponible puede ejecutar el método asincrónico y "desencapsular" el resultado de la tarea
completada.
A lo largo de todo este proceso, un punto clave es que ningún subproceso se dedica a ejecutar la tarea .
Aunque el trabajo se ejecuta en algún contexto (es decir, el sistema operativo tiene que pasar datos a un
controlador de dispositivo y responder a una interrupción), no hay ningún subproceso dedicado a esperar a que
vuelvan los datos de la solicitud. Esto permite al sistema controlar un volumen de trabajo mucho mayor en lugar
de esperar a que finalicen algunas llamadas de E/S.
Aunque lo anterior puede parecer mucho trabajo que realizar, al medirlo en términos de tiempo de reloj, es ínfimo
en comparación con el tiempo necesario para realizar el trabajo de E/S real. Aunque no es exacta, una escala de
tiempo posible para una llamada de este estilo tendría este aspecto:
0-1
————————————————————————————————————————————————–2-
3
El tiempo empleado de los puntos 0 a 1 es todo hasta que un método asincrónico cede el control a su
llamador.
El tiempo empleado de los puntos 1 a 2 es el tiempo transcurrido en E/S, sin ningún costo de CPU.
Por último, el tiempo empleado de los puntos 2 a 3 es durante el que se pasa el control (y posiblemente un
valor) de nuevo al método asincrónico, momento en que se vuelve a ejecutar.
¿Qué significa esto en un escenario de servidor?
Este modelo funciona bien con una carga de trabajo de escenario de servidor típica. Ya que no hay ningún
subproceso dedicado al bloqueo de tareas incompletas, el grupo de subprocesos de servidor puede atender a un
mayor volumen de solicitudes web.
Considere dos servidores: uno que ejecute código asincrónico y otro que no lo haga. Para este ejemplo, cada
servidor tiene solo 5 subprocesos disponibles para las solicitudes de servicio. Tenga en cuenta que estos números
son pequeños y sirven solo en un contexto demostrativo.
Suponga que ambos servidores reciben seis solicitudes simultáneas. Cada solicitud realiza una operación de E/S. El
servidor sin código asincrónico tiene que poner en cola la solicitud 6 hasta que uno de los 5 subprocesos haya
finalizado el trabajo enlazado a E/S y escrito una respuesta. En el momento en que entre la solicitud 20, el servidor
puede comenzar a ralentizarse, porque la cola se extiende demasiado.
El servidor con código asincrónico en ejecución también pone en cola la solicitud 6, pero ya que usa async y
await , cada uno de los subprocesos se libera cuando se inicia el trabajo enlazado a E/S, en lugar de cuando
finaliza. Cuando llega la solicitud 20, la cola de solicitudes entrantes será mucho más pequeña (en caso de que haya
algo) y no se ralentiza el servidor.
Aunque se trata de un ejemplo inventado, ocurre de forma muy similar en el mundo real. De hecho, puede esperar
que un servidor pueda controlar más solicitudes mediante async y await que si se dedicaba a un subproceso
para cada solicitud que recibe.
¿Qué significa esto en un escenario de cliente?
El mayor beneficio al usar async y await para una aplicación cliente es un aumento en la capacidad de respuesta.
Aunque puede crear una aplicación dinámica al generar subprocesos de forma manual, el hecho de generar un
subproceso es una operación costosa en comparación a usar solo async y await . Especialmente en el caso de
juegos para móviles, es fundamental afectar lo mínimo posible al subproceso de interfaz de usuario en lo que a
E/S se refiere.
Sobre todo, ya que el trabajo enlazado a E/S no invierte prácticamente ningún tiempo en la CPU, dedicar un
subproceso de CPU completo a realizar prácticamente ningún trabajo útil sería un mal uso de recursos.
Además, es muy sencillo enviar trabajo al subproceso de interfaz de usuario (como actualizar una interfaz de
usuario) con métodos async y no requiere trabajo adicional (como llamar a un delegado seguro para
subprocesos).
// Note that at this point, you can do some other work concurrently,
// as CalculateResult() is still executing!
return result;
}
CalculateResult() se ejecuta en el subproceso en que se ha llamado. Cuando llama a Task.Run , pone en cola la
operación costosa enlazada a la CPU, DoExpensiveCalculation() , en el grupo de subprocesos y recibe un
controlador Task<int> . DoExpensiveCalculation() se ejecuta finalmente de forma simultánea en el siguiente
subproceso disponible, probablemente en otro núcleo de CPU. Es posible realizar trabajo simultáneo mientras
DoExpensiveCalculation() está ocupado en otro subproceso, ya que el subproceso que llama a CalculateResult()
aún se está ejecutando.
Una vez se encuentra await , la ejecución de CalculateResult() se cede a su llamador, lo que permite que se
realice otro trabajo con el subproceso actual mientras DoExpensiveCalculation() genera un resultado. Una vez que
haya finalizado, el resultado se pone en la cola para ejecutarse en el subproceso principal. Finalmente, el
subproceso principal volverá a ejecutar CalculateResult() , momento en que tendrá el resultado de
DoExpensiveCalculation() .
Vea también
Programación asincrónica en C#
Programación asincrónica con async y await (C#)
Programación asincrónica en F#
Programación asincrónica con Async y Await (Visual Basic)
Patrones para la programación asincrónica
16/09/2020 • 3 minutes to read • Edit Online
Comparación de patrones
Para una comparación rápida de cómo los tres patrones modelan las operaciones asincrónicas, considere un
método Read que lea una determinada cantidad de datos en un búfer proporcionado comenzando en un
desplazamiento especificado:
Vea también
Async en profundidad
Programación asincrónica en C#
Programación asincrónica en F#
Programación asincrónica con Async y Await (Visual Basic)
Programación en paralelo en .NET
16/09/2020 • 3 minutes to read • Edit Online
Muchos equipos y estaciones de trabajo personales tienen varios núcleos de CPU que permiten ejecutar
múltiples subprocesos simultáneamente. Para aprovecharse del hardware, puede paralelizar el código para
distribuir el trabajo entre varios procesadores.
En el pasado, la paralelización requería manipulación de bajo nivel de los subprocesos y bloqueos. Visual Studio
y .NET Framework mejoran la compatibilidad para la programación paralela proporcionando un tiempo de
ejecución, tipos de biblioteca de clases y herramientas de diagnóstico. Estas características, que se presentaron
con .NET Framework 4, simplifican el desarrollo en paralelo. Puede escribir código paralelo eficaz, específico y
escalable de forma natural sin tener que trabajar directamente con subprocesos ni el bloque de subprocesos.
La siguiente ilustración proporciona una información general de alto nivel de la arquitectura de programación
paralela en .NET Framework:
Temas relacionados
T EC N O LO GÍA DESC RIP C IÓ N
Estructuras de datos para la programación paralela Proporciona vínculos a documentación sobre las clases de
colección seguras para subprocesos, tipos de sincronización
ligeros y tipos para la inicialización diferida.
T EC N O LO GÍA DESC RIP C IÓ N
Particionadores personalizados para PLINQ y TPL Describe cómo funcionan los particionadores y cómo
configurar particionadores predeterminados o crear nuevos.
Expresiones lambda en PLINQ y TPL Proporciona información general sobre expresiones lambda
en C# y Visual Basic, y presenta cómo se utilizan en PLINQ y
Task Parallel Library.
Vea también
Información general de Async
Subprocesamiento administrado
Biblioteca de procesamiento paralelo basado en
tareas (TPL)
16/09/2020 • 3 minutes to read • Edit Online
La biblioteca TPL (Task Parallel Library, biblioteca de procesamiento paralelo basado en tareas) es un conjunto de
API y tipos públicos de los espacios de nombres System.Threading y System.Threading.Tasks. El propósito de la
TPL es aumentar la productividad de los desarrolladores simplificando el proceso de agregar paralelismo y
simultaneidad a las aplicaciones. La TPL escala el grado de simultaneidad de manera dinámica para usar con
mayor eficacia todos los procesadores disponibles. Además, la TPL se encarga de la división del trabajo, la
programación de los subprocesos en ThreadPool, la compatibilidad con la cancelación, la administración de los
estados y otros detalles de bajo nivel. Al utilizar la TPL, el usuario puede optimizar el rendimiento del código
mientras se centra en el trabajo para el que el programa está diseñado.
A partir de .NET Framework 4, la TPL es el modo preferido de escribir código paralelo y multiproceso. Sin
embargo, no todo el código se presta para la paralelización; por ejemplo, si un bucle realiza solo una cantidad
reducida de trabajo en cada iteración o no se ejecuta para un gran número de iteraciones, la sobrecarga de la
paralelización puede dar lugar a una ejecución más lenta del código. Además, al igual que cualquier código
multiproceso, la paralelización hace que la ejecución del programa sea más compleja. Aunque la TPL simplifica
los escenarios de multithreading, recomendamos tener conocimientos básicos sobre conceptos de
subprocesamiento, por ejemplo, bloqueos, interbloqueos y condiciones de carrera, para usar la TPL eficazmente.
Temas relacionados
T IT L E DESC RIP C IÓ N
Programación asincrónica basada en tareas Describe cómo crear y ejecutar tareas implícitamente
mediante Parallel.Invoke o explícitamente usando objetos Task
directamente.
Usar TPL con otros patrones asincrónicos Describe cómo utilizar la TPL con otros modelos asincrónicos
de .NET.
Problemas potenciales en el paralelismo de datos y tareas Describe algunos problemas comunes y cómo evitarlos.
Parallel LINQ (PLINQ) Describe cómo lograr el paralelismo de datos con consultas
LINQ.
Vea también
Ejemplos de programación en paralelo con .NET Core y .NET Standard
Paralelismo de datos (biblioteca TPL)
16/09/2020 • 6 minutes to read • Edit Online
El paralelismo de datos hace referencia a los escenarios en los que la misma operación se realiza
simultáneamente (es decir, en paralelo) en elementos de una colección o matriz de origen. En las operaciones
paralelas de datos, se crean particiones de la colección de origen para que varios subprocesos puedan funcionar
simultáneamente en segmentos diferentes.
La biblioteca TPL (Task Parallel Library, biblioteca de procesamiento paralelo basado en tareas) admite el
paralelismo de datos a través de la clase System.Threading.Tasks.Parallel. Esta clase proporciona las
implementaciones paralelas basadas en método de los bucles for y foreach ( For y For Each en Visual Basic). Se
escribe la lógica del bucle para un bucle Parallel.For o Parallel.ForEach de forma muy similar a como se escribiría
un bucle secuencial. No tiene que crear los subprocesos ni poner en la cola los elementos de trabajo. En bucles
básicos, no es preciso tomar bloqueos. TPL administra todo el trabajo de bajo nivel. Para obtener información
detallada sobre el uso de Parallel.For y Parallel.ForEach, descargue el documento Patterns for Parallel
Programming: Understanding and Applying Parallel Patterns with the .NET Framework 4 (Patrones de
programación en paralelo: descripción y aplicación de los patrones en paralelo con .NET Framework 4). En el
siguiente ejemplo de código se muestra un bucle foreach simple y su equivalente paralelo.
NOTE
En esta documentación, se utilizan expresiones lambda para definir delegados en la TPL. Si no está familiarizado con las
expresiones lambda de C# o Visual Basic, consulte Expresiones lambda en PLINQ y TPL.
// Sequential version
foreach (var item in sourceCollection)
{
Process(item);
}
// Parallel equivalent
Parallel.ForEach(sourceCollection, item => Process(item));
Cuando un bucle paralelo se ejecuta, la TPL crea particiones del origen de datos para que el bucle pueda
funcionar simultáneamente en varias partes. En segundo plano, el programador de tareas crea particiones de la
tarea según los recursos del sistema y la carga de trabajo. Cuando es posible, el programador redistribuye el
trabajo entre varios subprocesos y procesadores si se desequilibra la carga de trabajo.
NOTE
También puede proporcionar un programador o creador de particiones personalizado. Para más información, consulte
Particionadores personalizados para PLINQ y TPL y Programadores de tareas.
Los métodos Parallel.For y Parallel.ForEach tienen varias sobrecargas que permiten detener o ejecutar la
ejecución de bucles, supervisar el estado del bucle en otros subprocesos, mantener el estado de subprocesos
locales, finalizar los objetos de subprocesos locales, controlar el grado de simultaneidad, etc. Los tipos del
asistente que habilitan esta funcionalidad son ParallelLoopState, ParallelOptions, ParallelLoopResult,
CancellationToken y CancellationTokenSource.
Para obtener más información, vea Patterns for Parallel Programming: Understanding and Applying Parallel
Patterns with the .NET Framework 4 (Patrones de programación en paralelo: descripción y aplicación de los
patrones en paralelo con .NET Framework 4).
PLINQ admite el paralelismo de datos con sintaxis declarativa o de consulta. Para más información, consulte
Parallel LINQ (PLINQ) (LINQ en paralelo [PLINQ]).
Temas relacionados
T IT L E DESC RIP C IÓ N
Cómo: Escribir un bucle Parallel.For sencillo Describe cómo escribir un bucle For en cualquier matriz o
colección de origen IEnumerable<T> indexable.
Cómo: Escribir un bucle Parallel.ForEach sencillo Describe cómo escribir un bucle ForEach en cualquier
colección de origen IEnumerable<T>.
Cómo: Detener o interrumpir un bucle Parallel.For Describe cómo detenerse o salir de un bucle paralelo de
forma que todos los subprocesos se informen de la acción.
Cómo: Escribir un bucle Parallel.For con variables locales de Describe cómo escribir un bucle For en el que cada
subproceso subproceso mantiene una variable privada que no está visible
para cualquier otro subproceso y cómo sincronizar los
resultados de todos los subprocesos cuando el bucle se
completa.
Cómo: Escribir un bucle Parallel.ForEach con variables locales Describe cómo escribir un bucle ForEach en el que cada
de partición subproceso mantiene una variable privada que no está visible
para cualquier otro subproceso y cómo sincronizar los
resultados de todos los subprocesos cuando el bucle se
completa.
Cómo: Cancelar un bucle Parallel.For o ForEach Describe cómo cancelar un bucle paralelo utilizando un
System.Threading.CancellationToken
Cómo: Acelerar cuerpos de bucle pequeños Describe una manera de acelerar la ejecución cuando el
cuerpo de un bucle es muy pequeño.
Vea también
Programación en paralelo
Procedimiento para escribir un bucle Parallel.For
simple
16/09/2020 • 13 minutes to read • Edit Online
Este tema contiene dos ejemplos que ilustran el método Parallel.For. El primer ejemplo usa la sobrecarga del
método Parallel.For(Int64, Int64, Action<Int64>) y el segundo usa la sobrecarga Parallel.For(Int32, Int32,
Action<Int32>), las dos sobrecargas más simples del método Parallel.For. Puede usar estas dos sobrecargas del
método Parallel.For cuando no sea necesario cancelar el bucle, interrumpir las iteraciones del bucle o mantener
cualquier estado local de subproceso.
NOTE
En esta documentación, se utilizan expresiones lambda para definir delegados en la TPL. Si no está familiarizado con las
expresiones lambda de C# o Visual Basic, consulte Expresiones lambda en PLINQ y TPL.
El primer ejemplo calcula el tamaño de los archivos de un solo directorio. El segundo calcula el producto de dos
matrices.
Module Example
Public Sub Main()
Dim totalSize As Long = 0
using System;
using System.Diagnostics;
using System.Threading.Tasks;
class MultiplyMatrices
{
#region Sequential_Loop
static void MultiplyMatricesSequential(double[,] matA, double[,] matB,
double[,] result)
{
int matACols = matA.GetLength(1);
int matBCols = matB.GetLength(1);
int matARows = matA.GetLength(0);
#region Parallel_Loop
static void MultiplyMatricesParallel(double[,] matA, double[,] matB, double[,] result)
{
int matACols = matA.GetLength(1);
int matBCols = matB.GetLength(1);
int matARows = matA.GetLength(0);
#region Main
static void Main(string[] args)
{
// Set up matrices. Use small values to better view
// result matrix. Increase the counts to see greater
// speedup in the parallel loop vs. the sequential loop.
int colCount = 180;
int rowCount = 2000;
int colCount2 = 270;
double[,] m1 = InitializeMatrix(rowCount, colCount);
double[,] m2 = InitializeMatrix(colCount, colCount2);
double[,] result = new double[rowCount, colCount2];
#region Helper_Methods
static double[,] InitializeMatrix(int rows, int cols)
{
double[,] matrix = new double[rows, cols];
Imports System.Diagnostics
Imports System.Threading.Tasks
Module MultiplyMatrices
#Region "Sequential_Loop"
Sub MultiplyMatricesSequential(ByVal matA As Double(,), ByVal matB As Double(,), ByVal result As
Double(,))
Dim matACols As Integer = matA.GetLength(1)
Dim matBCols As Integer = matB.GetLength(1)
Dim matARows As Integer = matA.GetLength(0)
#Region "Main"
Sub Main(ByVal args As String())
' Set up matrices. Use small values to better view
' result matrix. Increase the counts to see greater
' speedup in the parallel loop vs. the sequential loop.
Dim colCount As Integer = 180
Dim rowCount As Integer = 2000
Dim colCount2 As Integer = 270
Dim m1 As Double(,) = InitializeMatrix(rowCount, colCount)
Dim m2 As Double(,) = InitializeMatrix(colCount, colCount2)
Dim result As Double(,) = New Double(rowCount - 1, colCount2 - 1) {}
#Region "Helper_Methods"
Function InitializeMatrix(ByVal rows As Integer, ByVal cols As Integer) As Double(,)
Dim matrix As Double(,) = New Double(rows - 1, cols - 1) {}
Sub OfferToPrint(ByVal rowCount As Integer, ByVal colCount As Integer, ByVal matrix As Double(,))
Console.Error.Write("Computation complete. Display results (y/n)? ")
Dim c As Char = Console.ReadKey(True).KeyChar
Console.Error.WriteLine(c)
If Char.ToUpperInvariant(c) = "Y"c Then
If Not Console.IsOutputRedirected Then Console.WindowWidth = 168
Console.WriteLine()
For x As Integer = 0 To rowCount - 1
Console.WriteLine("ROW {0}: ", x)
For y As Integer = 0 To colCount - 1
Console.Write("{0:#.##} ", matrix(x, y))
Next
Console.WriteLine()
Next
End If
End Sub
#End Region
End Module
Al paralelizar código, incluidos los bucles, un objetivo importante consiste en usar los procesadores tanto como
sea posible, sin paralelizar en exceso hasta el punto de que la sobrecarga del procesamiento en paralelo niegue
cualquier mejora en el rendimiento. En este ejemplo concreto solo se paraleliza el bucle externo, ya que el bucle
interno no realiza mucho trabajo. La combinación de una pequeña cantidad de trabajo y efectos no deseados en la
caché puede producir una degradación del rendimiento en los bucles paralelos anidados. Por consiguiente,
paralelizar el bucle exterior solo es la mejor manera de maximizar las ventajas de simultaneidad en la mayoría de
los sistemas.
Delegado
El tercer parámetro de esta sobrecarga de For es un delegado de tipo Action<int> en C# o Action(Of Integer) en
Visual Basic. Un delegado Action , tanto si tiene uno, dieciséis o ningún parámetro de tipo, siempre devuelve void.
En Visual Basic, el comportamiento de Action se define con Sub . El ejemplo usa una expresión lambda para crear
el delegado, pero puede crear el delegado de otras maneras. Para obtener más información, consulte Expresiones
lambda en PLINQ y TPL.
Valor de iteración
El delegado toma un solo parámetro de entrada cuyo valor es la iteración actual. Este valor de iteración lo
suministra el tiempo de ejecución y su valor inicial es el índice del primer elemento del segmento (partición) del
origen que se está procesando en el subproceso actual.
Si necesita más control sobre el nivel de simultaneidad, use una de las sobrecargas que toma un parámetro de
entrada System.Threading.Tasks.ParallelOptions, como Parallel.For(Int32, Int32, ParallelOptions,
Action<Int32,ParallelLoopState>).
Análisis y rendimiento
Puede usar el Asistente de rendimiento para ver el uso de CPU en el equipo. Como experimento, aumente el
número de columnas y filas de las matrices. Cuanto mayores sean las matrices, mayor será la diferencia de
rendimiento entre las versiones paralelas y secuenciales del cálculo. Si la matriz es pequeña, la versión secuencial
se ejecutará más rápido debido a la sobrecarga de la configuración del bucle paralelo.
Las llamadas sincrónicas a los recursos compartidos, como la consola o el sistema de archivos, reducirán
considerablemente el rendimiento de un bucle paralelo. Al medir el rendimiento, intente evitar las llamadas como
Console.WriteLine dentro del bucle.
Compilar el código
Copie y pegue este código en un proyecto de Visual Studio.
Vea también
For
ForEach
Paralelismo de datos
Programación en paralelo
Procedimiento Escribir un bucle Parallel.ForEach
sencillo
16/09/2020 • 5 minutes to read • Edit Online
Este ejemplo muestra cómo utilizar un bucle Parallel.ForEach para habilitar el paralelismo de datos sobre cualquier
origen de datos System.Collections.IEnumerable o System.Collections.Generic.IEnumerable<T>.
NOTE
En esta documentación, se utilizan expresiones lambda para definir delegados en PLINQ. Si no está familiarizado con las
expresiones lambda de C# o Visual Basic, vea Expresiones lambda en PLINQ y TPL.
Ejemplo
En este ejemplo se supone que tiene varios archivos .jpg en la carpeta C:\Usuarios\Público\Imágenes\Imágenes de
muestra y crea una subcarpeta con el nombre Modificadas. Al ejecutar el ejemplo, gira cada imagen .jpg de
Imágenes de muestra y la guarda en Modificadas. Puede modificar las dos rutas de acceso según sea necesario.
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Drawing;
bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
bitmap.Save(Path.Combine(newDir, filename));
Module ForEachDemo
Sub Main()
' A simple source for demonstration purposes. Modify this path as necessary.
Dim files As String() = Directory.GetFiles("C:\Users\Public\Pictures\Sample Pictures", "*.jpg")
Dim newDir As String = "C:\Users\Public\Pictures\Sample Pictures\Modified"
Directory.CreateDirectory(newDir)
Parallel.ForEach(files, Sub(currentFile)
' The more computational work you do here, the greater
' the speedup compared to a sequential foreach loop.
Dim filename As String = Path.GetFileName(currentFile)
Dim bitmap As New Bitmap(currentFile)
bitmap.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone)
bitmap.Save(Path.Combine(newDir, filename))
Un bucle Parallel.ForEach funciona como un bucle Parallel.For. El bucle divide la colección de origen y programa el
trabajo en varios subprocesos en función del entorno del sistema. Mientras más procesadores haya en el sistema,
más rápido se ejecutará el método paralelo. Para algunas colecciones de origen, un bucle secuencial puede ser más
rápido, dependiendo del tamaño del origen y del tipo de trabajo que realiza el bucle. Para más información sobre el
rendimiento, vea Problemas potenciales en el paralelismo de datos y tareas.
Para obtener más información sobre los bucles paralelos, vea Procedimiento para escribir un bucle Parallel.For
sencillo.
Para usar Parallel.ForEach con una colección no genérica, puede usar el método de extensión Enumerable.Cast para
convertir la colección en una colección genérica, como se muestra en el ejemplo siguiente:
Parallel.ForEach(nonGenericCollection.Cast<object>(),
currentElement =>
{
});
Parallel.ForEach(nonGenericCollection.Cast(Of Object), _
Sub(currentElement)
' ... work with currentElement
End Sub)
También puede utilizar Parallel LINQ (PLINQ) para paralelizar el procesamiento de orígenes de datos
IEnumerable<T>. PLINQ permite usar la sintaxis de consulta declarativa para expresar el comportamiento del
bucle. Para más información, consulte Parallel LINQ (PLINQ).
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="4.5.1" />
</ItemGroup>
Para ejecutar una aplicación de consola .NET Core desde la línea de comandos, use dotnet run desde la carpeta
que contenga la aplicación.
Para ejecutar la aplicación de consola desde Visual Studio, presione F5 .
Vea también
Paralelismo de datos (biblioteca TPL)
Programación en paralelo en .NET
Parallel LINQ (PLINQ)
Procedimiento para escribir un bucle Parallel.For con
variables locales de subproceso
16/09/2020 • 6 minutes to read • Edit Online
En este ejemplo se muestra la forma de usar variables locales para el subproceso para almacenar y recuperar el
estado en cada una de las tareas independientes creadas por un bucle For. Mediante el uso de datos locales de
subproceso, se puede evitar la sobrecarga que supone la sincronización de un gran número de accesos a un
estado compartido. En vez de escribir en un recurso compartido en cada iteración, se calcula y se almacena el
valor hasta que finalizan todas las iteraciones de la tarea. A continuación, se escribe una vez el resultado final en el
recurso compartido o se pasa a otro método.
Ejemplo
En el ejemplo siguiente se llama al método For<TLocal>(Int32, Int32, Func<TLocal>,
Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) para calcular la suma de los valores de una matriz
que contiene un millón de elementos. El valor de cada elemento es igual a su índice.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
class Test
{
static void Main()
{
int[] nums = Enumerable.Range(0, 1000000).ToArray();
long total = 0;
Imports System.Threading
Imports System.Threading.Tasks
Module ForWithThreadLocal
Sub Main()
Dim nums As Integer() = Enumerable.Range(0, 1000000).ToArray()
Dim total As Long = 0
' Use type parameter to make subtotal a Long type. Function will overflow otherwise.
Parallel.For(Of Long)(0, nums.Length, Function() 0, Function(j, [loop], subtotal)
subtotal += nums(j)
Return subtotal
End Function, Function(x) Interlocked.Add(total,
x))
End Module
Los dos primeros parámetros de cada método For especifican los valores de iteración inicial y final. En esta
sobrecarga del método, el tercer parámetro es donde se inicializa el estado local. En este contexto, estado local
significa una variable cuya duración se extiende desde justo antes de la primera iteración del bucle en el
subproceso actual, hasta justo después de la última iteración.
El tipo del tercer parámetro es Func<TResult>, donde TResult es el tipo de la variable que almacenará el estado
local de subproceso. Su tipo se define mediante el argumento de tipo genérico que se proporciona al llamar al
método For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>)
genérico, que en este caso es Int64. El argumento de tipo le indica al compilador el tipo de variable temporal que
se usará para almacenar el estado local de subproceso. En este ejemplo, la expresión () => 0 (o Function() 0 en
Visual Basic) inicializa la variable local de subproceso en cero. Si el argumento de tipo genérico es un tipo de
referencia o un tipo de valor definido por el usuario, la expresión tendrá el aspecto siguiente:
El cuarto parámetro define la lógica del bucle y debe ser un delegado o una expresión lambda cuya firma sea
Func<int, ParallelLoopState, long, long> en C# o Func(Of Integer, ParallelLoopState, Long, Long) en Visual
Basic. El primer parámetro es el valor del contador de bucle correspondiente a esa iteración del bucle. El segundo
es un objeto ParallelLoopState que se puede usar para desglosar el bucle; la clase Parallel proporciona este objeto
a cada repetición del bucle. El tercer parámetro es la variable local de subproceso. El último parámetro es el tipo de
valor devuelto. En este caso, el tipo es Int64 porque es el que se especificó en el argumento de tipo For. Esa
variable se denomina subtotal y la devuelve la expresión lambda. El valor devuelto se usa para inicializar
subtotal en todas las iteraciones posteriores del bucle. Este último parámetro también se puede considerar como
un valor que se pasa a todas las iteraciones y que, cuando finaliza la última iteración, se pasa al delegado
localFinally .
El quinto parámetro define el método que se llama una vez, después de que se hayan completado todas las
iteraciones de un subproceso en particular. El tipo del argumento de entrada se corresponde de nuevo con el
argumento de tipo del método For<TLocal>(Int32, Int32, Func<TLocal>,
Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) y el tipo devuelto por la expresión lambda del
cuerpo. En este ejemplo, el valor se agrega a una variable en el ámbito de clase de un modo seguro para
subprocesos mediante una llamada al método Interlocked.Add. Gracias a la variable local de subproceso, se ha
evitado el tener que escribir en esta variable de clase en cada iteración del bucle.
Para obtener más información sobre cómo usar las expresiones lambda, vea Expresiones lambda en PLINQ y TPL.
Vea también
Paralelismo de datos
Programación en paralelo
Biblioteca TPL
Expresiones lambda en PLINQ y TPL
Procedimiento para escribir un bucle Parallel.ForEach
con variables locales de partición
16/09/2020 • 6 minutes to read • Edit Online
En el ejemplo siguiente se muestra cómo escribir un método ForEach en el que se usan variables locales de
partición. Cuando un bucle ForEach se ejecuta, divide su colección de origen en varias particiones. Cada partición
tiene su propia copia de la variable local de partición. Una variable local de partición es similar a una variable local
de subproceso, excepto por el hecho de que varias particiones se pueden ejecutar en un único subproceso.
El código y los parámetros de este ejemplo se parecen mucho al método For correspondiente. Para obtener más
información, vea Cómo: Escribir un bucle Parallel.For con variables locales de subproceso.
Para usar una variable local de partición en un bucle ForEach, debe llamar a una de las sobrecargas del método
que toma dos parámetros de tipo. El primer parámetro de tipo, TSource , especifica el tipo del elemento de origen,
mientras que el segundo parámetro de tipo, TLocal , especifica el tipo de la variable local de partición.
Ejemplo
En el ejemplo siguiente se realiza una llamada a la sobrecarga de Parallel.ForEach<TSource,TLocal>
(IEnumerable<TSource>, Func<TLocal>, Func<TSource,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) para
calcular la suma de una matriz de un millón de elementos. Esta sobrecarga gráfico tiene cuatro parámetros:
source, que es el origen de datos. Debe implementar IEnumerable<T>. El origen de datos de este ejemplo
es un objeto IEnumerable<Int32> de un millón de miembros que ha sido devuelto por el método
Enumerable.Range.
localInit , o bien la función que inicializa la variable local de partición. Esta función se llama una vez por
cada partición en la que se ejecuta la operación Parallel.ForEach. En el ejemplo se inicializa la variable local
de partición en cero.
body , un Func<T1,T2,T3,TResult> al que invoca el bucle paralelo en cada iteración del mismo bucle. Su
firma es Func\<TSource, ParallelLoopState, TLocal, TLocal> . Se proporciona el código para el delegado y el
bucle pasa los parámetros de entrada, que son:
El elemento actual de IEnumerable<T>.
Una variable ParallelLoopState que se puede usar en el código del delegado a fin de examinar el
estado del bucle.
La variable local de partición.
El delegado devuelve la variable local de partición, y esta se pasa luego a la siguiente iteración del bucle que
se ejecuta en esa partición concreta. Cada una de las particiones del bucle mantiene una instancia
independiente de esta variable.
En el ejemplo, el delegado agrega el valor de cada entero a la variable local de partición, que mantiene un
total acumulado de los valores de los elementos enteros de esa partición.
localFinally , un delegado Action<TLocal> al que invoca Parallel.ForEach cuando se han completado las
operaciones de bucle en las particiones. El método Parallel.ForEach pasa al delegado Action<TLocal> el
valor final de la variable local de partición correspondiente a esta partición del bucle, y usted proporciona el
código que realiza la acción necesaria para combinar el resultado de esta partición con los resultados de las
otras. Varias tareas pueden invocar simultáneamente a este delegado. Debido a esto, en el ejemplo se usa el
método Interlocked.Add(Int32, Int32) se utiliza para sincronizar el acceso a la variable total . Como el tipo
de delegado es Action<T>, no hay valor devuelto.
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
class Test
{
static void Main()
{
int[] nums = Enumerable.Range(0, 1000000).ToArray();
long total = 0;
' How to: Write a Parallel.ForEach Loop That Has Thread-Local Variables
Imports System.Threading
Imports System.Threading.Tasks
Module ForEachThreadLocal
Sub Main()
Los métodos Parallel.For y Parallel.ForEach admiten la cancelación mediante tokens de cancelación. Para obtener
más información sobre la cancelación, vea Cancelación. En un bucle paralelo, se proporciona CancellationToken al
método en el parámetro ParallelOptions y, a continuación, incluye la llamada paralela en un bloque try-catch.
Ejemplo
En el ejemplo siguiente se muestra cómo cancelar una llamada a Parallel.ForEach. Puede aplicar el mismo enfoque
a una llamada Parallel.For.
namespace CancelParallelLoops
{
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main()
{
int[] nums = Enumerable.Range(0, 10000000).ToArray();
CancellationTokenSource cts = new CancellationTokenSource();
try
{
Parallel.ForEach(nums, po, (num) =>
{
double d = Math.Sqrt(num);
Console.WriteLine("{0} on {1}", d, Thread.CurrentThread.ManagedThreadId);
po.CancellationToken.ThrowIfCancellationRequested();
});
}
catch (OperationCanceledException e)
{
Console.WriteLine(e.Message);
}
finally
{
cts.Dispose();
}
Console.ReadKey();
}
}
}
' How to: Cancel a Parallel.For or ForEach Loop
Imports System.Threading
Imports System.Threading.Tasks
Module CancelParallelLoops
Sub Main()
Dim nums() As Integer = Enumerable.Range(0, 10000000).ToArray()
Dim cts As New CancellationTokenSource
Try
' The error "Exception is unhandled by user code" will appear if "Just My Code"
' is enabled. This error is benign. You can press F5 to continue, or disable Just My Code.
Parallel.ForEach(nums, po, Sub(num)
Dim d As Double = Math.Sqrt(num)
Console.CursorLeft = 0
Console.Write("{0:##.##} on {1}", d,
Thread.CurrentThread.ManagedThreadId)
po.CancellationToken.ThrowIfCancellationRequested()
End Sub)
Catch e As OperationCanceledException
Console.WriteLine(e.Message)
Finally
cts.Dispose()
End Try
Console.ReadKey()
End Sub
End Module
Si el token que señala la cancelación es el mismo token que se especifica en la instancia ParallelOptions, el bucle
paralelo producirá una sola clase OperationCanceledException en la cancelación. Si algún otro token produce la
cancelación, el bucle producirá una clase AggregateException con una clase OperationCanceledException como una
excepción interna.
Vea también
Paralelismo de datos
Expresiones lambda en PLINQ y TPL
Procedimiento para controlar excepciones en bucles
paralelos
16/09/2020 • 4 minutes to read • Edit Online
Las sobrecargas Parallel.ForParallel.ForEach no tienen ningún mecanismo especial para controlar las excepciones
que puedan iniciarse. En este sentido, se asemejan a bucles for y foreach normales ( For y For Each en Visual
Basic). Una excepción no controlada hace que el bucle termine en cuanto finalicen todas las iteraciones que se estén
ejecutando.
Cuando agregue su propia lógica de control de excepciones a los bucles paralelos, tenga en cuenta la posibilidad de
que se inicien excepciones similares en varios subprocesos al mismo tiempo y la opción de que una excepción
iniciada en un subproceso puede hacer que se inicie otra excepción en otro subproceso. Puede controlar ambos
casos ajustando todas las excepciones del bucle en System.AggregateException. En el ejemplo siguiente, se muestra
un posible enfoque.
NOTE
Cuando está habilitada la opción "Solo mi código", en algunos casos, Visual Studio se interrumpe en la línea que produce la
excepción y muestra el mensaje de error "Excepción no controlada por el código de usuario". Este error es benigno. Puede
presionar F5 para continuar y ver el comportamiento de control de excepciones que se muestra en el siguiente ejemplo. Para
evitar que Visual Studio se interrumpa con el primer error, desactive la casilla "Solo mi código" en Herramientas, Opciones,
Depurar, General.
Ejemplo
En este ejemplo, se detectan todas las excepciones y, a continuación, se ajusta en un System.AggregateException
que se produce. El llamador puede decidir qué excepciones se deben administrar.
class ExceptionDemo2
{
static void Main(string[] args)
{
// Create some random data to process in parallel.
// There is a good probability this data will cause some exceptions to be thrown.
byte[] data = new byte[5000];
Random r = new Random();
r.NextBytes(data);
try
{
ProcessDataInParallel(data);
}
catch (AggregateException ae)
{
var ignoredExceptions = new List<Exception>();
// This is where you can choose which exceptions to handle.
foreach (var ex in ae.Flatten().InnerExceptions)
{
if (ex is ArgumentException)
Console.WriteLine(ex.Message);
else
ignoredExceptions.Add(ex);
}
if (ignoredExceptions.Count > 0) throw new AggregateException(ignoredExceptions);
}
Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Threading.Tasks
Module ExceptionsInLoops
Sub Main()
Try
ProcessDataInParallel(data)
Catch ae As AggregateException
Dim ignoredExceptions As New List(Of Exception)
' This is where you can choose which exceptions to handle.
For Each ex As Exception In ae.Flatten().InnerExceptions
If (TypeOf (ex) Is ArgumentException) Then
Console.WriteLine(ex.Message)
Else
ignoredExceptions.Add(ex)
End If
Next
If ignoredExceptions.Count > 0 Then
Throw New AggregateException(ignoredExceptions)
End If
End Try
Console.WriteLine("Press any key to exit.")
Console.ReadKey()
End Sub
Sub ProcessDataInParallel(ByVal data As Byte())
Vea también
Paralelismo de datos
Expresiones lambda en PLINQ y TPL
Cómo: Acelerar cuerpos de bucle pequeños
16/09/2020 • 2 minutes to read • Edit Online
Si un bucle Parallel.For tiene un cuerpo pequeño, su rendimiento puede ser menor que el del bucle secuencial
equivalente, como el bucle for de C# y el bucle For de Visual Basic. El menor rendimiento se debe a la sobrecarga
implicada en la partición de los datos y al costo de invocar un delegado en cada iteración del bucle. Para abordar
estos escenarios, la clase Partitioner proporciona el método Partitioner.Create, que permite proporcionar un bucle
secuencial para el cuerpo del delegado de manera que el delegado solo se invoque una sola vez por partición, en
lugar de una vez por cada iteración. Para más información, consulte Custom Partitioners for PLINQ and TPL
(Particionadores personalizados para PLINQ y TPL).
Ejemplo
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main()
{
Module PartitionDemo
Sub Main()
' Source must be array or IList.
Dim source = Enumerable.Range(0, 100000).ToArray()
End Sub
End Module
El enfoque que se muestra en este ejemplo resulta útil cuando el bucle realiza una cantidad mínima de trabajo. A
medida que el trabajo requiere más procesamiento, probablemente obtendrá un rendimiento igual o mejor usando
un bucle For o ForEach con el particionador predeterminado.
Vea también
Data Parallelism (Paralelismo de datos)
Particionadores personalizados para PLINQ y TPL
Iteradores (C#)
Iteradores (Visual Basic)
Expresiones lambda en PLINQ y TPL
Cómo: Recorrer en iteración directorios con la clase
paralela
16/09/2020 • 7 minutes to read • Edit Online
En muchos casos, la iteración de archivo es una operación que se puede paralelizar fácilmente. El tema Cómo:
Recorrer en iteración directorios con PLINQ muestra la manera más fácil de realizar esta tarea para muchos
escenarios. Sin embargo, pueden surgir complicaciones cuando el código tiene que tratar con los muchos tipos de
excepciones que pueden surgir al obtener acceso al sistema de archivos. En el ejemplo siguiente se muestra un
enfoque para el problema. Usa una iteración basada en la pila para recorrer todos los archivos y carpetas en un
directorio especificado y habilita el código para detectar y controlar diversas excepciones. Por supuesto, la forma de
controlar las excepciones depende de usted.
Ejemplo
En el ejemplo siguiente se recorren los directorios secuencialmente, pero se procesan los archivos en paralelo. Este
enfoque es probablemente el mejor cuando hay una tasa alta de directorios y archivos. También es posible ejecutar
la iteración de directorio y obtener acceso a cada archivo secuencialmente. Probablemente no es eficaz paralelizar
ambos bucles a menos que esté dirigido específicamente a un equipo con un gran número de procesadores. Sin
embargo, como en todos los casos, se debe probar exhaustivamente la aplicación para determinar el mejor
enfoque.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main()
{
try {
TraverseTreeParallelForEach(@"C:\Program Files", (f) =>
{
// Exceptions are no-ops.
try {
// Do nothing with the data except read it.
byte[] data = File.ReadAllBytes(f);
}
catch (FileNotFoundException) {}
catch (IOException) {}
catch (UnauthorizedAccessException) {}
catch (SecurityException) {}
// Display the filename.
Console.WriteLine(f);
});
}
catch (ArgumentException) {
Console.WriteLine(@"The directory 'C:\Program Files' does not exist.");
}
// Determine whether to parallelize file processing on each folder based on processor count.
int procCount = System.Environment.ProcessorCount;
if (!Directory.Exists(root)) {
throw new ArgumentException();
}
dirs.Push(root);
try {
subDirs = Directory.GetDirectories(currentDir);
}
// Thrown if we do not have discovery permission on the directory.
catch (UnauthorizedAccessException e) {
Console.WriteLine(e.Message);
continue;
}
// Thrown if another process has deleted the directory after we retrieved its name.
catch (DirectoryNotFoundException e) {
Console.WriteLine(e.Message);
continue;
}
try {
files = Directory.GetFiles(currentDir);
}
catch (UnauthorizedAccessException e) {
Console.WriteLine(e.Message);
continue;
}
catch (DirectoryNotFoundException e) {
Console.WriteLine(e.Message);
continue;
}
catch (IOException e) {
Console.WriteLine(e.Message);
continue;
}
return false;
});
}
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.IO
Imports System.Security
Imports System.Threading
Imports System.Threading.Tasks
Module Example
Sub Main()
Try
TraverseTreeParallelForEach("C:\Program Files",
Sub(f)
' Exceptions are No-ops.
Try
' Do nothing with the data except read it.
Dim data() As Byte = File.ReadAllBytes(f)
' In the event the file has been deleted.
Catch e As FileNotFoundException
End Try
' Display the filename.
Console.WriteLine(f)
End Sub)
Catch e As ArgumentException
Console.WriteLine("The directory 'C:\Program Files' does not exist.")
End Try
' Keep the console window open.
Console.ReadKey()
End Sub
' Determine whether to parallelize file processing on each folder based on processor count.
Dim procCount As Integer = System.Environment.ProcessorCount
dirs.Push(root)
Try
subDirs = Directory.GetDirectories(currentDir)
' Thrown if we do not have discovery permission on the directory.
Catch e As UnauthorizedAccessException
Console.WriteLine(e.Message)
Continue While
' Thrown if another process has deleted the directory after we retrieved its name.
Catch e As DirectoryNotFoundException
Console.WriteLine(e.Message)
Continue While
End Try
Try
files = Directory.GetFiles(currentDir)
Catch e As UnauthorizedAccessException
Console.WriteLine(e.Message)
Continue While
Catch e As DirectoryNotFoundException
Console.WriteLine(e.Message)
Continue While
Catch e As IOException
Console.WriteLine(e.Message)
Continue While
End Try
Return False
End Function)
End Try
' Push the subdirectories onto the stack for traversal.
' This could also be done before handing the files.
For Each str As String In subDirs
dirs.Push(str)
Next
En este ejemplo, la E/S de archivo se realiza de forma sincrónica. Al trabajar con archivos grandes o conexiones de
red lentas, puede ser preferible obtener acceso a los archivos de forma asincrónica. Puede combinar las técnicas de
E/S asincrónica con la iteración paralela. Para más información, consulte TPL and Traditional .NET Framework
Asynchronous Programming (TPL y la programación asincrónica tradicional de .NET).
El ejemplo utiliza la variable local fileCount para mantener un número total de archivos procesados. Dado que
varias tareas podrían tener acceso simultáneamente a la variable, el acceso a la misma se sincroniza llamando al
método Interlocked.Add.
Tenga en cuenta que si se produce una excepción en el subproceso principal, los subprocesos que inicia el método
ForEach podrían seguir ejecutándose. Para detener estos subprocesos, puede establecer una variable booleana en
los controladores de excepciones y comprobar su valor en cada iteración del bucle paralelo. Si el valor indica que
se ha iniciado una excepción, use la variable ParallelLoopState para detener o interrumpir el bucle. Para más
información, vea Cómo: Detener o interrumpir un bucle Parallel.For.
Vea también
Data Parallelism (Paralelismo de datos)
Programación asincrónica basada en tareas
16/09/2020 • 60 minutes to read • Edit Online
La biblioteca TPL se basa en el concepto de tarea, que representa una operación asincrónica. De cierta forma, una
tarea recuerda a un subproceso o elemento de trabajo ThreadPool, pero en un nivel más alto de abstracción. El
término paralelismo de tareas hace referencia a la ejecución simultánea de una o varias tareas independientes.
Las tareas proporcionan dos ventajas fundamentales:
Un uso más eficaz y más escalable de los recursos del sistema.
En segundo plano, las tareas se ponen en la cola del elemento ThreadPool, que se ha mejorado con
algoritmos que determinan y ajustan el número de subprocesos y que ofrecen el equilibrio de carga para
maximizar el rendimiento. Esto hace que las tareas resulten relativamente ligeras y que, por tanto, pueda
crearse un gran número de ellas para habilitar un paralelismo pormenorizado.
Un mayor control mediante programación del que se puede conseguir con un subproceso o un elemento
de trabajo.
Las tareas y el marco que se crea en torno a ellas proporcionan un amplio conjunto de API que admiten el
uso de esperas, cancelaciones, continuaciones, control robusto de excepciones, estado detallado,
programación personalizada, y más.
Por estos dos motivos, en .NET Framework, TPL es la API preferida para escribir código multiproceso, asincrónico
y paralelo.
NOTE
En esta documentación, se utilizan expresiones lambda para definir delegados en la TPL. Si no está familiarizado con las
expresiones lambda de C# o Visual Basic, consulte Expresiones lambda en PLINQ y TPL.
NOTE
El número de instancias de Task que Invoke crea en segundo plano no es necesariamente igual al número de delegados que
se proporcionan. La TPL puede emplear varias optimizaciones, sobre todo con grandes números de delegados.
Para obtener más información, vea Cómo: Usar Parallel.Invoke para ejecutar operaciones en paralelo.
Para tener un mayor control de la ejecución de tareas o para devolver un valor de la tarea, debe trabajar con
objetos Task más explícitamente.
using System;
using System.Threading;
using System.Threading.Tasks;
Module Example
Public Sub Main()
Thread.CurrentThread.Name = "Main"
' Create a task and supply a user delegate by using a lambda expression.
Dim taskA = New Task(Sub() Console.WriteLine("Hello from taskA."))
' Start the task.
taskA.Start()
También se pueden usar los métodos Task.Run para crear e iniciar una tarea en una sola operación. Para
administrar la tarea, los métodos Run usan el programador de tareas predeterminado, independientemente qué
programador de tareas esté asociado al subproceso actual. Los métodos Run son el modo preferido para crear e
iniciar tareas cuando no se necesita más control sobre la creación y la programación de la tarea.
using System;
using System.Threading;
using System.Threading.Tasks;
Module Example
Public Sub Main()
Thread.CurrentThread.Name = "Main"
También se puede usar el método TaskFactory.StartNew para crear e iniciar una tarea en una sola operación.
Utilice este método cuando la creación y la programación no tengan que ser independientes y necesite opciones
de creación de tareas adicionales o el uso de un programador concreto, o cuando necesita pasar información de
estado adicional en la tarea que se puede recuperar mediante su propiedad Task.AsyncState, como se muestra en
el ejemplo siguiente.
using System;
using System.Threading;
using System.Threading.Tasks;
class CustomData
{
public long CreationTime;
public int Name;
public int ThreadNum;
}
data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
},
new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks}
);
}
Task.WaitAll(taskArray);
foreach (var task in taskArray) {
var data = task.AsyncState as CustomData;
if (data != null)
Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum);
}
}
}
// The example displays output like the following:
// Task #0 created at 635116412924597583 on thread #3.
// Task #1 created at 635116412924607584 on thread #4.
// Task #3 created at 635116412924607584 on thread #4.
// Task #4 created at 635116412924607584 on thread #4.
// Task #2 created at 635116412924607584 on thread #3.
// Task #6 created at 635116412924607584 on thread #3.
// Task #5 created at 635116412924607584 on thread #4.
// Task #8 created at 635116412924607584 on thread #4.
// Task #7 created at 635116412924607584 on thread #3.
// Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks
Class CustomData
Public CreationTime As Long
Public Name As Integer
Public ThreadNum As Integer
End Class
Module Example
Public Sub Main()
Dim taskArray(9) As Task
For i As Integer = 0 To taskArray.Length - 1
taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
Dim data As CustomData = TryCast(obj, CustomData)
If data Is Nothing Then Return
data.ThreadNum = Thread.CurrentThread.ManagedThreadId
End Sub,
New CustomData With {.Name = i, .CreationTime =
DateTime.Now.Ticks})
Next
Task.WaitAll(taskArray)
Task y Task<TResult> exponen en cada caso una propiedad Factory estática que devuelve una instancia
predeterminada de TaskFactory, por lo que se puede llamar al método como Task.Factory.StartNew() . Asimismo,
en el siguiente ejemplo, dado que las tareas son de tipo System.Threading.Tasks.Task<TResult>, cada una tiene
una propiedad Task<TResult>.Result pública que contiene el resultado del cálculo. Las tareas se ejecutan de forma
asincrónica y pueden completarse en cualquier orden. Si se obtiene acceso a la propiedad Result antes de que el
cálculo se complete, la propiedad bloqueará el subproceso hasta que el valor esté disponible.
using System;
using System.Threading.Tasks;
return sum;
}
}
// The example displays the following output:
// 606.0 + 10,605.0 + 100,495.0 = 111,706.0
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim taskArray() = {Task(Of Double).Factory.StartNew(Function() DoComputation(1.0)),
Task(Of Double).Factory.StartNew(Function() DoComputation(100.0)),
Task(Of Double).Factory.StartNew(Function() DoComputation(1000.0))}
using System;
using System.Threading;
using System.Threading.Tasks;
class CustomData
{
public long CreationTime;
public int Name;
public int ThreadNum;
}
Class CustomData
Public CreationTime As Long
Public Name As Integer
Public ThreadNum As Integer
End Class
Module Example
Public Sub Main()
' Create the task object by using an Action(Of Object) to pass in the loop
' counter. This produces an unexpected result.
Dim taskArray(9) As Task
For i As Integer = 0 To taskArray.Length - 1
taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
Dim data As New CustomData With {.Name = i,
.CreationTime = DateTime.Now.Ticks}
data.ThreadNum = Thread.CurrentThread.ManagedThreadId
Console.WriteLine("Task #{0} created at {1} on thread #
{2}.",
data.Name, data.CreationTime,
data.ThreadNum)
End Sub,
i)
Next
Task.WaitAll(taskArray)
End Sub
End Module
' The example displays output like the following:
' Task #10 created at 635116418427727841 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427737842 on thread #4.
' Task #10 created at 635116418427727841 on thread #3.
' Task #10 created at 635116418427747843 on thread #3.
' Task #10 created at 635116418427747843 on thread #3.
' Task #10 created at 635116418427737842 on thread #4.
Puede obtener acceso al valor en cada iteración si proporciona un objeto de estado a una tarea a través de su
constructor. En el ejemplo siguiente se modifica el ejemplo anterior utilizando el contador de bucle al crear el
objeto CustomData que, a su vez, se pasa a la expresión lambda. Como muestra el resultado del ejemplo, cada
objeto CustomData tiene ahora un identificador único basado en el valor del contador de bucle cuando se creó la
instancia del objeto.
using System;
using System.Threading;
using System.Threading.Tasks;
class CustomData
{
public long CreationTime;
public int Name;
public int ThreadNum;
}
data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Task #{0} created at {1} on thread #
{2}.",
data.Name, data.CreationTime,
data.ThreadNum);
},
new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks}
);
}
Task.WaitAll(taskArray);
}
}
// The example displays output like the following:
// Task #0 created at 635116412924597583 on thread #3.
// Task #1 created at 635116412924607584 on thread #4.
// Task #3 created at 635116412924607584 on thread #4.
// Task #4 created at 635116412924607584 on thread #4.
// Task #2 created at 635116412924607584 on thread #3.
// Task #6 created at 635116412924607584 on thread #3.
// Task #5 created at 635116412924607584 on thread #4.
// Task #8 created at 635116412924607584 on thread #4.
// Task #7 created at 635116412924607584 on thread #3.
// Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks
Class CustomData
Public CreationTime As Long
Public Name As Integer
Public ThreadNum As Integer
End Class
Module Example
Public Sub Main()
' Create the task object by using an Action(Of Object) to pass in custom data
' to the Task constructor. This is useful when you need to capture outer variables
' from within a loop.
Dim taskArray(9) As Task
For i As Integer = 0 To taskArray.Length - 1
taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
Dim data As CustomData = TryCast(obj, CustomData)
If data Is Nothing Then Return
data.ThreadNum = Thread.CurrentThread.ManagedThreadId
Console.WriteLine("Task #{0} created at {1} on thread #
{2}.",
data.Name, data.CreationTime,
data.ThreadNum)
End Sub,
New CustomData With {.Name = i, .CreationTime =
DateTime.Now.Ticks})
Next
Task.WaitAll(taskArray)
End Sub
End Module
' The example displays output like the following:
' Task #0 created at 635116412924597583 on thread #3.
' Task #1 created at 635116412924607584 on thread #4.
' Task #3 created at 635116412924607584 on thread #4.
' Task #4 created at 635116412924607584 on thread #4.
' Task #2 created at 635116412924607584 on thread #3.
' Task #6 created at 635116412924607584 on thread #3.
' Task #5 created at 635116412924607584 on thread #4.
' Task #8 created at 635116412924607584 on thread #4.
' Task #7 created at 635116412924607584 on thread #3.
' Task #9 created at 635116412924607584 on thread #4.
Este estado se pasa como argumento al delegado de la tarea y permite tener acceso desde el objeto de tarea
mediante la propiedad Task.AsyncState. El ejemplo siguiente es una variación del ejemplo anterior. Utiliza la
propiedad AsyncState para mostrar información sobre los objetos CustomData pasados a la expresión lambda.
using System;
using System.Threading;
using System.Threading.Tasks;
class CustomData
{
public long CreationTime;
public int Name;
public int ThreadNum;
}
data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
},
new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks}
);
}
Task.WaitAll(taskArray);
foreach (var task in taskArray) {
var data = task.AsyncState as CustomData;
if (data != null)
Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
data.Name, data.CreationTime, data.ThreadNum);
}
}
}
// The example displays output like the following:
// Task #0 created at 635116412924597583 on thread #3.
// Task #1 created at 635116412924607584 on thread #4.
// Task #3 created at 635116412924607584 on thread #4.
// Task #4 created at 635116412924607584 on thread #4.
// Task #2 created at 635116412924607584 on thread #3.
// Task #6 created at 635116412924607584 on thread #3.
// Task #5 created at 635116412924607584 on thread #4.
// Task #8 created at 635116412924607584 on thread #4.
// Task #7 created at 635116412924607584 on thread #3.
// Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading
Imports System.Threading.Tasks
Class CustomData
Public CreationTime As Long
Public Name As Integer
Public ThreadNum As Integer
End Class
Module Example
Public Sub Main()
Dim taskArray(9) As Task
For i As Integer = 0 To taskArray.Length - 1
taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
Dim data As CustomData = TryCast(obj, CustomData)
If data Is Nothing Then Return
data.ThreadNum = Thread.CurrentThread.ManagedThreadId
End Sub,
New CustomData With {.Name = i, .CreationTime =
DateTime.Now.Ticks})
Next
Task.WaitAll(taskArray)
Identificador de tarea
Cada tarea recibe un identificador entero que la identifica de manera inequívoca en un dominio de aplicación y al
que se puede obtener acceso mediante la propiedad Task.Id. El identificador resulta útil para ver información
sobre la tarea en las ventanas Pilas paralelas y Tareas del depurador de Visual Studio. El identificador se crea de
forma diferida, lo que significa que no se crea hasta que se solicita; por tanto, una tarea podrá tener un
identificador diferente cada vez que se ejecute el programa. Para más información sobre cómo ver los
identificadores de tarea en el depurador, consulte Usar la ventana Tareas y Usar la ventana Pilas paralelas.
Las opciones pueden combinarse con una operación OR . En el ejemplo siguiente se muestra una tarea que tiene
las opciones LongRunning y PreferFairness.
IMPORTANT
Tenga en cuenta que la referencia cultural del subproceso que realiza la llamada como parte del contexto de una tarea se
aplica a las aplicaciones destinadas a .NET Framework 4.6, no a las que se ejecutan en .NET Framework 4.6. Puede elegir
como destino una versión determinada de .NET Framework al crear el proyecto en Visual Studio seleccionado esta versión
en la lista desplegable situada en la parte superior del cuadro de diálogo Nuevo proyecto o fuera de Visual Studio puede
usar el atributo TargetFrameworkAttribute. Para las aplicaciones destinadas a versiones de .NET Framework anteriores a .NET
Framework 4.6 o que no están destinadas a una versión específica de .NET Framework, la referencia cultural del subproceso
en el que se ejecuta sigue determinando la referencia cultural de la tarea.
A partir de las aplicaciones destinadas a .NET Framework 4.6, cada tarea hereda la referencia cultural del
subproceso que realiza la llamada, aunque la tarea se ejecute de forma asincrónica en un subproceso del grupo
de subprocesos.
Esto se muestra en el ejemplo siguiente. Usa el atributo TargetFrameworkAttribute para elegir .NET
Framework 4.6 como destino y cambia la referencia cultural actual de la aplicación a francés (Francia) o, si francés
(Francia) es la referencia cultural actual, a inglés (Estados Unidos). Después, invoca un delegado llamado
formatDelegate que devuelve algunos números con el formato de valores de moneda de la nueva referencia
cultural. Tenga en cuenta si el delegado se ejecuta como una tarea tanto de forma sincrónica como asincrónica,
devuelve el resultado esperado porque la tarea asincrónica hereda la referencia cultural del subproceso que
realiza la llamada.
using System;
using System.Globalization;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
[assembly:TargetFramework(".NETFramework,Version=v4.6")]
Thread.CurrentThread.ManagedThreadId);
foreach (var value in values)
output += String.Format("{0} ",
value.ToString(formatString));
output += Environment.NewLine;
return output;
};
// Call an async delegate to format the values using one format string.
Console.WriteLine("Executing a task asynchronously:");
var t1 = Task.Run(formatDelegate);
Console.WriteLine(t1.Result);
Imports System.Globalization
Imports System.Runtime.Versioning
Imports System.Threading
Imports System.Threading.Tasks
<Assembly: TargetFramework(".NETFramework,Version=v4.6")>
Module Example
Public Sub Main()
Dim values() As Decimal = {163025412.32d, 18905365.59d}
Dim formatString As String = "C2"
Dim formatDelegate As Func(Of String) = Function()
Dim output As String = String.Format("Formatting using
the {0} culture on thread {1}.",
CultureInfo.CurrentCulture.Name,
Thread.CurrentThread.ManagedThreadId)
output += Environment.NewLine
For Each value In values
output += String.Format("{0} ",
value.ToString(formatString))
Next
output += Environment.NewLine
Return output
End Function
' Call an async delegate to format the values using one format string.
Console.WriteLine("Executing a task asynchronously:")
Dim t1 = Task.Run(formatDelegate)
Console.WriteLine(t1.Result)
Si utiliza Visual Studio, puede omitir el atributo TargetFrameworkAttribute y seleccionar en su lugar .NET
Framework 4.6 como destino al crear el proyecto en el cuadro de diálogo Nuevo proyecto .
Para que la salida refleje el comportamiento de las aplicaciones destinadas a versiones de .NET Framework
anteriores a .NET Framework 4.6, quite el atributo TargetFrameworkAttribute del código fuente. La salida reflejará
las convenciones de formato de la referencia cultural predeterminada del sistema, no la referencia cultural del
subproceso que realiza la llamada.
Para obtener más información sobre las tareas asincrónicas y la referencia cultural, consulte la sección "Referencia
cultural y operaciones asincrónicas basadas en tareas" en el tema CultureInfo.
return values;
} );
var processData = getData.ContinueWith((x) => {
int n = x.Result.Length;
long sum = 0;
double mean;
Module Example
Public Sub Main()
Dim getData = Task.Factory.StartNew(Function()
Dim rnd As New Random()
Dim values(99) As Integer
For ctr = 0 To values.GetUpperBound(0)
values(ctr) = rnd.Next()
Next
Return values
End Function)
Dim processData = getData.ContinueWith(Function(x)
Dim n As Integer = x.Result.Length
Dim sum As Long
Dim mean As Double
Dado que Task.ContinueWith es un método de instancia, puede encadenar llamadas a métodos en lugar de crear
instancias de un objeto Task<TResult> para cada tarea antecedente. El ejemplo siguiente es funcionalmente
idéntico al anterior, con la diferencia de que encadena llamadas al método Task.ContinueWith. Observe que el
objeto Task<TResult> devuelto por la cadena de llamadas al método es la tarea final de continuación.
using System;
using System.Threading.Tasks;
return values;
} ).
ContinueWith((x) => {
int n = x.Result.Length;
long sum = 0;
double mean;
Module Example
Public Sub Main()
Dim displayData = Task.Factory.StartNew(Function()
Dim rnd As New Random()
Dim values(99) As Integer
For ctr = 0 To values.GetUpperBound(0)
values(ctr) = rnd.Next()
Next
Return values
End Function). _
ContinueWith(Function(x)
Dim n As Integer = x.Result.Length
Dim sum As Long
Dim mean As Double
outer.Wait();
Console.WriteLine("Outer task completed.");
// The example displays the following output:
// Outer task beginning.
// Outer task completed.
// Detached task completed.
Dim outer = Task.Factory.StartNew(Sub()
Console.WriteLine("Outer task beginning.")
Dim child = Task.Factory.StartNew(Sub()
Thread.SpinWait(5000000)
Console.WriteLine("Detached task
completed.")
End Sub)
End Sub)
outer.Wait()
Console.WriteLine("Outer task completed.")
' The example displays the following output:
' Outer task beginning.
' Outer task completed.
' Detached child completed.
Observe que la tarea primaria no espera a que la tarea secundaria desasociada se complete.
parent.Wait();
Console.WriteLine("Parent task completed.");
}
}
// The example displays output like the following:
// Parent task beginning.
// Attached child #9 completed.
// Attached child #0 completed.
// Attached child #8 completed.
// Attached child #1 completed.
// Attached child #7 completed.
// Attached child #2 completed.
// Attached child #6 completed.
// Attached child #3 completed.
// Attached child #5 completed.
// Attached child #4 completed.
// Parent task completed.
Imports System.Threading
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim parent = Task.Factory.StartNew(Sub()
Console.WriteLine("Parent task beginning.")
For ctr As Integer = 0 To 9
Dim taskNo As Integer = ctr
Task.Factory.StartNew(Sub(x)
Thread.SpinWait(5000000)
Console.WriteLine("Attached
child #{0} completed.",
x)
End Sub,
taskNo,
TaskCreationOptions.AttachedToParent)
Next
End Sub)
parent.Wait()
Console.WriteLine("Parent task completed.")
End Sub
End Module
' The example displays output like the following:
' Parent task beginning.
' Attached child #9 completed.
' Attached child #0 completed.
' Attached child #8 completed.
' Attached child #1 completed.
' Attached child #7 completed.
' Attached child #2 completed.
' Attached child #6 completed.
' Attached child #3 completed.
' Attached child #5 completed.
' Attached child #4 completed.
' Parent task completed.
Una tarea principal puede usar la opción TaskCreationOptions.DenyChildAttach para evitar que otras tareas se
asocien a la tarea primaria. Para más información, consulte Tareas secundarias asociadas y desasociadas.
Dim tasks() =
{
Task.Factory.StartNew(Sub() MethodA()),
Task.Factory.StartNew(Sub() MethodB()),
Task.Factory.StartNew(Sub() MethodC())
}
Componer tareas
Las clases Task y Task<TResult> proporcionan varios métodos que pueden ayudar a componer varias tareas para
implementar patrones comunes y para mejorar el uso de las características de lenguaje asincrónicas
proporcionadas por C#, Visual Basic y F#. En esta sección se describen los métodos WhenAll, WhenAny, Delay y
FromResult.
Task.WhenAll
El método Task.WhenAll espera de forma asincrónica a que finalicen varios Task objetos Task<TResult>.
Proporciona versiones sobrecargadas que permiten esperar a conjuntos no uniformes de tareas. Por ejemplo,
puede esperar a que varios objetos Task y Task<TResult> se completen de una llamada al método.
Task.WhenAny
El método Task.WhenAny espera de forma asincrónica a que finalice uno de varios Task objetos Task<TResult>.
Como en el método Task.WhenAll, este método proporciona versiones sobrecargadas que permiten esperar a
conjuntos no uniformes de tareas. El método WhenAny es especialmente útil en los siguientes escenarios.
Operaciones redundantes. Considere un algoritmo o una operación que pueda realizarse de muchas
maneras. Puede usar el método WhenAny para seleccionar la operación que finaliza primero y luego
cancelar las operaciones restantes.
Operaciones intercaladas. Puede iniciar varias operaciones, todas las cuales deben finalizar y utilizar el
método WhenAny para procesar los resultados cuando finalice cada operación. Finalizada una operación,
puede iniciar una o más tareas adicionales.
Operaciones limitadas. Puede usar el método WhenAny para extender el escenario anterior limitando el
número de operaciones simultáneas.
Operaciones que han expirado. Puede usar el método WhenAny para seleccionar entre una o más tareas y
una tarea que termina después de un tiempo concreto, como una tarea devuelta por el método Delay. El
método Delay se describe en la sección siguiente.
Task.Delay
El método Task.Delay produce un objeto Task que finaliza tras el tiempo especificado. Puede usar este método
para crear bucles que sondeen ocasionalmente en busca de datos, introduzcan finales de tiempo de espera,
retrasen el control de los datos proporcionados por el usuario durante un tiempo predeterminado, etc.
Task(T ).FromResult
Mediante el método Task.FromResult, puede crear un objeto Task<TResult> que contenga un resultado
previamente calculado. Este método es útil cuando se realiza una operación asincrónica que devuelve un objeto
Task<TResult> y el resultado de ese objeto Task<TResult> ya se ha calculado. Para obtener un ejemplo en el que
se usa FromResult para recuperar los resultados de las operaciones de descarga asincrónica que se retienen en
caché, vea Procedimiento para crear tareas precalculadas.
Cancelar tareas
La clase Task admite la cancelación cooperativa y está completamente integrada con las clases
System.Threading.CancellationTokenSource y System.Threading.CancellationToken, que se presentaron en .NET
Framework versión 4. Muchos de los constructores de la clase System.Threading.Tasks.Task toman un objeto
CancellationToken como parámetro de entrada. Muchas de las sobrecargas de StartNew y Run incluyen también
un parámetro CancellationToken.
Puede crear el token y emitir la solicitud de cancelación posteriormente usando la clase CancellationTokenSource.
A continuación, debe pasar el token a Task como argumento y hacer referencia al mismo token también en el
delegado de usuario, que se encarga de responder a una solicitud de cancelación.
Para más información, vea Cancelación de tareas y Cómo: Cancelar una tarea y sus elementos secundarios.
Clase TaskFactory
La clase TaskFactory proporciona métodos estáticos que encapsulan algunos modelos comunes de creación e
inicio de tareas y tareas de continuación.
El modelo más común es StartNew, que crea e inicia una tarea en una sola instrucción.
Cuando cree tareas de continuación a partir de varios antecedentes, use el método ContinueWhenAll o el
métodoContinueWhenAny o sus equivalentes en la clase Task<TResult>. Para más información, consulte
Encadenar tareas mediante tareas de continuación.
Para encapsular los métodos BeginX y EndX del modelo de programación asincrónica en una instancia de
Task o Task<TResult>, use los métodos FromAsync. Para más información, consulte TPL y la programación
asincrónica tradicional de .NET Framework.
Al objeto TaskFactory predeterminado se puede tener acceso como propiedad estática de la clase Task o de la
clase Task<TResult>. También pueden crearse directamente instancias de TaskFactory y especificar varias opciones
entre las que se incluyan las opciones CancellationToken, TaskCreationOptions, TaskContinuationOptions o
TaskScheduler. Cualquier opción que se especifique al crear el generador de tareas se aplicará a todas las tareas
que este generador cree, a menos que Task se cree usando la enumeración TaskCreationOptions, en cuyo caso las
opciones de la tarea reemplazarán a las del generador de tareas.
Programadores personalizados
La mayoría de los desarrolladores de aplicaciones o bibliotecas no prestan atención al procesador en el que se
ejecuta la tarea, al modo en que la tarea sincroniza su trabajo con otras tareas o al modo en que se programa la
tarea en el objeto System.Threading.ThreadPool. Solo necesitan que la ejecución en el equipo host sea lo más
eficaz posible. Si necesita tener un control más minucioso sobre los detalles de programación, la biblioteca TPL
permite configurar algunos valores del programador de tareas predeterminado e incluso permite proporcionar
un programador personalizado. Para obtener más información, vea TaskScheduler.
Temas relacionados
T IT L E DESC RIP C IÓ N
Attached and Detached Child Tasks (Tareas secundarias Describe la diferencia entre las tareas secundarias asociadas y
asociadas y desasociadas) desasociadas.
Cómo: Usar Parallel.Invoke para ejecutar operaciones en Describe cómo usar Invoke.
paralelo
Cómo: Devolver un valor a partir de una tarea Describe cómo devolver valores de tareas.
Cómo: Cancelar una tarea y sus elementos secundarios Describe cómo cancelar tareas.
Cómo: Crear tareas precalculadas Describe cómo utilizar el método Task.FromResult para
recuperar los resultados de las operaciones asincrónicas de
descarga que se retienen en una memoria caché.
Cómo: Recorrer un árbol binario con tareas en paralelo Describe cómo utilizar tareas para atravesar un árbol binario.
Cómo: Desencapsular una tarea anidada Demuestra cómo utilizar el método de extensión Unwrap.
Paralelismo de datos Describe cómo usar For y ForEach para crear bucles paralelos
sobre los datos.
Vea también
Programación en paralelo
Ejemplos de programación en paralelo con .NET Core y .NET Standard
Encadenamiento de tareas mediante tareas de
continuación
16/09/2020 • 39 minutes to read • Edit Online
En la programación asincrónica, es habitual que una operación asincrónica, al finalizar, invoque una segunda
operación. Las continuaciones permiten a las operaciones descendentes usar los resultados de la primera
operación. Tradicionalmente, las continuaciones se han realizado mediante métodos de devolución de llamada. En
la biblioteca TPL (Task Parallel Library, biblioteca de procesamiento paralelo basado en tareas), se proporciona la
misma funcionalidad mediante tareas de continuación. Una tarea de continuación (también conocida
simplemente como una continuación) es una tarea asincrónica invocada por otra tarea, conocida como
antecedente, cuando esta finaliza.
A pesar de que las continuaciones son relativamente fáciles de usar, resultan eficaces y flexibles. Por ejemplo, se
puede:
Pasar datos del antecedente a la continuación.
Especificar las condiciones precisas en las que se invoca o no se invoca la continuación.
Cancelar una continuación antes de que se inicie o de forma cooperativa mientras se ejecuta.
Proporcionar sugerencias sobre cómo debería programarse la continuación.
Invocar varias continuaciones desde el mismo antecedente.
Invocar una continuación cuando todos o alguno de los antecedentes finalicen.
Encadenar las continuaciones una tras otra hasta cualquier longitud arbitraria.
Usar una continuación para controlar las excepciones producidas por el antecedente.
NOTE
En los ejemplos de C# de este artículo se usa el modificador async en el método Main . Esa característica está disponible
en C# 7.1 y versiones posteriores. En las versiones anteriores se genera CS5001 al compilar este código de ejemplo. Tendrá
que establecer la versión del lenguaje en C# 7.1 o posterior. Puede obtener información sobre cómo configurar la versión
del lenguaje en el artículo sobre configuración de la versión del lenguaje.
using System;
using System.Threading.Tasks;
Imports System.Threading.Tasks
Module Example
Public Sub Main()
' Execute the antecedent.
Dim taskA As Task(Of DayOfWeek) = Task.Run(Function() DateTime.Today.DayOfWeek)
int sum = 0;
for (int ctr = 0; ctr <= results.Length - 1; ctr++)
{
var result = results[ctr];
Console.Write($"{result} {((ctr == results.Length - 1) ? "=" : "+")} ");
sum += result;
}
Console.WriteLine(sum);
}
}
// The example displays the similar output:
// 1 + 4 + 9 + 16 + 25 + 36 + 49 + 64 + 81 + 100 = 385
Imports System.Collections.Generic
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim tasks As New List(Of Task(Of Integer))()
For ctr As Integer = 1 To 10
Dim baseValue As Integer = ctr
tasks.Add(Task.Factory.StartNew(Function(b)
Dim i As Integer = CInt(b)
Return i * i
End Function, baseValue))
Next
Dim continuation = Task.WhenAll(tasks)
Opciones de continuación
Cuando se crea una continuación de tarea única, se puede usar una sobrecarga ContinueWith que toma un valor
de enumeración System.Threading.Tasks.TaskContinuationOptions para especificar las condiciones en las que se
inicia la continuación. Por ejemplo, se puede especificar que la continuación únicamente se ejecute si el
antecedente se completa correctamente, o solo si se completa en un estado de error. Si la condición no es true
cuando el antecedente está listo para invocar la continuación, la continuación realiza la transición directamente al
estado TaskStatus.Canceled y, por tanto, no se puede iniciar.
Hay distintos métodos de continuación de varias tareas, como las sobrecargas del método
TaskFactory.ContinueWhenAll , que también incluyen un parámetro
System.Threading.Tasks.TaskContinuationOptions . Sin embargo, solo es válido un subconjunto de todos los
miembros de enumeración de System.Threading.Tasks.TaskContinuationOptions . Se pueden especificar valores
System.Threading.Tasks.TaskContinuationOptions que tengan equivalentes en la enumeración
System.Threading.Tasks.TaskCreationOptions , como TaskContinuationOptions.AttachedToParent,
TaskContinuationOptions.LongRunningy TaskContinuationOptions.PreferFairness. Si se especifica cualquiera de
las opciones NotOn o OnlyOn con una continuación de varias tareas, se producirá una excepción
ArgumentOutOfRangeException en tiempo de ejecución.
Para obtener más información sobre las opciones de continuación de tarea, consulte el tema
TaskContinuationOptions .
using System;
using System.Threading.Tasks;
await task.ContinueWith(
antecedent =>
{
Console.WriteLine($"Good {antecedent.Result}!");
Console.WriteLine($"And how are you this fine {antecedent.Result}?");
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
}
// The example displays the similar output:
// Good afternoon!
// And how are you this fine afternoon?
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim t = Task.Run(Function()
Dim dat As DateTime = DateTime.Now
If dat = DateTime.MinValue Then
Throw New ArgumentException("The clock is not working.")
End If
Si desea que la continuación se ejecute aunque el antecedente no finalice correctamente, debe protegerse de la
excepción. Un modo de hacerlo es probar la propiedad Task.Status del antecedente y solo intentar el acceso a la
propiedad Result si el estado no es Faulted ni Canceled. También puede examinar la propiedad Exception del
antecedente. Para más información, consulte Control de excepciones. En el ejemplo siguiente se modifica el
ejemplo anterior para tener acceso a la propiedad Task<TResult>.Result del antecedente, pero solo si su estado es
TaskStatus.RanToCompletion.
using System;
using System.Threading.Tasks;
Module Example
Public Sub Main()
Dim t = Task.Run(Function()
Dim dat As DateTime = DateTime.Now
If dat = DateTime.MinValue Then
Throw New ArgumentException("The clock is not working.")
End If
return product33.ToArray();
}, token);
try
{
await task;
double result = await continuation;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim rnd As New Random()
Dim lockObj As New Object()
Dim cts As New CancellationTokenSource()
Dim token As CancellationToken = cts.Token
Dim timer As New Timer(AddressOf Elapsed, cts, 5000, Timeout.Infinite)
Dim t = Task.Run(Function()
Dim product33 As New List(Of Integer)()
For ctr As Integer = 1 To Int16.MaxValue
' Check for cancellation.
If token.IsCancellationRequested Then
Console.WriteLine("\nCancellation requested in antecedent...\n")
token.ThrowIfCancellationRequested()
End If
' Introduce a delay.
If ctr Mod 2000 = 0 Then
Dim delay As Integer
SyncLock lockObj
delay = rnd.Next(16, 501)
End SyncLock
Thread.Sleep(delay)
End If
Try
continuation.Wait()
Catch e As AggregateException
For Each ie In e.InnerExceptions
Console.WriteLine("{0}: {1}", ie.GetType().Name,
ie.Message)
Next
Finally
cts.Dispose()
End Try
cts.Cancel()
Console.WriteLine("{0}Cancellation request issued...{0}", vbCrLf)
End Sub
End Module
' The example displays output like the following:
' The example displays output like the following:
' Multiples of 33:
'
' 33, 66, 99, 132, 165, 198, 231, 264, 297, 330, 363, 396, 429, 462, 495, 528,
' 561, 594, 627, 660, 693, 726, 759, 792, 825, 858, 891, 924, 957, 990, 1,023,
' 1,056, 1,089, 1,122, 1,155, 1,188, 1,221, 1,254, 1,287, 1,320, 1,353, 1,386,
' 1,419, 1,452, 1,485, 1,518, 1,551, 1,584, 1,617, 1,650, 1,683, 1,716, 1,749,
' 1,782, 1,815, 1,848, 1,881, 1,914, 1,947, 1,980, 2,013, 2,046, 2,079, 2,112,
' 2,145, 2,178, 2,211, 2,244, 2,277, 2,310, 2,343, 2,376, 2,409, 2,442, 2,475,
' 2,508, 2,541, 2,574, 2,607, 2,640, 2,673, 2,706, 2,739, 2,772, 2,805, 2,838,
' 2,871, 2,904, 2,937, 2,970, 3,003, 3,036, 3,069, 3,102, 3,135, 3,168, 3,201,
' 3,234, 3,267, 3,300, 3,333, 3,366, 3,399, 3,432, 3,465, 3,498, 3,531, 3,564,
' 3,597, 3,630, 3,663, 3,696, 3,729, 3,762, 3,795, 3,828, 3,861, 3,894, 3,927,
' 3,960, 3,993, 4,026, 4,059, 4,092, 4,125, 4,158, 4,191, 4,224, 4,257, 4,290,
' 4,323, 4,356, 4,389, 4,422, 4,455, 4,488, 4,521, 4,554, 4,587, 4,620, 4,653,
' 4,686, 4,719, 4,752, 4,785, 4,818, 4,851, 4,884, 4,917, 4,950, 4,983, 5,016,
' 5,049, 5,082, 5,115, 5,148, 5,181, 5,214, 5,247, 5,280, 5,313, 5,346, 5,379,
' 5,412, 5,445, 5,478, 5,511, 5,544, 5,577, 5,610, 5,643, 5,676, 5,709, 5,742,
' 5,775, 5,808, 5,841, 5,874, 5,907, 5,940, 5,973, 6,006, 6,039, 6,072, 6,105,
' 6,138, 6,171, 6,204, 6,237, 6,270, 6,303, 6,336, 6,369, 6,402, 6,435, 6,468,
' 6,501, 6,534, 6,567, 6,600, 6,633, 6,666, 6,699, 6,732, 6,765, 6,798, 6,831,
' 6,864, 6,897, 6,930, 6,963, 6,996, 7,029, 7,062, 7,095, 7,128, 7,161, 7,194,
' 7,227, 7,260, 7,293, 7,326, 7,359, 7,392, 7,425, 7,458, 7,491, 7,524, 7,557,
' 7,590, 7,623, 7,656, 7,689, 7,722, 7,755, 7,788, 7,821, 7,854, 7,887, 7,920,
' 7,953, 7,986, 8,019, 8,052, 8,085, 8,118, 8,151, 8,184, 8,217, 8,250, 8,283,
' 8,316, 8,349, 8,382, 8,415, 8,448, 8,481, 8,514, 8,547, 8,580, 8,613, 8,646,
' 8,679, 8,712, 8,745, 8,778, 8,811, 8,844, 8,877, 8,910, 8,943, 8,976, 9,009,
' 9,042, 9,075, 9,108, 9,141, 9,174, 9,207, 9,240, 9,273, 9,306, 9,339, 9,372,
' 9,405, 9,438, 9,471, 9,504, 9,537, 9,570, 9,603, 9,636, 9,669, 9,702, 9,735,
' 9,768, 9,801, 9,834, 9,867, 9,900, 9,933, 9,966, 9,999, 10,032, 10,065, 10,098,
' 10,131, 10,164, 10,197, 10,230, 10,263, 10,296, 10,329, 10,362, 10,395, 10,428,
' 10,461, 10,494, 10,527, 10,560, 10,593, 10,626, 10,659, 10,692, 10,725, 10,758,
' 10,791, 10,824, 10,857, 10,890, 10,923, 10,956, 10,989, 11,022, 11,055, 11,088,
' 11,121, 11,154, 11,187, 11,220, 11,253, 11,286, 11,319, 11,352, 11,385, 11,418,
' 11,451, 11,484, 11,517, 11,550, 11,583, 11,616, 11,649, 11,682, 11,715, 11,748,
' 11,781, 11,814, 11,847, 11,880, 11,913, 11,946, 11,979, 12,012, 12,045, 12,078,
' 12,111, 12,144, 12,177, 12,210, 12,243, 12,276, 12,309, 12,342, 12,375, 12,408,
' 12,441, 12,474, 12,507, 12,540, 12,573, 12,606, 12,639, 12,672, 12,705, 12,738,
' 12,771, 12,804, 12,837, 12,870, 12,903, 12,936, 12,969, 13,002, 13,035, 13,068,
' 13,101, 13,134, 13,167, 13,200, 13,233, 13,266,
' Cancellation requested in continuation...
'
'
' Cancellation request issued...
'
' TaskCanceledException: A task was canceled.
'
' Antecedent Status: RanToCompletion
' Continuation Status: Canceled
También se puede evitar que una continuación se ejecute si su antecedente se cancela sin proporcionar a la
continuación un token de cancelación. Para ello, debe especificarse la opción
TaskContinuationOptions.NotOnCanceled al crear la continuación. Este es un ejemplo sencillo.
using System;
using System.Threading;
using System.Threading.Tasks;
try
{
await task;
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
Console.WriteLine();
}
Module Example
Public Sub Main()
Dim cts As New CancellationTokenSource()
Dim token As CancellationToken = cts.Token
cts.Cancel()
Cuando una continuación entra en el estado Canceled puede afectar a las continuaciones posteriores, en función
de los valores de TaskContinuationOptions que se especificaron para esas continuaciones.
Las continuaciones eliminadas no se iniciarán.
No obstante, si las tareas secundarias se desasocian del antecedente, la continuación se ejecuta en cuanto finaliza
el antecedente y con independencia del estado de las tareas secundarias. Como resultado, varias ejecuciones del
ejemplo siguiente pueden tener una salida distinta que depende de cómo el programador de tareas controla cada
tarea secundaria.
using System;
using System.Threading.Tasks;
Task continuation =
task.ContinueWith(
antecedent =>
Console.WriteLine($"Executing continuation of Task {antecedent.Id}"));
await continuation;
Console.ReadLine();
}
}
// The example displays the similar output:
// Running antecedent task 1...
// Launching attached child tasks...
// Finished launching detached child tasks...
// Attached child task #2 running
// Attached child task #3 running
// Attached child task #1 running
// Attached child task #4 running
// Attached child task #5 running
// Executing continuation of Task 1
Imports System.Threading
Imports System.Threading.Tasks
El estado final de la tarea antecedente depende del estado final de las tareas secundarias asociadas. El estado de
las tareas secundarias desasociadas no afecta al elemento primario. Para más información, consulte Tareas
secundarias asociadas y desasociadas.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
class ContinuationStateExample
{
static DateTime DoWork()
{
Thread.Sleep(2000);
return DateTime.Now;
}
await task;
Console.ReadLine();
}
}
// The example displays the similar output:
// Task was created at 10:56:21.1561762 and finished at 10:56:25.1672062.
// Task was created at 10:56:21.1610677 and finished at 10:56:27.1707646.
// Task was created at 10:56:21.1610677 and finished at 10:56:29.1743230.
// Task was created at 10:56:21.1610677 and finished at 10:56:31.1779883.
// Task was created at 10:56:21.1610677 and finished at 10:56:33.1837083.
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
' Display the creation time of each continuation (the state object)
' and the completion time (the result of that task) to the console.
For Each continuation In continuations
Dim start As DateTime = CDate(continuation.AsyncState)
Dim [end] As DateTime = continuation.Result
En el ejemplo siguiente se muestra cómo usar continuaciones que encapsulan funciones adicionales que
devuelven tareas. Cada continuación se puede desencapsular, de modo que se expone la tarea interna que se
encapsuló.
using System;
using System.Threading;
using System.Threading.Tasks;
Console.WriteLine("Started RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)");
try
{
await taskOne;
Console.WriteLine("Finished RemoteIncrement(0)");
await taskTwo;
Console.WriteLine("Finished RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)");
}
catch (Exception e)
{
Console.WriteLine($"A task has thrown the following (unexpected) exception:\n{e}");
}
}
int x = (int)obj;
Console.WriteLine("Thread={0}, Next={1}", Thread.CurrentThread.ManagedThreadId, ++x);
return x;
},
number);
}
Module UnwrapExample
Sub Main()
Dim taskOne As Task(Of Integer) = RemoteIncrement(0)
Console.WriteLine("Started RemoteIncrement(0)")
Console.WriteLine("Started RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)")
Try
taskOne.Wait()
Console.WriteLine("Finished RemoteIncrement(0)")
taskTwo.Wait()
Console.WriteLine("Finished RemoteIncrement(...(RemoteIncrement(RemoteIncrement(4))...)")
Catch e As AggregateException
Console.WriteLine($"A task has thrown the following (unexpected) exception:{vbLf}{e}")
End Try
End Sub
Para más información sobre el uso de Unwrap, consulte Cómo: Desencapsular una tarea anidada.
try
{
await task;
await continuation;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
// The example displays the similar output:
// Executing task 1
// Executing continuation task 2
// Value from antecedent: 54
// Operation is not valid due to the current state of the object.
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim task1 = Task(Of Integer).Run(Function()
Console.WriteLine("Executing task {0}",
Task.CurrentId)
Return 54
End Function)
Dim continuation = task1.ContinueWith(Sub(antecedent)
Console.WriteLine("Executing continuation task {0}",
Task.CurrentId)
Console.WriteLine("Value from antecedent: {0}",
antecedent.Result)
Throw New InvalidOperationException()
End Sub)
Try
task1.Wait()
continuation.Wait()
Catch ae As AggregateException
For Each ex In ae.InnerExceptions
Console.WriteLine(ex.Message)
Next
End Try
End Sub
End Module
' The example displays the following output:
' Executing task 1
' Executing continuation task 2
' Value from antecedent: 54
' Operation is not valid due to the current state of the object.
Puede usar una segunda continuación para observar la propiedad Exception de la primera continuación. En el
siguiente ejemplo, una tarea intenta leer en un archivo inexistente. La continuación muestra entonces
información sobre la excepción en la tarea antecedente.
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
if (fileNotFound != null)
{
Console.WriteLine(fileNotFound.Message);
}
}, TaskContinuationOptions.OnlyOnFaulted);
await continuation;
Console.ReadLine();
}
}
// The example displays the following output:
// Could not find file 'C:\NonexistentFile.txt'.
Imports System.IO
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim t = Task.Run(Function()
Dim s As String = File.ReadAllText("C:\NonexistentFile.txt")
Return s
End Function)
Dim c = t.ContinueWith(Sub(antecedent)
' Get the antecedent's exception information.
For Each ex In antecedent.Exception.InnerExceptions
If TypeOf ex Is FileNotFoundException
Console.WriteLine(ex.Message)
End If
Next
End Sub, TaskContinuationOptions.OnlyOnFaulted)
c.Wait()
End Sub
End Module
' The example displays the following output:
' Could not find file 'C:\NonexistentFile.txt'.
var fileNotFound =
antecedent.Exception
?.InnerExceptions
?.FirstOrDefault(e => e is FileNotFoundException) as FileNotFoundException;
if (fileNotFound != null)
{
Console.WriteLine(fileNotFound.Message);
}
Vea también
Biblioteca TPL
Tareas secundarias asociadas y desasociadas
16/09/2020 • 15 minutes to read • Edit Online
Una tarea secundaria o tarea anidada es una instancia de System.Threading.Tasks.Task que se crea en el delegado
de usuario de otra tarea, conocida como tarea primaria. Una tarea secundaria puede estar desasociada o
asociada. Una tarea secundaria desasociada es una tarea que se ejecuta independientemente de su elemento
principal. Una tarea secundaria asociada es una tarea anidada que se crea con la opción
TaskCreationOptions.AttachedToParent y cuyo elemento primario no le prohíbe asociarse de forma explícita o
predeterminada. Una tarea puede crear cualquier número de tareas secundarias asociadas y desasociadas, con la
única limitación de los recursos del sistema.
En la tabla siguiente se muestran las diferencias básicas entre los dos tipos de tareas secundarias.
En la mayoría de los casos, se recomienda usar tareas secundarias desasociadas porque las relaciones con otras
tareas son menos complejas. Esta es la razón por la que las tareas que se crean dentro de tareas primarias están
desasociadas de forma predeterminada y es necesario especificar explícitamente la opción
TaskCreationOptions.AttachedToParent para crear una tarea secundaria asociada.
parent.Wait();
Console.WriteLine("Outer has completed.");
}
}
// The example produces output like the following:
// Outer task executing.
// Nested task starting.
// Outer has completed.
// Nested task completing.
Imports System.Threading
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim parent = Task.Factory.StartNew(Sub()
Console.WriteLine("Outer task executing.")
Dim child = Task.Factory.StartNew(Sub()
Si la tarea secundaria se representa mediante un objeto Task<TResult> en lugar de un objeto Task, puede
garantizarse que la tarea primaria esperará a que la tarea secundaria se complete teniendo acceso a la propiedad
Task<TResult>.Result de la tarea secundaria, aunque sea una tarea secundaria desasociada. La propiedad Result
se bloquea hasta que se completa su tarea, como se muestra en el ejemplo siguiente.
using System;
using System.Threading;
using System.Threading.Tasks;
class Example
{
static void Main()
{
var outer = Task<int>.Factory.StartNew(() => {
Console.WriteLine("Outer task executing.");
Imports System.Threading
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim parent = Task(Of Integer).Factory.StartNew(Function()
Console.WriteLine("Outer task executing.")
Dim child = Task(Of
Integer).Factory.StartNew(Function()
Thread.SpinWait(5000000)
Return 42
End
Function)
Return child.Result
End Function)
Console.WriteLine("Outer has returned {0}", parent.Result)
End Sub
End Module
' The example displays the following output:
' Outer task executing.
' Nested task starting.
' Detached task completing.
' Outer has returned 42
Tareas secundarias asociadas
A diferencia de las tareas secundarias desasociadas, las tareas secundarias asociadas se sincronizan
estrechamente con la tarea primaria. En el ejemplo anterior, se puede cambiar la tarea secundaria desasociada a
una tarea secundaria asociada mediante la opción TaskCreationOptions.AttachedToParent en la instrucción de
creación de tareas, como se muestra en el ejemplo siguiente. En este código, la tarea secundaria asociada se
completa antes que su tarea primaria. Como resultado, el resultado del ejemplo es igual cada vez que se ejecuta
el código.
using System;
using System.Threading;
using System.Threading.Tasks;
Imports System.Threading
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim parent = Task.Factory.StartNew(Sub()
Console.WriteLine("Parent task executing")
Dim child = Task.Factory.StartNew(Sub()
Thread.SpinWait(5000000)
Puede usar tareas secundarias asociadas para crear gráficos con una estrecha sincronización de operaciones
asincrónicas.
Sin embargo, una tarea secundaria se puede asociar a su elemento primario solo si su elemento primario no
prohíbe las tareas secundarias asociadas. Las tareas primarias pueden evitar explícitamente que las tareas
secundarias se asocien a ellas especificando la opción TaskCreationOptions.DenyChildAttach del constructor de
clase de la tarea primaria o el método TaskFactory.StartNew. Las tareas primarias impiden implícitamente que las
tareas secundarias se asocien a ellas si se crean mediante una llamada al método Task.Run. Esto se ilustra en el
siguiente ejemplo: Es idéntico al ejemplo anterior, excepto en que la tarea primaria se crea mediante una llamada
al método Task.Run(Action) en lugar de al método TaskFactory.StartNew(Action). Como la tarea secundaria no se
puede asociar a su elemento primario, el resultado del ejemplo es impredecible. Como las opciones de creación
de tareas predeterminadas para las sobrecargas de Task.Run incluyen TaskCreationOptions.DenyChildAttach,
este ejemplo es funcionalmente equivalente al primer ejemplo de la sección "Tareas secundarias desasociadas".
using System;
using System.Threading;
using System.Threading.Tasks;
Imports System.Threading
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim parent = Task.Run(Sub()
Console.WriteLine("Parent task executing.")
Dim child = Task.Factory.StartNew(Sub()
Console.WriteLine("Attached child
starting.")
Thread.SpinWait(5000000)
Console.WriteLine("Attached child
completing.")
End Sub,
TaskCreationOptions.AttachedToParent)
End Sub)
parent.Wait()
Console.WriteLine("Parent has completed.")
End Sub
End Module
' The example displays output like the following:
' Parent task executing.
' Parent has completed.
' Attached child starting.
Excepciones en tareas secundarias
Si una tarea secundaria desasociadas inicia una excepción, esa excepción debe observarse o controlarse
directamente en la tarea primaria como si se tratara de una tarea no anidada. Si una tarea secundaria asociada
inicia una excepción, la excepción se propaga automáticamente a la tarea primaria y de nuevo al subproceso, que
espera o intenta obtener acceso a la propiedad Task<TResult>.Result de la tarea. Por tanto, si se usan tareas
secundarias asociadas, se pueden controlar todas las excepciones en un solo punto en la llamada a Task.Wait del
subproceso que realiza la llamada. Para más información, consulte Control de excepciones.
Vea también
Programación en paralelo
Data Parallelism (Paralelismo de datos)
Cancelación de tareas
16/09/2020 • 5 minutes to read • Edit Online
class Program
{
static async Task Main()
{
var tokenSource2 = new CancellationTokenSource();
CancellationToken ct = tokenSource2.Token;
tokenSource2.Cancel();
Console.ReadKey();
}
}
Imports System.Threading
Imports System.Threading.Tasks
Module Test
Sub Main()
Dim tokenSource2 As New CancellationTokenSource()
Dim ct As CancellationToken = tokenSource2.Token
Dim t2 = Task.Factory.StartNew(Sub()
' Were we already canceled?
ct.ThrowIfCancellationRequested()
End While
End Sub _
, tokenSource2.Token) ' Pass same token to StartNew.
Catch e As AggregateException
Console.ReadKey()
End Sub
End Module
Para obtener un ejemplo más completo, vea Cómo: Cancelar una tarea y sus elementos secundarios.
Cuando una instancia de tarea observa una excepción OperationCanceledException iniciada desde el código de
usuario, compara el token de la excepción con su token asociado (el que se pasó a la API que creó la tarea). Si
son iguales y la propiedad IsCancellationRequested del token devuelve true, la tarea lo interpreta como una
confirmación de cancelación y pasa al estado Canceled. Si no se usa un método Wait o WaitAll para esperar a la
tarea, esta simplemente establece su estado en Canceled.
Si espera en un elemento Task que cambia al estado “Canceled”, se crea y se inicia una excepción
System.Threading.Tasks.TaskCanceledException (encapsulada en la excepción AggregateException ). Observe que
esta excepción indica la cancelación correcta en lugar de una situación de error. Por consiguiente, la propiedad
Exception del elemento Task devuelve null .
Si la propiedad IsCancellationRequested del token devuelve False o si el token de la excepción no coincide con el
token de la tarea, OperationCanceledException se trata como una excepción normal, por lo que la tarea cambia
al estado Faulted. Observe también que la presencia de otras excepciones también hará que la tarea pase al
estado Faulted. Puede obtener el estado de la tarea completada en la propiedad Status .
Es posible que una tarea continúe procesando algunos elementos una vez solicitada la cancelación.
Vea también
Cancelación en subprocesos administrados
Cómo: Cancelar una tarea y sus elementos secundarios
Control de excepciones (biblioteca TPL)
16/09/2020 • 20 minutes to read • Edit Online
Las excepciones no controladas que se inician mediante código de usuario que se ejecuta dentro de una tarea
se propagan de vuelta al subproceso que hace la llamada, excepto en determinados escenarios que se
describen posteriormente en este tema. Las excepciones se propagan cuando se usa uno de los métodos
estáticos o de instancia Task.Wait, los cuales se pueden controlar si se incluye la llamada en una instrucción
try / catch . Si una tarea es la tarea primaria de tareas secundarias asociadas o si se esperan varias tareas,
pueden producirse varias excepciones.
Para propagar todas las excepciones de nuevo al subproceso que realiza la llamada, la infraestructura de la
tarea las encapsula en una instancia de AggregateException . La excepción AggregateException tiene una
propiedad InnerExceptions que se puede enumerar para examinar todas las excepciones originales que se
produjeron y controlar (o no) cada una de ellas de forma individual. También puede controlar las excepciones
originales mediante el método AggregateException.Handle .
Incluso aunque solo se produzca una excepción, se encapsulará en una excepción AggregateException , como
se muestra en el ejemplo siguiente.
using System;
using System.Threading.Tasks;
try
{
task1.Wait();
}
catch (AggregateException ae)
{
foreach (var e in ae.InnerExceptions) {
// Handle the custom exception.
if (e is CustomException) {
Console.WriteLine(e.Message);
}
// Rethrow any other exception.
else {
throw;
}
}
}
}
}
Module Example
Public Sub Main()
Dim task1 = Task.Run(Sub() Throw New CustomException("This exception is expected!"))
Try
task1.Wait()
Catch ae As AggregateException
For Each ex In ae.InnerExceptions
' Handle the custom exception.
If TypeOf ex Is CustomException Then
Console.WriteLine(ex.Message)
' Rethrow any other exception.
Else
Throw
End If
Next
End Try
End Sub
End Module
Para evitar una excepción no controlada, basta con detectar el objeto AggregateException y omitir las
excepciones internas. Sin embargo, esta operación no resulta recomendable porque es igual que detectar el
tipo Exception base en escenarios no paralelos. Si desea detectar una excepción sin realizar acciones concretas
que la resuelvan, puede dejar al programa en un estado indeterminado.
Si no desea llamar al método Task.Wait para esperar a la finalización de una tarea, también puede recuperar la
excepción AggregateException de la propiedad Exception de la tarea, como se muestra en el ejemplo siguiente.
Para más información, consulte la sección Observar excepciones mediante la propiedad Task.Exception de este
tema.
using System;
using System.Threading.Tasks;
while(! task1.IsCompleted) {}
if (task1.Status == TaskStatus.Faulted) {
foreach (var e in task1.Exception.InnerExceptions) {
// Handle the custom exception.
if (e is CustomException) {
Console.WriteLine(e.Message);
}
// Rethrow any other exception.
else {
throw e;
}
}
}
}
}
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim task1 = Task.Run(Sub() Throw New CustomException("This exception is expected!"))
NOTE
Cuando está habilitada la opción "Solo mi código", en algunos casos, Visual Studio se interrumpe en la línea que produce
la excepción y muestra el mensaje de error "Excepción no controlada por el código de usuario". Este error es benigno.
Puede presionar F5 para continuar y ver el comportamiento de control de excepciones que se muestra en estos
ejemplos. Para evitar que Visual Studio se interrumpa con el primer error, desactive la casilla Habilitar Solo mi código
bajo Herramientas, Opciones, Depuración, General.
try {
task1.Wait();
}
catch (AggregateException ae) {
foreach (var e in ae.Flatten().InnerExceptions) {
if (e is CustomException) {
Console.WriteLine(e.Message);
}
else {
throw;
}
}
}
}
}
Module Example
Public Sub Main()
Dim task1 = Task.Factory.StartNew(Sub()
Dim child1 = Task.Factory.StartNew(Sub()
Dim child2 =
Task.Factory.StartNew(Sub()
End Sub,
TaskCreationOptions.AttachedToParent)
Throw New
CustomException("Attached child1 faulted.")
End Sub,
TaskCreationOptions.AttachedToParent)
End Sub)
Try
task1.Wait()
Catch ae As AggregateException
For Each ex In ae.Flatten().InnerExceptions
If TypeOf ex Is CustomException Then
Console.WriteLine(ex.Message)
Else
Throw
End If
Next
End Try
End Sub
End Module
También puede utilizar el método AggregateException.Flatten para volver a generar las excepciones internas de
varias instancias de AggregateException iniciadas por varias tareas en una sola instancia de
AggregateException, como se muestra en el ejemplo siguiente.
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
public class Example
{
public static void Main()
{
try {
ExecuteTasks();
}
catch (AggregateException ae) {
foreach (var e in ae.InnerExceptions) {
Console.WriteLine("{0}:\n {1}", e.GetType().Name, e.Message);
}
}
}
tasks.Add(Task.Run(() => {
// This should throw an UnauthorizedAccessException.
return Directory.GetFiles(path, "*.txt",
SearchOption.AllDirectories);
}));
tasks.Add(Task.Run(() => {
if (path == @"C:\")
throw new ArgumentException("The system root is not a valid path.");
return new String[] { ".txt", ".dll", ".exe", ".bin", ".dat" };
}));
tasks.Add(Task.Run(() => {
throw new NotImplementedException("This operation has not been
implemented.");
}));
try {
Task.WaitAll(tasks.ToArray());
}
catch (AggregateException ae) {
throw ae.Flatten();
}
}
}
// The example displays the following output:
// UnauthorizedAccessException:
// Access to the path 'C:\Documents and Settings' is denied.
// ArgumentException:
// The system root is not a valid path.
// NotImplementedException:
// This operation has not been implemented.
Imports System.Collections.Generic
Imports System.IO
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Try
ExecuteTasks()
Catch ae As AggregateException
For Each e In ae.InnerExceptions
Console.WriteLine("{0}:{2} {1}", e.GetType().Name, e.Message,
vbCrLf)
Next
End Try
End Sub
Sub ExecuteTasks()
' Assume this is a user-entered String.
Dim path = "C:\"
Dim tasks As New List(Of Task)
tasks.Add(Task.Run(Function()
' This should throw an UnauthorizedAccessException.
Return Directory.GetFiles(path, "*.txt",
SearchOption.AllDirectories)
End Function))
tasks.Add(Task.Run(Function()
If path = "C:\" Then
Throw New ArgumentException("The system root is not a valid path.")
End If
Return {".txt", ".dll", ".exe", ".bin", ".dat"}
End Function))
tasks.Add(Task.Run(Sub()
Throw New NotImplementedException("This operation has not been
implemented.")
End Sub))
Try
Task.WaitAll(tasks.ToArray)
Catch ae As AggregateException
Throw ae.Flatten()
End Try
End Sub
End Module
' The example displays the following output:
' UnauthorizedAccessException:
' Access to the path 'C:\Documents and Settings' is denied.
' ArgumentException:
' The system root is not a valid path.
' NotImplementedException:
' This operation has not been implemented.
try {
task1.Wait();
}
catch (AggregateException ae) {
foreach (var e in ae.Flatten().InnerExceptions) {
if (e is CustomException) {
Console.WriteLine(e.Message);
}
}
}
}
}
Module Example
Public Sub Main()
Dim task1 = Task.Run(Sub()
Dim nestedTask1 = Task.Run(Sub()
Throw New CustomException("Detached child
task faulted.")
End Sub)
' Here the exception will be escalated back to joining thread.
' We could use try/catch here to prevent that.
nestedTask1.Wait()
End Sub)
Try
task1.Wait()
Catch ae As AggregateException
For Each ex In ae.Flatten().InnerExceptions
If TypeOf ex Is CustomException Then
' Recover from the exception. Here we just
' print the message for demonstration purposes.
Console.WriteLine(ex.Message)
End If
Next
End Try
End Sub
End Module
Aunque se use una tarea de continuación para observar una excepción en una tarea secundaria, la tarea
primaria debe seguir observando la excepción.
// No waiting required.
tokenSource.Dispose();
try {
task1.Wait();
}
catch (AggregateException ae)
{
// Call the Handle method to handle the custom exception,
// otherwise rethrow the exception.
ae.Handle(ex => { if (ex is CustomException)
Console.WriteLine(ex.Message);
return ex is CustomException;
});
}
}
}
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim task1 = Task.Run(Sub() Throw New CustomException("This exception is expected!"))
Try
task1.Wait()
Catch ae As AggregateException
' Call the Handle method to handle the custom exception,
' otherwise rethrow the exception.
ae.Handle(Function(e)
If TypeOf e Is CustomException Then
Console.WriteLine(e.Message)
End If
Return TypeOf e Is CustomException
End Function)
End Try
End Sub
End Module
A continuación, se muestra un ejemplo más completo que usa el método AggregateException.Handle para
ofrecer un control especial para una excepción UnauthorizedAccessException al enumerar los archivos.
using System;
using System.IO;
using System.Threading.Tasks;
try {
return task1.Result;
}
catch (AggregateException ae) {
ae.Handle( x => { // Handle an UnauthorizedAccessException
if (x is UnauthorizedAccessException) {
Console.WriteLine("You do not have permission to access all folders in this
path.");
Console.WriteLine("See your network administrator or try another path.");
}
return x is UnauthorizedAccessException;
});
return Array.Empty<String>();
}
}
}
// The example displays the following output:
// You do not have permission to access all folders in this path.
// See your network administrator or try another path.
//
// ArgumentException: The path is not of a legal form.
Imports System.IO
Imports System.Threading.Tasks
Module Example
Public Sub Main()
' This should throw an UnauthorizedAccessException.
Try
Dim files = GetAllFiles("C:\")
If files IsNot Nothing Then
For Each file In files
Console.WriteLine(file)
Next
End If
Catch ae As AggregateException
For Each ex In ae.InnerExceptions
Console.WriteLine("{0}: {1}", ex.GetType().Name, ex.Message)
Next
End Try
Console.WriteLine()
using System;
using System.Threading;
using System.Threading.Tasks;
Imports System.Threading
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim task1 = Task.Factory.StartNew(Sub()
Throw New CustomException("task1 faulted.")
End Sub).
ContinueWith(Sub(t)
Console.WriteLine("{0}: {1}",
t.Exception.InnerException.GetType().Name,
t.Exception.InnerException.Message)
End Sub, TaskContinuationOptions.OnlyOnFaulted)
Thread.Sleep(500)
End Sub
End Module
En una aplicación significativa, el delegado de continuación podría registrar información detallada sobre la
excepción y posiblemente generar nuevas tareas para recuperarse de la excepción. Si se produce un error en
una tarea, las siguientes expresiones inician la excepción:
await task
task.Wait()
task.Result
task.GetAwaiter().GetResult()
Use una instrucción try-catch para controlar y observar las excepciones producidas. Como alternativa, acceda
a la propiedad Task.Exception para observar la excepción.
Evento UnobservedTaskException
En algunos escenarios (por ejemplo, cuando se hospedan complementos que no son de confianza), es posible
que se produzcan numerosas excepciones benignas y que resulte demasiado difícil observarlas todas
manualmente. En estos casos, se puede proceder a controlar el evento TaskScheduler.UnobservedTaskException
. La instancia de System.Threading.Tasks.UnobservedTaskExceptionEventArgs que se pasa al controlador se
puede utilizar para evitar que la excepción no observada se propague de nuevo al subproceso de unión.
Vea también
Biblioteca TPL
Procedimiento para usar Parallel.Invoke para ejecutar
operaciones en paralelo
16/09/2020 • 5 minutes to read • Edit Online
En este ejemplo se muestra cómo paralelizar operaciones usando Invoke en Task Parallel Library. Se realizan tres
operaciones en un origen de datos compartido. Las operaciones se pueden ejecutar en paralelo de manera
sencilla, ya que ninguna de ellas modifica el origen.
NOTE
En esta documentación, se utilizan expresiones lambda para definir delegados en la TPL. Si no está familiarizado con las
expresiones lambda de C# o Visual Basic, vea Expresiones lambda en PLINQ y TPL.
Ejemplo
namespace ParallelTasks
{
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
class ParallelInvoke
{
static void Main()
{
// Retrieve Goncharov's "Oblomov" from Gutenberg.org.
string[] words = CreateWordArray(@"http://www.gutenberg.org/files/54700/54700-0.txt");
#region ParallelTasks
// Perform three tasks in parallel on the source array
Parallel.Invoke(() =>
{
Console.WriteLine("Begin first task...");
GetLongestWord(words);
}, // close first Action
() =>
{
Console.WriteLine("Begin second task...");
GetMostCommonWords(words);
}, //close second Action
() =>
{
Console.WriteLine("Begin third task...");
GetCountForWord(words, "sleep");
} //close third Action
); //close parallel.invoke
#region HelperMethods
private static void GetCountForWord(string[] words, string term)
{
var findWord = from word in words
where word.ToUpper().Contains(term.ToUpper())
select word;
Imports System.Net
Imports System.Threading.Tasks
Module ParallelTasks
Sub Main()
' Retrieve Goncharov's "Oblomov" from Gutenberg.org.
Dim words As String() = CreateWordArray("http://www.gutenberg.org/files/54700/54700-0.txt")
'#Region "ParallelTasks"
' Perform three tasks in parallel on the source array
Parallel.Invoke(Sub()
Console.WriteLine("Begin first task...")
GetLongestWord(words)
' close first Action
End Sub,
Sub()
Console.WriteLine("Begin second task...")
GetMostCommonWords(words)
'close second Action
End Sub,
Sub()
Console.WriteLine("Begin third task...")
GetCountForWord(words, "sleep")
'close third Action
End Sub)
'close parallel.invoke
Console.WriteLine("Returned from Parallel.Invoke")
'#End Region
#Region "HelperMethods"
Sub GetCountForWord(ByVal words As String(), ByVal term As String)
Dim findWord = From word In words
Where word.ToUpper().Contains(term.ToUpper())
Select word
Dim s As String
s = "Task 2 -- The most common words are:" & vbCrLf
For Each v In commonWords
s = s & v(0) & vbCrLf
Next
Console.WriteLine(s)
End Sub
' Separate string into an array of words, removing some common punctuation.
Return s.Split(New Char() {" "c, ControlChars.Lf, ","c, "."c, ";"c, ":"c,
"-"c, "_"c, "/"c}, StringSplitOptions.RemoveEmptyEntries)
End Function
#End Region
End Module
' The exmaple displays output like the following:
' Retrieving from http://www.gutenberg.org/files/54700/54700-0.txt
' Begin first task...
' Begin second task...
' Begin third task...
' Task 2 -- The most common words are:
' Oblomov
' himself
' Schtoltz
' Gutenberg
' Project
' another
' thought
' Oblomov's
' nothing
' replied
'
' Task 1 -- The longest word is incomprehensible.
' Task 3 -- The word "sleep" occurs 57 times.
' Returned from Parallel.Invoke
' Press any key to exit
Con Invoke, solo tiene que expresar qué acciones quiere ejecutar simultáneamente y el tiempo de ejecución
controla todos los detalles de programación de los subprocesos, incluido el ajuste automático del número de
núcleos en el equipo host.
En este ejemplo se paralelizan las operaciones, no los datos. Como método alternativo, puede paralelizar los
consultas de LINQ mediante PLINQ y ejecutar las consultas de forma secuencial. También puede paralelizar los
datos con PLINQ. Otra opción consiste en paralelizar las consultas y las tareas. Aunque la sobrecarga resultante
podría reducir el rendimiento en equipos host con relativamente pocos procesadores, se ajusta mejor en equipos
con varios procesadores.
Compilar el código
Copie y pegue el ejemplo completo en un proyecto de Microsoft Visual Studio y presione F5 .
Vea también
Programación en paralelo
Cómo: Cancelar una tarea y sus elementos secundarios
Parallel LINQ (PLINQ)
Procedimiento para devolver un valor a partir de una
tarea
16/09/2020 • 2 minutes to read • Edit Online
En este ejemplo se muestra cómo se usa el tipo System.Threading.Tasks.Task<TResult> para devolver un valor de
la propiedad Result. Requiere que exista el directorio de C:\Usuarios\Pública\Imágenes\Imágenes de muestra\ y
que contenga archivos.
Ejemplo
using System;
using System.Linq;
using System.Threading.Tasks;
class Program
{
static void Main()
{
// Return a value type with a lambda expression
Task<int> task1 = Task<int>.Factory.StartNew(() => 1);
int i = task1.Result;
return result;
});
Module Module1
Sub Main()
ReturnAValue()
End Sub
Sub ReturnAValue()
Dim path =
"C:\Users\Public\Pictures\Sample Pictures\"
Dim files =
System.IO.Directory.GetFiles(path)
Class Test
Public Name As String
Public Number As Double
End Class
End Module
La propiedad Result bloquea el subproceso que realiza la llamada hasta que la tarea finaliza.
Para ver cómo se pasa el resultado de System.Threading.Tasks.Task<TResult> a una tarea de continuación, consulte
Encadenar tareas mediante tareas de continuación.
Vea también
Programación asincrónica basada en tareas
Expresiones lambda en PLINQ y TPL
Procedimiento para cancelar una tarea y sus
elementos secundarios
16/09/2020 • 9 minutes to read • Edit Online
Ejemplo
En este ejemplo se muestra cómo finalizar un objeto Task y sus elementos secundarios en respuesta a una
solicitud de cancelación. También se muestra que, cuando un delegado de usuario finaliza con una excepción
TaskCanceledException, el subproceso que realiza la llamada puede usar opcionalmente el método Wait o el
método WaitAll para esperar a que las tareas finalicen. En este caso, se debe usar un bloque try/catch para
controlar las excepciones en el subproceso que realiza la llamada.
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
// Request cancellation of a task and its children. Note the token is passed
// to (1) the user delegate and (2) as the second argument to Task.Run, so
// to (1) the user delegate and (2) as the second argument to Task.Run, so
// that the task instance can correctly handle the OperationCanceledException.
t = Task.Run(() =>
{
// Create some cancelable child tasks.
Task tc;
for (int i = 3; i <= 10; i++)
{
// For each child task, pass the same token
// to each user delegate and to Task.Run.
tc = Task.Run(() => DoSomeWork(i, token), token);
Console.WriteLine("Task {0} executing", tc.Id);
tasks.Add(tc);
// Pass the same token again to do work on the parent task.
// All will be signaled by the call to tokenSource.Cancel below.
DoSomeWork(2, token);
}
}, token);
try
{
await Task.WhenAll(tasks.ToArray());
}
catch (OperationCanceledException)
{
Console.WriteLine($"\n{nameof(OperationCanceledException)} thrown\n");
}
finally
{
tokenSource.Dispose();
}
if (ct.IsCancellationRequested)
{
Console.WriteLine("Task {0} cancelled", taskNum);
ct.ThrowIfCancellationRequested();
}
}
}
}
// The example displays output like the following:
// Press any key to begin tasks...
// To terminate the example, press 'c' to cancel and exit...
//
// Task 1 executing
// Task 2 executing
// Task 3 executing
// Task 4 executing
// Task 5 executing
// Task 6 executing
// Task 7 executing
// Task 8 executing
// c
// Task cancellation requested.
// Task 2 cancelled
// Task 7 cancelled
//
// OperationCanceledException thrown
//
// Task 2 status is now Canceled
// Task 1 status is now RanToCompletion
// Task 8 status is now Canceled
// Task 7 status is now Canceled
// Task 6 status is now RanToCompletion
// Task 5 status is now RanToCompletion
// Task 4 status is now RanToCompletion
// Task 3 status is now RanToCompletion
Imports System.Collections.Concurrent
Imports System.Threading
Imports System.Threading.Tasks
Module Example
Sub Main()
Dim tokenSource As New CancellationTokenSource()
Dim token As CancellationToken = tokenSource.Token
' Store references to the tasks so that we can wait on them and
' observe their status after cancellation.
Dim t As Task
Dim tasks As New ConcurrentBag(Of Task)()
' Request cancellation of a single task when the token source is canceled.
' Pass the token to the user delegate, and also to the task so it can
' handle the exception correctly.
t = Task.Factory.StartNew(Sub() DoSomeWork(1, token), token)
Console.WriteLine("Task {0} executing", t.Id)
tasks.Add(t)
' Request cancellation of a task and its children. Note the token is passed
' to (1) the user delegate and (2) as the second argument to StartNew, so
' that the task instance can correctly handle the OperationCanceledException.
t = Task.Factory.StartNew(Sub()
' Create some cancelable child tasks.
Dim tc As Task
For i As Integer = 3 To 10
' For each child task, pass the same token
' to each user delegate and to StartNew.
tc = Task.Factory.StartNew(Sub(iteration) DoSomeWork(iteration,
token), i, token)
Console.WriteLine("Task {0} executing", tc.Id)
tasks.Add(tc)
' Pass the same token again to do work on the parent task.
' All will be signaled by the call to tokenSource.Cancel below.
DoSomeWork(2, token)
Next
End Sub,
token)
' Optional: Observe the change in the Status property on the task.
' It is not necessary to wait on tasks that have canceled. However,
' if you do wait, you must enclose the call in a try-catch block to
' catch the TaskCanceledExceptions that are thrown. If you do
' not wait, no exception is thrown if the token that was passed to the
' StartNew method is the same token that requested the cancellation.
End If
Try
Task.WaitAll(tasks.ToArray())
Catch e As AggregateException
Console.WriteLine()
Console.WriteLine("AggregateException thrown with the following inner exceptions:")
' Display information about each exception.
For Each v In e.InnerExceptions
If TypeOf v Is TaskCanceledException
Console.WriteLine(" TaskCanceledException: Task {0}",
DirectCast(v, TaskCanceledException).Task.Id)
Else
Console.WriteLine(" Exception: {0}", v.GetType().Name)
End If
Next
Console.WriteLine()
Finally
tokenSource.Dispose()
End Try
La clase System.Threading.Tasks.Task está totalmente integrada con el modelo de cancelación basado en los tipos
System.Threading.CancellationTokenSource y System.Threading.CancellationToken. Para más información,
consulte el tema sobre la cancelación en subprocesos administrados y la cancelación de tareas.
Vea también
System.Threading.CancellationTokenSource
System.Threading.CancellationToken
System.Threading.Tasks.Task
System.Threading.Tasks.Task<TResult>
Programación asincrónica basada en tareas
Tareas secundarias asociadas y desasociadas
Expresiones lambda en PLINQ y TPL
Cómo: Crear tareas precalculadas
16/09/2020 • 4 minutes to read • Edit Online
En este documento se describe cómo utilizar el método Task.FromResult para recuperar los resultados de las
operaciones asincrónicas de descarga que se retienen en una memoria caché. El método FromResult devuelve un
objeto Task<TResult> terminado que contiene el valor proporcionado como su propiedad Result. Este método es
útil cuando se realiza una operación asincrónica que devuelve un objeto Task<TResult> y el resultado de ese
objeto Task<TResult> ya se ha calculado.
Ejemplo
En el ejemplo siguiente se descargan cadenas desde la Web. Define el método DownloadStringAsync . Este método
descarga cadenas de la Web de forma asincrónica. En este ejemplo también se usa un objeto
ConcurrentDictionary<TKey,TValue> para almacenar en caché los resultados de las operaciones anteriores. Si la
dirección de entrada se mantiene en esta memoria caché, DownloadStringAsync utiliza el método FromResult para
generar un objeto Task<TResult> que incluye el contenido en esa dirección. En caso contrario,
DownloadStringAsync descarga el archivo desde la Web y agrega el resultado a la memoria caché.
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
/* Sample output:
Retrieved 27798 characters. Elapsed time was 1045 ms.
Retrieved 27798 characters. Elapsed time was 0 ms.
*/
Imports System.Collections.Concurrent
Imports System.Diagnostics
Imports System.Linq
Imports System.Net
Imports System.Threading.Tasks
' Perform the same operation a second time. The time required
' should be shorter because the results are held in the cache.
stopwatch.Restart()
downloads = From url In urls _
Select DownloadStringAsync(url)
Task.WhenAll(downloads).ContinueWith(Sub(results)
' Print the number of characters download and the elapsed
time.
stopwatch.Stop()
Console.WriteLine("Retrieved {0} characters. Elapsed time was
{1} ms.", results.Result.Sum(Function(result) result.Length), stopwatch.ElapsedMilliseconds)
End Sub).Wait()
End Sub
End Class
Este ejemplo calcula el tiempo necesario para descargar varias cadenas dos veces. El segundo conjunto de
operaciones de descarga debe tardar menos tiempo que el primer conjunto porque los resultados se mantienen
en la memoria caché. El método FromResult habilita el método DownloadStringAsync para crear objetos
Task<TResult> que contienen estos resultados precalculados.
Vea también
Programación asincrónica basada en tareas
Cómo: Recorrer un árbol binario con tareas paralelas
16/09/2020 • 2 minutes to read • Edit Online
En el ejemplo siguiente se muestran dos formas de usar las tareas paralelas para recorrer una estructura de datos
en árbol. La creación del árbol se deja como un ejercicio.
Ejemplo
public class TreeWalk
{
static void Main()
{
Tree<MyClass> tree = new Tree<MyClass>();
try
{
Task.WaitAll(left, right);
}
catch (AggregateException )
{
//handle exceptions here
}
}
// By using Parallel.Invoke
public static void DoTree2<T>(Tree<T> tree, Action<T> action)
{
if (tree == null) return;
Parallel.Invoke(
() => DoTree2(tree.Left, action),
() => DoTree2(tree.Right, action),
() => action(tree.Data)
);
}
}
Imports System.Threading.Tasks
Try
Task.WaitAll(left, right)
Catch ae As AggregateException
'handle exceptions here
End Try
End Sub
Los dos métodos que se muestran son funcionalmente equivalentes. Con el uso del método StartNew para crear y
ejecutar las tareas, estas devuelven un identificador que puede usarse para esperar las tareas y controlar
excepciones.
Vea también
Biblioteca TPL
Cómo: Desencapsular una tarea anidada
16/09/2020 • 5 minutes to read • Edit Online
Puede devolver una tarea de un método y, a continuación, esperar o continuar a partir de dicha tarea, tal como se
muestra en el ejemplo siguiente:
// Outputs: System.Threading.Tasks.Task`1[System.String]
Console.WriteLine(t.Result);
' Note the type of t and t2.
Dim t As Task(Of Task(Of String)) = Task.Run(Function() DoWorkAsync())
Dim t2 As Task(Of Task(Of String)) = DoWorkAsync().ContinueWith(Function(s) DoMoreWorkAsync())
Aunque es posible escribir código para desajustar la tarea exterior y recuperar la tarea original y su propiedad
Result, este código no es fácil de escribir porque se deben controlar las excepciones y también las solicitudes de
cancelación. En esta situación, se recomienda que use uno de los métodos de extensión Unwrap, como se muestra
en el ejemplo siguiente.
Los métodos Unwrap se pueden usar para transformar cualquier delegado Task<Task> o Task<Task<TResult>> (
Task(Of Task) o Task(Of Task(Of TResult)) en Visual Basic) en un delegado Task o Task<TResult> (
Task(Of TResult) en Visual Basic). La nueva tarea representa totalmente la tarea anidada interna e incluye el
estado de cancelación y todas las excepciones.
Ejemplo
En el ejemplo siguiente se muestra cómo usar los métodos de extensión Unwrap.
namespace Unwrap
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
// A program whose only use is to demonstrate Unwrap.
class Program
{
static void Main()
{
// An arbitrary threshold value.
byte threshold = 0x40;
// data is a Task<byte[]>
var data = Task<byte[]>.Factory.StartNew(() =>
{
return GetData();
});
lastStep.Wait();
Console.WriteLine("Press any key");
Console.ReadKey();
}
#region Dummy_Methods
private static byte[] GetData()
{
Random rand = new Random();
byte[] bytes = new byte[64];
rand.NextBytes(bytes);
return bytes;
}
byte final = 0;
foreach (byte item in data)
{
final ^= item;
Console.WriteLine("{0:x}", final);
}
Console.WriteLine("Done computing");
return final;
}
#endregion
}
}
Module UnwrapATask2
Sub Main()
' An arbitrary threshold value.
Dim threshold As Byte = &H40
stepTwo.Result, threshold)
End Sub)
Else
Return DoSomeOtherAsynchronousWork(stepTwo.Result,
threshold)
End If
End Function)
Try
lastStep.Wait()
Catch ae As AggregateException
For Each ex As Exception In ae.InnerExceptions
Console.WriteLine(ex.Message & ex.StackTrace & ex.GetBaseException.ToString())
Next
End Try
#Region "Dummy_Methods"
Function GetData() As Byte()
Dim rand As Random = New Random()
Dim bytes(64) As Byte
rand.NextBytes(bytes)
Return bytes
End Function
Vea también
System.Threading.Tasks.TaskExtensions
Programación asincrónica basada en tareas
Cómo: Evitar que una tarea secundaria se adjunte a
su elemento primario
16/09/2020 • 6 minutes to read • Edit Online
En este documento se explica cómo evitar que una tarea secundaria se adjunte a la tarea principal. Impedir que una
tarea secundaria se adjunte a su elemento principal es útil cuando se llama a un componente que está escrito por
un tercero y que también usa las tareas. Por ejemplo, un componente de terceros que utiliza la opción
TaskCreationOptions.AttachedToParent para crear un objeto Task o Task<TResult> puede causar problemas en el
código si es de larga duración o produce una excepción no controlada.
Ejemplo
En el ejemplo siguiente se comparan los efectos del uso de las opciones predeterminadas con los efectos de
impedir que una tarea secundaria se adjunte al elemento principal. En el ejemplo se crea un objeto Task que llama
a una biblioteca de terceros que también usa un objeto Task. La biblioteca de otro fabricante utiliza la opción
AttachedToParent para crear el objeto Task. La aplicación utiliza la opción TaskCreationOptions.DenyChildAttach
para crear la tarea principal. Esta opción indica el tiempo de ejecución para quitar la especificación
AttachedToParent de las tareas secundarias.
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
return widgetTask;
}, parentTaskOptions);
// Perform the same operation two times. The first time, the operation
// is performed by using the default task creation options. The second
// time, the operation is performed by using the DenyChildAttach option
// in the parent task.
Console.WriteLine();
/* Sample output:
Demonstrating parent/child tasks with default options...
Starting widget as a background task...
Waiting for parent task to finish...
Parent task has finished. Elapsed time is 5014 ms.
Performing more work on the main thread...
Elapsed time is 7019 ms.
Waiting for child task to finish...
Child task has finished. Elapsed time is 7019 ms.
' Demonstrates how to prevent a child task from attaching to the parent.
Friend Class DenyChildAttach
Private Shared Sub RunWidget(ByVal widget As Contoso.Widget, ByVal parentTaskOptions As
TaskCreationOptions)
' Record the time required to run the parent
' and child tasks.
Dim stopwatch As New Stopwatch()
stopwatch.Start()
' Perform the same operation two times. The first time, the operation
' is performed by using the default task creation options. The second
' time, the operation is performed by using the DenyChildAttach option
' in the parent task.
Console.WriteLine()
Dado que una tarea primaria no finaliza hasta que finalizan todas las tareas secundarias, una tarea secundaria que
se ejecute durante mucho tiempo puede provocar que el rendimiento general de la aplicación sea bajo. En este
ejemplo, cuando la aplicación usa las opciones predeterminadas para crear la tarea principal, la tarea secundaria
debe finalizar antes de que finalice la principal. Cuando la aplicación utiliza la opción
TaskCreationOptions.DenyChildAttach, el elemento secundario no está asociado al principal. Por lo tanto, la
aplicación puede realizar un trabajo adicional después de que finalice la tarea principal y antes debe esperar a que
finalice la tarea secundaria.
Vea también
Programación asincrónica basada en tareas
Flujo de datos (biblioteca TPL)
16/09/2020 • 68 minutes to read • Edit Online
La biblioteca TPL (Task Parallel Library, biblioteca de procesamiento paralelo basado en tareas) proporciona
componentes de flujo de datos que ayudan a aumentar la solidez de aplicaciones habilitadas para
simultaneidad. Se conoce colectivamente a estos componentes de flujo de datos como biblioteca TDF (biblioteca
de TPL Dataflow) pero nos referiremos descriptivamente a ella como "biblioteca de flujos de datos TPL". Este
modelo de flujo de datos promueve una programación basada en actores mediante el paso de mensajes en
proceso para tareas de canalización y de flujo de datos de grano grueso. Los componentes de flujo de datos se
basan en los tipos y la infraestructura de programación de la biblioteca TPL y se integran con la compatibilidad
de los lenguajes C#, Visual Basic y F# para proporcionar programación asincrónica. Estos componentes de flujo
de datos son útiles cuando se tienen varias operaciones que deben comunicarse entre sí de forma asincrónica, o
cuando se desea procesar datos a medida que estén disponibles. Por ejemplo, piense en una aplicación que
procesa datos de imagen de una cámara web. Con el modelo de flujo de datos, la aplicación puede procesar
fotogramas de imagen a medida que estén disponibles. Si la aplicación mejora fotogramas de imagen, por
ejemplo, corrigiendo la luz o reduciendo ojos rojos, puede crear una canalización de los componentes de flujo
de datos. Cada fase de la canalización puede utilizar más funcionalidad de paralelismo de grano grueso, como la
funcionalidad proporcionada por la biblioteca TPL, para transformar la imagen.
En este documento se proporciona información general sobre la biblioteca de flujos de datos TPL. Se describe el
modelo de programación, los tipos de bloques de flujo de datos predefinidos y cómo configurar bloques de
flujo de datos para satisfacer las necesidades específicas de las aplicaciones.
NOTE
La biblioteca de flujos de datos TPL (el espacio de nombres System.Threading.Tasks.Dataflow) no se distribuye con .NET.
Para instalar el espacio de nombres System.Threading.Tasks.Dataflow en Visual Studio, abra el proyecto, seleccione
Administrar paquetes NuGet en el menú Proyecto y busque en línea el paquete
System.Threading.Tasks.Dataflow . Como alternativa, para realizar la instalación con la CLI de .Net Core, ejecute
dotnet add package System.Threading.Tasks.Dataflow .
Modelo de programación
La biblioteca de flujos de datos TPL proporciona una base para el paso de mensajes y para paralelizar
aplicaciones que consumen mucha CPU, así como aplicaciones intensivas de entrada y salida con alto
rendimiento y latencia baja. También ofrece el control explícito sobre cómo almacenar los datos en búfer y
desplazarlos alrededor del sistema. Para entender mejor el modelo de programación de flujo de datos, piense
en una aplicación que de forma asincrónica carga imágenes desde el disco y crea un compuesto de esas
imágenes. Los modelos de programación tradicionales suelen usar devoluciones de llamada y objetos de
sincronización, como bloqueos, para coordinar tareas y tener acceso a datos compartidos. Con el modelo de
programación de flujo de datos, puede crear objetos de flujo de datos que procesan las imágenes como se leen
desde el disco. Bajo el modelo de flujo de datos, se declara cómo se controlan los datos cuando están
disponibles, así como las dependencias entre datos. Dado que el runtime administra las dependencias entre
datos, se puede evitar a menudo la necesidad de sincronizar el acceso a los datos compartidos. Además, dado
que el runtime programa el trabajo según la llegada asincrónica de datos, el flujo de datos puede mejorar la
capacidad de respuesta y el nivel de rendimiento administrando de forma eficaz los subprocesos subyacentes.
Para ver un ejemplo en donde se usa el modelo de programación de flujo de datos para implementar
procesamiento de imágenes en una aplicación de Windows Forms, vea Tutorial: Uso de flujos de datos en
aplicaciones de Windows Forms.
Orígenes y destinos
La biblioteca de flujos de datos TPL consta de bloques de flujo de datos, que son estructuras de datos que
almacenan datos en búfer y procesan datos. La biblioteca TPL define tres tipos de bloques de flujos de datos:
bloques de origen, bloques de destino y bloques propagadores. Un bloque de origen actúa como un origen de
datos y se puede leer desde él. Un bloque de destino actúa como un receptor de datos y se puede escribir en él.
Un bloque propagador actúa como un bloque de origen y un bloque de destino, y se puede leer desde él y
escribir en él. La biblioteca TPL define la interfaz System.Threading.Tasks.Dataflow.ISourceBlock<TOutput> para
representar orígenes, System.Threading.Tasks.Dataflow.ITargetBlock<TInput> para representar destinos y
System.Threading.Tasks.Dataflow.IPropagatorBlock<TInput,TOutput> para representar propagadores.
IPropagatorBlock<TInput,TOutput> hereda de ISourceBlock<TOutput> y de ITargetBlock<TInput>.
La biblioteca de flujos de datos TPL proporciona varios tipos de bloques de flujo de datos predefinidos que
implementan las interfaces ISourceBlock<TOutput>, ITargetBlock<TInput> y
IPropagatorBlock<TInput,TOutput>. Estos tipos de bloques de flujo de datos se describen en este documento en
la sección Tipos de bloques de flujo de datos predefinidos.
Conectar bloques
Puede conectar los bloques de flujo de datos para establecer canalizaciones, que son secuencias lineales de
bloques de flujo de datos, o redes, que son gráficos de bloques de flujo de datos. Una canalización es una forma
de red. En una canalización o red, los orígenes propagan datos de forma asincrónica en destinos a medida que
esos datos están disponibles. El método ISourceBlock<TOutput>.LinkTo vincula un bloque de flujo de datos de
origen a un bloque de destino. Un origen puede vincularse a cero o más destinos; los destinos se pueden
vincular a partir de cero o más orígenes. Puede agregar o quitar bloques de flujo de datos hacia o desde una
canalización o red simultáneamente. Los tipos de bloques de flujo de datos predefinidos controlan todos los
aspectos de la seguridad para subprocesos de vinculación y desvinculación.
Para ver un ejemplo en donde se conectan bloques de flujo de datos para formar una canalización básica, vea
Tutorial: Creación de una canalización de flujos de datos. Para ver un ejemplo en donde se conectan bloques de
flujo de datos para formar una red más compleja, vea Tutorial: Uso de flujos de datos en aplicaciones de
Windows Forms. Para obtener un ejemplo en donde un destino se desvincula de un origen después de que el
origen le ofrezca un mensaje, vea Procedimiento: Desvinculación de bloques de flujo de datos.
Filtrado
Cuando se llama al método ISourceBlock<TOutput>.LinkTo para vincular un origen a un destino, puede
proporcionar un delegado que determina si el bloque de destino acepta o rechaza un mensaje basado en el
valor de ese mensaje. Este mecanismo de filtrado resulta útil para garantizar que un bloque de flujo de datos
recibe solo ciertos valores. Para la mayoría de los tipos de bloques de flujo de datos predefinidos, si un bloque
de origen está conectado a varios bloques de destino, cuando un bloque de destino rechaza un mensaje, el
origen proporciona ese mensaje al destino siguiente. El orden en el que un origen proporciona mensajes a los
destinos se define mediante el origen y puede variar según el tipo de origen. La mayoría de los tipos de bloques
de origen dejan de proporcionar un mensaje después de que un destino acepta ese mensaje. Una excepción a
esta regla es la clase BroadcastBlock<T>, que proporciona cada mensaje a todos los destinos, aunque algunos
destinos rechacen el mensaje. Para ver un ejemplo en donde se usa el filtrado para procesar únicamente
determinados mensajes, consulte Tutorial: Uso de flujos de datos en aplicaciones de Windows Forms.
IMPORTANT
Dado que cada tipo de bloque de flujo de datos de origen predefinido garantiza que los mensajes se propaguen en el
orden en que se reciben, se debe leer cada mensaje desde el bloque de origen antes de que el bloque de origen pueda
procesar el mensaje siguiente. Por consiguiente, si usa el filtrado para conectarse varios destinos a un origen, asegúrese
de que al menos un bloque de destino recibe cada mensaje. De lo contrario, la aplicación podría generar un interbloqueo.
Paso de mensajes
El modelo de programación basado en el flujo de datos está relacionado con el concepto paso de mensajes,
donde los componentes independientes de un programa comunican entre sí enviándose mensajes. Una manera
de propagar mensajes entre los componentes de la aplicación es llamar a los métodos Post y
DataflowBlock.SendAsync para enviar mensajes a los bloques de flujo de datos de destino (Post actúa de forma
sincrónica; SendAsync actúa de forma asincrónica) y a los métodos Receive, ReceiveAsync y TryReceive para
recibir los mensajes desde los bloques de origen. Puede combinar estos métodos con las canalizaciones o redes
de flujo de datos enviando datos de entrada al nodo principal (un bloque de destino) y recibiendo datos de
salida del nodo terminal de la canalización o de los nodos terminales de la red (uno o varios bloques de origen).
También puede utilizar el método Choose para leer desde el primero de los orígenes proporcionados siempre
que tenga datos disponibles y realice acciones sobre esos datos.
Los bloques de origen proporcionan datos a los bloques de destino llamando al método
ITargetBlock<TInput>.OfferMessage. El bloque de destino responde a un mensaje proporcionado de una de
estas tres maneras: puede aceptar el mensaje, rechazar el mensaje o posponer el mensaje. Cuando el destino
acepta el mensaje, el método OfferMessage devuelve Accepted. Cuando el destino rechaza el mensaje, el
método OfferMessage devuelve Declined. Cuando el destino requiere que ya no recibe ningún mensaje del
origen, OfferMessage devuelve DecliningPermanently. Los tipos de bloques de origen predefinidos no
proporcionan mensajes a los destinos vinculados después de recibir este valor devuelto, y automáticamente se
desvinculan de estos destinos.
Cuando un bloque de destino pospone el mensaje para su uso posterior, el método OfferMessage devuelve
Postponed. Un bloque de destino que pospone un mensaje puede llamar posteriormente al método
ISourceBlock<TOutput>.ReserveMessage para tratar de reservar el mensaje proporcionado. En este punto, el
mensaje todavía permanece disponible y lo puede usar el bloque de destino, o puede que otro destino haya
tomado el mensaje. Cuando el bloque de destino requiere el mensaje posteriormente o cuando ya no necesita
el mensaje, llama al método ISourceBlock<TOutput>.ConsumeMessage o ReleaseReservation, respectivamente.
La reserva de mensajes la utilizan normalmente los tipos de bloques de flujo de datos que trabajan en modo no
expansivo. El modo no expansivo se explica más adelante en este documento. En lugar de reservar un mensaje
pospuesto, un bloque de destino puede utilizar el método ISourceBlock<TOutput>.ConsumeMessage para
intentar utilizar directamente el mensaje pospuesto.
Finalización del bloque de flujo de datos
Los bloques de flujo de datos también admiten el concepto de finalización. Un bloque de flujo de datos que está
en estado completado no realiza ningún trabajo posterior. Cada bloque de flujo de datos tiene un objeto
System.Threading.Tasks.Task asociado, conocido como tarea de finalización, que representa el estado de
finalización del bloque. Dado que para finalizar se puede esperar un objeto Task, mediante tareas de finalización,
para finalizar se pueden esperar uno o más nodos terminales de una red de flujo de datos. La interfaz
IDataflowBlock define el método Complete, que informa al bloque de flujo de datos de una solicitud para que se
complete, y la propiedad Completion, que devuelve la tarea de finalización para el bloque de flujo de datos.
Tanto ISourceBlock<TOutput> como ITargetBlock<TInput> heredan de la interfaz IDataflowBlock.
Hay dos maneras de determinar si un bloque de flujo de datos se completó sin error, encontró uno o más
errores, o se canceló. La primera manera consiste en llamar al método Task.Wait en la tarea de finalización en un
bloque try - catch ( Try - Catch en Visual Basic). En el siguiente ejemplo se crea un objeto
ActionBlock<TInput> que produce ArgumentOutOfRangeException si su valor de entrada es menor que cero.
AggregateException se produce cuando este ejemplo llama a Wait en la tarea de finalización. Se obtiene acceso
a ArgumentOutOfRangeException mediante la propiedad InnerExceptions del objeto AggregateException.
// Create an ActionBlock<int> object that prints its input
// and throws ArgumentOutOfRangeException if the input
// is less than zero.
var throwIfNegative = new ActionBlock<int>(n =>
{
Console.WriteLine("n = {0}", n);
if (n < 0)
{
throw new ArgumentOutOfRangeException();
}
});
/* Output:
n = 0
n = -1
Encountered ArgumentOutOfRangeException: Specified argument was out of the range
of valid values.
*/
' Create an ActionBlock<int> object that prints its input
' and throws ArgumentOutOfRangeException if the input
' is less than zero.
Dim throwIfNegative = New ActionBlock(Of Integer)(Sub(n)
Console.WriteLine("n = {0}", n)
If n < 0 Then
Throw New ArgumentOutOfRangeException()
End If
End Sub)
' Output:
' n = 0
' n = -1
' Encountered ArgumentOutOfRangeException: Specified argument was out of the range
' of valid values.
'
En este ejemplo se muestra el caso en el que una excepción no está controlada en el delegado de un bloque de
flujo de datos de ejecución. Se recomienda controlar las excepciones en los cuerpos de estos bloques. Sin
embargo, si no puede hacerlo, el bloque se comporta como si estuviera cancelado y no procesa los mensajes
entrantes.
Cuando un bloque de flujo de datos se cancela explícitamente, el objeto AggregateException contiene
OperationCanceledException en la propiedad InnerExceptions. Para obtener más información sobre la
cancelación del flujo de datos, vea la sección Habilitar la cancelación.
La segunda manera de determinar el estado de finalización de un bloque de flujo de datos es usar una
continuación de la tarea de finalización o utilizar las características de lenguaje asincrónicas de C# y Visual Basic
para esperar a la tarea de finalización de forma asincrónica. El delegado que se proporciona al método
Task.ContinueWith toma un objeto Task que representa la tarea anterior. En el caso de la propiedad Completion,
el delegado de continuación toma la propia tarea de finalización. El siguiente ejemplo se parece el anterior, con
la salvedad de que también utiliza el método ContinueWith para crear una tarea de continuación que imprime
el estado de la operación total de flujo de datos.
// Create an ActionBlock<int> object that prints its input
// and throws ArgumentOutOfRangeException if the input
// is less than zero.
var throwIfNegative = new ActionBlock<int>(n =>
{
Console.WriteLine("n = {0}", n);
if (n < 0)
{
throw new ArgumentOutOfRangeException();
}
});
/* Output:
n = 0
n = -1
The status of the completion task is 'Faulted'.
Encountered ArgumentOutOfRangeException: Specified argument was out of the range
of valid values.
*/
' Create an ActionBlock<int> object that prints its input
' and throws ArgumentOutOfRangeException if the input
' is less than zero.
Dim throwIfNegative = New ActionBlock(Of Integer)(Sub(n)
Console.WriteLine("n = {0}", n)
If n < 0 Then
Throw New ArgumentOutOfRangeException()
End If
End Sub)
' Output:
' n = 0
' n = -1
' The status of the completion task is 'Faulted'.
' Encountered ArgumentOutOfRangeException: Specified argument was out of the range
' of valid values.
'
También puede usar propiedades como IsCanceled en el cuerpo de la tarea de continuación para determinar
información adicional sobre el estado de finalización de un bloque de flujo de datos. Para obtener más
información sobre las tareas de continuación y cómo se relacionan con la cancelación y el control de errores,
vea Encadenar tareas mediante tareas de continuación, Cancelación de tareas y Control de excepciones.
/* Output:
0
1
2
*/
' Output:
' 0
' 1
' 2
'
Para obtener un ejemplo completo en donde se muestra cómo escribir y leer mensajes desde un
objeto BufferBlock<T>, vea Procedimiento: Escritura y lectura de mensajes en un bloque de flujo de datos.
BroadcastBlock(T)
La clase BroadcastBlock<T> resulta útil si debe pasar varios mensajes a otro componente, pero este
componente solo necesita el valor más reciente. Esta clase también resulta útil si desea difundir un mensaje a
varios componentes.
En el siguiente ejemplo básico se publica un valor Double a un objeto BroadcastBlock<T> y después se lee ese
valor desde el objeto varias veces. Dado que los valores no se quitan de los objetos BroadcastBlock<T> después
de leerlos, el mismo valor está disponible cada vez.
// Create a BroadcastBlock<double> object.
var broadcastBlock = new BroadcastBlock<double>(null);
/* Output:
3.14159265358979
3.14159265358979
3.14159265358979
*/
' Receive the messages back from the block several times.
For i As Integer = 0 To 2
Console.WriteLine(broadcastBlock.Receive())
Next i
' Output:
' 3.14159265358979
' 3.14159265358979
' 3.14159265358979
'
Para obtener un ejemplo completo en donde se muestra cómo utilizar BroadcastBlock<T> para difundir un
mensaje a varios bloques de destino, vea Procedimiento: Especificación de un Programador de tareas en un
bloque de flujo de datos.
WriteOnceBlock(T)
La clase WriteOnceBlock<T> se asemeja a la clase BroadcastBlock<T>, salvo que un objeto WriteOnceBlock<T>
se puede escribir una sola una vez. Puede pensar que WriteOnceBlock<T> es similar a la palabra clave de C#
readonly (Readonly en Visual Basic), salvo que un objeto WriteOnceBlock<T> se vuelve inalterable después de
recibir un valor en lugar de una construcción. Al igual que la clase BroadcastBlock<T>, cuando un destino recibe
un mensaje de un objeto WriteOnceBlock<T>, el mensaje no se quita de dicho objeto. Por tanto, varios destinos
reciben una copia del mensaje. La clase WriteOnceBlock<T> es útil si desea difundir solamente el primero de
varios de mensajes.
En el siguiente ejemplo básico se exponen varios valores String a un objeto WriteOnceBlock<T> y después se
lee ese valor desde el objeto. Dado que un objeto WriteOnceBlock<T> solo se puede escribir una vez, a partir
de que un objeto WriteOnceBlock<T> recibe un mensaje, ese objeto descarta los mensajes subsiguientes.
// Create a WriteOnceBlock<string> object.
var writeOnceBlock = new WriteOnceBlock<string>(null);
/* Sample output:
Message 2
*/
Para ver un ejemplo completo en donde se muestra cómo utilizar WriteOnceBlock<T> para recibir el valor de la
primera operación que finaliza, consulte Procedimiento: Desvinculación de bloques de flujo de datos.
Bloques de ejecución
Los bloques de ejecución llaman a un delegado proporcionado por el usuario para cada fragmento de datos
recibidos. La biblioteca de flujos de datos TPL proporciona tres tipos de bloques de ejecución:
ActionBlock<TInput>, System.Threading.Tasks.Dataflow.TransformBlock<TInput,TOutput> y
System.Threading.Tasks.Dataflow.TransformManyBlock<TInput,TOutput>.
ActionBlock(T)
La clase ActionBlock<TInput> es un bloque de destino que llama a un delegado cuando recibe datos. Piense en
un objeto ActionBlock<TInput> como un delegado que se ejecuta de forma asincrónica cuando los datos están
disponibles. El delegado que se proporciona a un objeto ActionBlock<TInput> puede ser de tipo Action<T> o
tipo System.Func<TInput, Task> . Si se utiliza un objeto ActionBlock<TInput> con Action<T>, se considera que el
procesamiento de cada elemento de entrada se ha completado cuando devuelve el delegado. Si se utiliza un
objeto ActionBlock<TInput> con System.Func<TInput, Task> , se considera que el procesamiento de cada
elemento de entrada se ha completado solamente cuando el objeto devuelto Task está completo. Mediante
estos dos mecanismos, se puede utilizar ActionBlock<TInput> para el procesamiento sincrónico y asincrónico
de cada elemento de entrada.
En el siguiente ejemplo básico se exponen varios valores Int32 a un objeto ActionBlock<TInput>. El objeto
ActionBlock<TInput> imprime esos valores en la consola. Después, en este ejemplo se establece el bloque en
estado completado y se espera hasta que finalicen todas las tareas de flujo de datos.
// Create an ActionBlock<int> object that prints values
// to the console.
var actionBlock = new ActionBlock<int>(n => Console.WriteLine(n));
// Set the block to the completed state and wait for all
// tasks to finish.
actionBlock.Complete();
actionBlock.Completion.Wait();
/* Output:
0
10
20
*/
' Set the block to the completed state and wait for all
' tasks to finish.
actionBlock.Complete()
actionBlock.Completion.Wait()
' Output:
' 0
' 10
' 20
'
Para obtener ejemplos completos en donde se muestra cómo usar delegados con la clase ActionBlock<TInput>,
vea Procedimiento: Ejecución de una acción cuando un bloque de flujo de datos recibe datos.
TransformBlock(TInput, TOutput)
La clase TransformBlock<TInput,TOutput> es similar a la clase ActionBlock<TInput>, salvo que actúa como
origen y como destino. El delegado que pasa a un objeto TransformBlock<TInput,TOutput> devuelve un valor
de tipo TOutput . El delegado que se proporciona a un objeto TransformBlock<TInput,TOutput> puede ser de
tipo System.Func<TInput, TOutput> o tipo System.Func<TInput, Task<TOutput>> . Si se utiliza un objeto
TransformBlock<TInput,TOutput> con System.Func<TInput, TOutput> , se considera que el procesamiento de
cada elemento de entrada se ha completado cuando devuelve el delegado. Si se utiliza un objeto
TransformBlock<TInput,TOutput> que se usa con System.Func<TInput, Task<TOutput>> , se considera que el
procesamiento de cada elemento de entrada se ha completado solamente cuando el objeto devuelto
Task<TResult> está completo. Al igual que sucede con ActionBlock<TInput>, mediante estos dos mecanismos,
se puede utilizar TransformBlock<TInput,TOutput> para el procesamiento sincrónico y asincrónico de cada
elemento de entrada.
En el siguiente ejemplo básico se crea un objeto TransformBlock<TInput,TOutput> que calcula la raíz cuadrada
de la entrada. El objeto TransformBlock<TInput,TOutput> toma valores Int32 como entrada y genera valores
Double como salida.
// Create a TransformBlock<int, double> object that
// computes the square root of its input.
var transformBlock = new TransformBlock<int, double>(n => Math.Sqrt(n));
/* Output:
3.16227766016838
4.47213595499958
5.47722557505166
*/
' Output:
' 3.16227766016838
' 4.47213595499958
' 5.47722557505166
'
Para obtener ejemplos completos que utilizan TransformBlock<TInput,TOutput> en una red de bloques de flujo
de datos que realiza procesamiento de imágenes en una aplicación de Windows Forms, vea Tutorial: Uso de
flujos de datos en aplicaciones de Windows Forms.
TransformManyBlock(TInput, TOutput)
La clase TransformManyBlock<TInput,TOutput> es similar a la clase TransformBlock<TInput,TOutput>, salvo
que TransformManyBlock<TInput,TOutput> genere cero o más valores de salida por cada valor de entrada, en
lugar de generar un solo valor de salida por cada valor de entrada. El delegado que se proporciona a un objeto
TransformManyBlock<TInput,TOutput> puede ser de tipo System.Func<TInput, IEnumerable<TOutput>> o tipo
System.Func<TInput, Task<IEnumerable<TOutput>>> . Si se utiliza un objeto TransformManyBlock<TInput,TOutput>
con System.Func<TInput, IEnumerable<TOutput>> , se considera que el procesamiento de cada elemento de
entrada se ha completado cuando devuelve el delegado. Si se utiliza un objeto
TransformManyBlock<TInput,TOutput> con System.Func<TInput, Task<IEnumerable<TOutput>>> , se considera que
el procesamiento de cada elemento de entrada se ha completado solo cuando el objeto devuelto
System.Threading.Tasks.Task<IEnumerable<TOutput>> está completo.
En el siguiente ejemplo básico se crea un objeto TransformManyBlock<TInput,TOutput> que divide las cadenas
en sus secuencias de caracteres individuales. El objeto TransformManyBlock<TInput,TOutput> toma valores
String como entrada y genera valores Char como salida.
// Create a TransformManyBlock<string, char> object that splits
// a string into its individual characters.
var transformManyBlock = new TransformManyBlock<string, char>(
s => s.ToCharArray());
/* Output:
H
e
l
l
o
W
o
r
l
d
*/
' Output:
' H
' e
' l
' l
' o
' W
' o
' r
' l
' d
'
Para obtener ejemplos completos que usan TransformManyBlock<TInput,TOutput> para generar varios
resultados independientes para cada entrada en una canalización de flujo de datos, vea Tutorial: Creación de una
canalización de flujos de datos.
Grado de paralelismo
Cada uno de los objetos ActionBlock<TInput>, TransformBlock<TInput,TOutput> y
TransformManyBlock<TInput,TOutput> almacena en búfer los mensajes de entrada hasta que el bloque está
listo para procesarlos. De forma predeterminada, estas clases procesan los mensajes en el orden en el que se
recibieron, un mensaje cada vez. También puede especificar el grado de paralelismo para permitir que los
objetos ActionBlock<TInput>, TransformBlock<TInput,TOutput> y TransformManyBlock<TInput,TOutput>
puedan procesar varios mensajes simultáneamente. Para obtener más información sobre la ejecución
simultánea, vea la sección Especificar el grado de paralelismo más adelante en este documento. Para obtener un
ejemplo en donde se establece el grado de paralelismo que permite que un bloque de flujo de datos de
ejecución pueda procesar varios mensajes al mismo tiempo, vea Procedimiento: Especificación del grado de
paralelismo en un bloque de flujo de datos.
Resumen de tipos de delegado
En la tabla siguiente se resumen los tipos de delegado que puede proporcionar a los objetos
ActionBlock<TInput>, TransformBlock<TInput,TOutput> y TransformManyBlock<TInput,TOutput>. En esta tabla
también se especifica si el tipo de delegado funciona de forma sincrónica o asincrónica.
También puede utilizar expresiones lambda cuando trabaja con tipos de bloque de ejecución. Para obtener un
ejemplo en donde se muestra cómo usar una expresión lambda con un bloque de ejecución, vea Procedimiento:
Ejecución de una acción cuando un bloque de flujo de datos recibe datos.
Bloques de agrupación
Los bloques de agrupación combinan datos de uno o más orígenes y con distintas restricciones. La biblioteca de
flujos de datos TPL proporciona tres tipos de bloques de combinación: BatchBlock<T>, JoinBlock<T1,T2> y
BatchedJoinBlock<T1,T2>.
BatchBlock(T)
La clase BatchBlock<T> combina conjuntos de datos de entrada, que se conocen como lotes, en las matrices de
datos de salida. Especifique el tamaño de cada lote cuando crea un objeto BatchBlock<T>. Cuando el objeto
BatchBlock<T> recibe el número especificado de elementos de entrada, propaga de forma asincrónica una
matriz que contiene esos elementos. Si un objeto BatchBlock<T> se establece en el estado completado pero no
contiene elementos suficientes para formar un lote, propaga una matriz final que contiene los elementos de
entrada restantes.
La clase BatchBlock<T> funciona en modo expansivo o no expansivo. En modo expansivo, que es el valor
predeterminado, un objeto BatchBlock<T> acepta cada mensaje que se proporciona y propaga una matriz
después de recibir el número especificado de elementos. En modo no expansivo, un objeto BatchBlock<T>
pospone todos los mensajes entrantes hasta que haya suficientes orígenes que proporcionen mensajes al
bloque para formar un lote. Normalmente, el modo expansivo se comporta mejor que el modo no expansivo
porque requiere menos sobrecarga de procesamiento. Sin embargo, se puede usar el modo no expansivo
cuando se debe coordinar el consumo de varios orígenes en modo atómico. Especifique el modo no expansivo
estableciendo Greedy en False en el parámetro dataflowBlockOptions del constructor BatchBlock<T>.
En el siguiente ejemplo básico se exponen varios valores Int32 a un objeto BatchBlock<T> que contiene diez
elementos en un lote. Para garantizar que todos los valores se propagan fuera de BatchBlock<T>, este ejemplo
llama al método Complete. El método Complete establece el objeto BatchBlock<T> en el estado completado y,
por consiguiente, el objeto BatchBlock<T> propaga cualquier elemento restante como un lote final.
// Create a BatchBlock<int> object that holds ten
// elements per batch.
var batchBlock = new BatchBlock<int>(10);
/* Output:
The sum of the elements in batch 1 is 45.
The sum of the elements in batch 2 is 33.
*/
' Output:
' The sum of the elements in batch 1 is 45.
' The sum of the elements in batch 2 is 33.
'
Para obtener un ejemplo completo que usa BatchBlock<T> para mejorar la eficacia de las operaciones de
inserción de la base de datos, vea Tutorial: Uso de BatchBlock y BatchedJoinBlock para mejorar la eficacia.
JoinBlock(T1, T2, ...)
Las clases JoinBlock<T1,T2> y JoinBlock<T1,T2,T3> obtienen elementos de entrada y propagan objetos
System.Tuple<T1,T2> o System.Tuple<T1,T2,T3> que contienen esos elementos. Las clases JoinBlock<T1,T2> y
JoinBlock<T1,T2,T3> no heredan de ITargetBlock<TInput>. En su lugar, proporcionan propiedades, Target1,
Target2 y Target3, que implementan ITargetBlock<TInput>.
Al igual que BatchBlock<T>, JoinBlock<T1,T2> y JoinBlock<T1,T2,T3> funcionan en modo expansivo o no
expansivo. En modo expansivo, que es el valor predeterminado, un objeto JoinBlock<T1,T2> o
JoinBlock<T1,T2,T3> acepta cada mensaje que se proporciona y propaga una tupla después de que cada uno de
sus destinos reciba por lo menos un mensaje. En modo no expansivo, un objeto JoinBlock<T1,T2> o
JoinBlock<T1,T2,T3> pospone todos los mensajes entrantes hasta que todos los destinos han proporcionado
los datos necesarios para crear una tupla. En este punto, el bloque se involucra en un protocolo de confirmación
en dos fases para recuperar atómicamente todos los elementos necesarios de los orígenes. Este aplazamiento
permite que, mientras tanto, otra entidad consuma datos, para permitir que el sistema global progrese.
En el siguiente ejemplo básico se muestra un caso en el que un objeto JoinBlock<T1,T2,T3> requiere varios
datos para calcular un valor. En este ejemplo se crea un objeto JoinBlock<T1,T2,T3> que requiere dos valores
Int32 y un valor Char para realizar una operación aritmética.
joinBlock.Target1.Post(3);
joinBlock.Target1.Post(6);
joinBlock.Target2.Post(5);
joinBlock.Target2.Post(4);
joinBlock.Target3.Post('+');
joinBlock.Target3.Post('-');
/* Output:
3 + 5 = 8
6 - 4 = 2
*/
' Create a JoinBlock<int, int, char> object that requires
' two numbers and an operator.
Dim joinBlock = New JoinBlock(Of Integer, Integer, Char)()
joinBlock.Target1.Post(3)
joinBlock.Target1.Post(6)
joinBlock.Target2.Post(5)
joinBlock.Target2.Post(4)
joinBlock.Target3.Post("+"c)
joinBlock.Target3.Post("-"c)
' Receive each group of values and apply the operator part
' to the number parts.
For i As Integer = 0 To 1
Dim data = joinBlock.Receive()
Select Case data.Item3
Case "+"c
Console.WriteLine("{0} + {1} = {2}", data.Item1, data.Item2, data.Item1 + data.Item2)
Case "-"c
Console.WriteLine("{0} - {1} = {2}", data.Item1, data.Item2, data.Item1 - data.Item2)
Case Else
Console.WriteLine("Unknown operator '{0}'.", data.Item3)
End Select
Next i
' Output:
' 3 + 5 = 8
' 6 - 4 = 2
'
Para obtener un ejemplo completo en donde se usan objetos JoinBlock<T1,T2> en modo no expansivo para
compartir conjuntamente un recurso, vea Procedimiento: Uso de JoinBlock para leer datos de varios orígenes.
BatchedJoinBlock(T1, T2, ...)
Las clases BatchedJoinBlock<T1,T2> y BatchedJoinBlock<T1,T2,T3> obtienen lotes de elementos de entrada y
propagan objetos System.Tuple(IList(T1), IList(T2)) o System.Tuple(IList(T1), IList(T2), IList(T3)) que
contienen esos elementos. Piense en BatchedJoinBlock<T1,T2> como una combinación de BatchBlock<T> y
JoinBlock<T1,T2>. Especifique el tamaño de cada lote cuando crea un objeto BatchedJoinBlock<T1,T2>.
BatchedJoinBlock<T1,T2> también proporciona propiedades, Target1 y Target2, que implementan
ITargetBlock<TInput>. Cuando el número especificado de elementos de entrada se recibe a través de todos los
destinos, el objeto BatchedJoinBlock<T1,T2> propaga de forma asincrónica un objeto
System.Tuple(IList(T1), IList(T2)) que contiene esos elementos.
En el siguiente ejemplo básico se crea un objeto BatchedJoinBlock<T1,T2> que contiene resultados, valores
Int32 y errores que son objetos Exception. En este ejemplo se realizan varias operaciones y se escriben los
resultados en la propiedad Target1, y los errores en la propiedad Target2, del objeto BatchedJoinBlock<T1,T2>.
Dado que el número de operaciones correctas y las que dieron error no se conoce de antemano, los objetos
IList<T> permiten que cada destino reciba cero o más valores.
// For demonstration, create a Func<int, int> that
// returns its argument, or throws ArgumentOutOfRangeException
// if the argument is less than zero.
Func<int, int> DoWork = n =>
{
if (n < 0)
throw new ArgumentOutOfRangeException();
return n;
};
/* Output:
5
6
13
55
0
Specified argument was out of the range of valid values.
Specified argument was out of the range of valid values.
*/
' For demonstration, create a Func<int, int> that
' returns its argument, or throws ArgumentOutOfRangeException
' if the argument is less than zero.
Dim DoWork As Func(Of Integer, Integer) = Function(n)
If n < 0 Then
Throw New ArgumentOutOfRangeException()
End If
Return n
End Function
' Output:
' 5
' 6
' 13
' 55
' 0
' Specified argument was out of the range of valid values.
' Specified argument was out of the range of valid values.
'
Para obtener un ejemplo completo que usa BatchedJoinBlock<T1,T2> para capturar resultados y cualquier
excepción que se produzca mientras el programa lee de una base de datos, vea Tutorial: Uso de BatchBlock y
BatchedJoinBlock para mejorar la eficacia.
BufferBlock<T> DataflowBlockOptions
BroadcastBlock<T> DataflowBlockOptions
WriteOnceBlock<T> DataflowBlockOptions
ActionBlock<TInput> ExecutionDataflowBlockOptions
TransformBlock<TInput,TOutput> ExecutionDataflowBlockOptions
TransformManyBlock<TInput,TOutput> ExecutionDataflowBlockOptions
BatchBlock<T> GroupingDataflowBlockOptions
JoinBlock<T1,T2> GroupingDataflowBlockOptions
BatchedJoinBlock<T1,T2> GroupingDataflowBlockOptions
Las secciones siguientes proporcionan información adicional sobre las clases importantes de opciones de
bloques de flujo de datos que están disponibles a través de las clases
System.Threading.Tasks.Dataflow.DataflowBlockOptions,
System.Threading.Tasks.Dataflow.ExecutionDataflowBlockOptions y
System.Threading.Tasks.Dataflow.GroupingDataflowBlockOptions.
Especificar el programador de tareas
Cada bloque de flujo de datos predefinido utiliza el mecanismo de programación de tareas de la biblioteca TPL
para realizar actividades como propagar datos a un destino, recibir datos de un origen y ejecutar delegados
definido por el usuario si los datos están disponibles. TaskScheduler es una clase abstracta que representa un
programador de tareas que pone en cola las tareas en los subprocesos. El programador de tareas
predeterminado, Default, utiliza la clase ThreadPool para poner en cola y ejecutar el trabajo. Puede reemplazar
el programador de tareas predeterminado estableciendo la propiedad TaskScheduler al crear un objeto de
bloque de flujo de datos.
Cuando el mismo programador de tareas administra varios bloques de flujo de datos, puede aplicar directivas
entre ellos. Por ejemplo, si cada uno de los bloques de flujo de datos se configuran como destino del
programador exclusivo del mismo objeto ConcurrentExclusiveSchedulerPair, todo el trabajo que se ejecuta a
través de estos bloques se serializa. De igual forma, si estos bloques se configuran como destino del
programador simultáneo del mismo objeto ConcurrentExclusiveSchedulerPair, y el programador se configura
para tener un nivel de simultaneidad máximo, todo el trabajo de estos bloques se limita a ese número de
operaciones simultáneas. Para obtener un ejemplo en donde se usa la clase ConcurrentExclusiveSchedulerPair
para permitir que las operaciones de lectura se produzcan en paralelo, pero las operaciones de escritura sean
exclusivas del resto de operaciones, vea Procedimiento: Especificación de un Programador de tareas en un
bloque de flujo de datos. Para obtener más información sobre los programadores de tareas en la biblioteca TPL,
consulte el tema sobre la clase TaskScheduler.
Especificar el grado de Paralelismo
De forma predeterminada, los tres tipos de bloques de ejecución que la biblioteca de flujos de datos TPL
proporciona, ActionBlock<TInput>, TransformBlock<TInput,TOutput> y TransformManyBlock<TInput,TOutput>,
procesan un mensaje al mismo tiempo. Estos tipos de bloques de flujo de datos también procesan mensajes en
el orden en que se reciben. Para permitir que estos bloques de flujo de datos procesen mensajes
simultáneamente, establezca la propiedad ExecutionDataflowBlockOptions.MaxDegreeOfParallelism cuando
construya el objeto de bloques de flujo de datos.
El valor predeterminado de MaxDegreeOfParallelism es 1, que garantiza que el bloque de flujo de datos procesa
un mensaje al mismo tiempo. Al establecer esta propiedad en un valor mayor de 1 se permite que el bloque de
flujo de datos procese varios mensajes simultáneamente. Al establecer esta propiedad en
DataflowBlockOptions.Unbounded se permite que el programador de tareas subyacente administre el grado
máximo de simultaneidad.
IMPORTANT
Cuando se especifica un grado máximo de paralelismo mayor que 1, varios mensajes se procesan simultáneamente y, por
tanto, los mensajes no se pueden procesar en el orden en que se reciben. Pero los mensajes salen del bloque en el mismo
orden en que se reciben.
Dado que la propiedad MaxDegreeOfParallelism representa el grado máximo de paralelismo, el bloque de flujo
de datos puede ejecutarse con un menor grado de paralelismo que el especificado. El bloque de flujo de datos
puede utilizar un menor grado de paralelismo para cumplir los requisitos funcionales o porque hay una falta de
recursos del sistema disponibles. Un flujo de datos bloqueado nunca elige más paralelismo que el especificado.
El valor de la propiedad MaxDegreeOfParallelism es exclusivo para cada objeto de bloque de flujo de datos. Por
ejemplo, si cuatro objetos de bloques de flujo de datos especifican 1 como el grado máximo de paralelismo, los
cuatro objetos de bloques de flujo de datos podrían ejecutarse en paralelo.
Para obtener un ejemplo en donde se establece el grado máximo de paralelismo que permite que se produzcan
operaciones largas en paralelo, vea Procedimiento: Especificación del grado de paralelismo en un bloque de
flujo de datos.
Especificar el número de mensajes por tarea
Los tipos predefinidos de bloques de flujo de datos utilizan tareas para procesar varios elementos de entrada.
Esto ayuda a minimizar el número de objetos de tarea necesarios para procesar datos, lo que permite que las
aplicaciones se ejecuten más eficazmente. Sin embargo, cuando las tareas de un conjunto de bloques de flujo de
datos están procesando datos, es posible que las tareas de otros bloques de flujo de datos tengan que esperar
el tiempo de procesamiento en la cola mensajes. Para permitir una mejor equidad entre tareas de flujo de datos,
establezca la propiedad MaxMessagesPerTask. Cuando MaxMessagesPerTask se establece en
DataflowBlockOptions.Unbounded, que es el valor predeterminado, la tarea utilizada por un bloque de flujo de
datos procesa tantos mensajes como están disponibles. Cuando MaxMessagesPerTask se establece en un valor
distinto de Unbounded, el bloque de flujo de datos procesa como máximo este número de mensajes por objeto
Task. Aunque al establecer la propiedad MaxMessagesPerTask se puede aumentar la equidad entre tareas, puede
provocar que el sistema cree más tareas que las necesarias, lo que puede reducir el rendimiento.
Habilitar la cancelación
La biblioteca TPL proporciona un mecanismo que habilita las tareas para coordinar la cancelación de manera
cooperativa. Para permitir que los bloques de flujo de datos puedan participar en este mecanismo de
cancelación, establezca la propiedad CancellationToken. Cuando este objeto CancellationToken se establece en el
estado cancelado, todos los bloques de flujo de datos que controlan este token finalizan la ejecución de su
elemento actual pero no comienzan a procesar los elementos siguientes. Estos bloques de flujo de datos
también borran los mensajes almacenados en búfer, conexiones de inicio para los bloques de origen y de
destino, y la transición al estado cancelado. Al realizar la transición al estado cancelado, la propiedad
Completion tiene la propiedad Status establecida en Canceled, a menos que se produzca una excepción durante
el procesamiento. En ese caso, Status se establece en Faulted.
Para obtener un ejemplo en donde se muestra cómo usar la cancelación en una aplicación de Windows Forms,
vea Procedimiento: Cómo: Cancelar un bloque de flujos de datos. Para más información sobre la cancelación en
la biblioteca TPL, consulte Task Cancellation (Cancelación de tareas).
Especificar el comportamiento expansivo frente al no expansivo
Varios tipos de bloques de flujo de datos de agrupación pueden trabajar en modo expansivo o no expansivo. De
forma predeterminada, los tipos de bloques de flujo de datos predefinidos funcionan en modo expansivo.
Para los tipos de bloques de combinación como JoinBlock<T1,T2>, el modo expansivo significa que el bloque
acepta datos inmediatamente aunque los correspondientes datos con los que se combinará aún no estén
disponibles. El modo no expansivo significa que el bloque pospone todos los mensajes entrantes hasta que uno
esté disponible para cada uno de sus destinos para completar la combinación. Si los mensajes pospuestos ya no
están disponibles, el bloque de combinación libera todos los mensajes pospuestos y reinicia el proceso. Para la
clase BatchBlock<T>, el comportamiento expansivo y no expansivo es similar, salvo que en modo no expansivo,
un objeto BatchBlock<T> pospone todos los mensajes entrantes hasta que haya suficientes mensajes
disponibles de orígenes distintos para completar un lote.
Para especificar el modo no expansivo para un bloque de flujo de datos, establezca Greedy en False . Para
obtener un ejemplo en donde se muestra cómo usar el modo no expansivo para permitir que varios bloques de
combinación compartan un origen de datos con mayor eficacia, vea Procedimiento: Uso de JoinBlock para leer
datos de varios orígenes.
Temas relacionados
T IT L E DESC RIP C IÓ N
Cómo: Escritura y lectura de mensajes en un bloque de flujo Muestra cómo escribir y leer los mensajes de un objeto
de datos BufferBlock<T>.
Cómo: Implementación de un modelo de flujo de datos Describe cómo utilizar el modelo de flujo de datos para
productor-consumidor implementar un patrón consumidor-productor, cuando el
productor envía mensajes a un bloque de flujo de datos y el
consumidor lee mensajes de ese bloque.
Cómo: Ejecución de una acción cuando un bloque de flujo de Describe cómo proporcionar delegados a los tipos de
datos recibe datos bloques de flujo de datos de ejecución,
ActionBlock<TInput>, TransformBlock<TInput,TOutput> y
TransformManyBlock<TInput,TOutput>.
Tutorial: Creación de una canalización de flujos de datos Describe cómo crear una canalización de flujo de datos que
descarga texto desde Internet y realiza operaciones en ese
texto.
Cómo: Desvinculación de bloques de flujo de datos Muestra cómo utilizar el método LinkTo para desvincular un
bloque de destino de su origen después de que el origen
proporciona un mensaje al destino.
Tutorial: Uso de flujos de datos en aplicaciones de Muestra cómo crear una red de bloques de flujo de datos
Windows Forms que realizan procesamiento de imágenes en una aplicación
de Windows Forms.
T IT L E DESC RIP C IÓ N
Cómo: Cancelación de un bloque de flujo de datos Muestra cómo se usa la cancelación en una aplicación de
Windows Forms.
Cómo: Uso de JoinBlock para leer datos de diferentes Explica cómo utilizar la clase JoinBlock<T1,T2> para realizar
orígenes una operación cuando los datos están disponibles a partir
de varios orígenes, y cómo utilizar el modo no expansivo
para permitir que varios bloques de combinación puedan
compartir un origen de datos más eficazmente.
Cómo: Especificación del grado de paralelismo en un bloque Describe cómo establecer la propiedad
de flujo de datos MaxDegreeOfParallelism para permitir que un bloque de
flujo de datos de ejecución pueda procesar varios mensajes
al mismo tiempo.
Cómo: Especificación de un Programador de tareas en un Muestra cómo asociar un programador de tareas específico
bloque de flujo de datos cuando se usa flujo de datos en la aplicación.
Tutorial: Uso de BatchBlock y BatchedJoinBlock para mejorar Describe cómo utilizar la clase BatchBlock<T> para mejorar
la eficacia la eficacia de las operaciones de inserción de la base de
datos y cómo utilizar la clase BatchedJoinBlock<T1,T2> para
capturar los resultados y cualquier excepción que se
produzca mientras el programa lee de una base de datos.
Tutorial: Creación de tipos de bloques de flujo de datos Muestra dos maneras de crear un tipo de bloque de flujo de
personalizados datos que implementa un comportamiento personalizado.
En este documento se describe cómo usar la biblioteca de flujos de datos TPL para escribir y leer mensajes en un
bloque de flujo de datos. La biblioteca de flujos de datos TPL proporciona métodos sincrónicos y asincrónicos para
escribir y leer mensajes en un bloque de flujo de datos. Este documento usa la clase
System.Threading.Tasks.Dataflow.BufferBlock<T>. La clase BufferBlock<T> almacena mensajes en búfer y se
comporta como origen y destino de los mismos.
NOTE
La biblioteca de flujos de datos TPL (el espacio de nombres System.Threading.Tasks.Dataflow) no se distribuye con .NET. Para
instalar el espacio de nombres System.Threading.Tasks.Dataflow en Visual Studio, abra el proyecto, seleccione Administrar
paquetes NuGet en el menú Proyecto y busque en línea el paquete System.Threading.Tasks.Dataflow . Como
alternativa, para realizar la instalación con la CLI de .Net Core, ejecute
dotnet add package System.Threading.Tasks.Dataflow .
// Output:
// 0
// 1
// 2
Dim bufferBlock = New BufferBlock(Of Integer)()
' Output:
' 0
' 1
' 2
También puede usar el método TryReceive para leer desde un bloque de flujo de datos, como se muestra en el
ejemplo siguiente. El método TryReceive no bloquea el subproceso actual y es útil cuando se realizan sondeos
ocasionales de los datos.
// Output:
// 0
// 1
// 2
' Output:
' 0
' 1
' 2
Dado que el método Post actúa de forma sincrónica, el objeto BufferBlock<T> de los ejemplos anteriores recibe
todos los datos antes de que el segundo bucle los lea. En el siguiente ejemplo se amplía el primer ejemplo
mediante Invoke para leer y escribir en el bloque de mensajes simultáneamente. Dado que Invoke realiza acciones
al mismo tiempo, los valores no se escriben en el objeto BufferBlock<T> en un orden específico.
// Write to and read from the message block concurrently.
var post01 = Task.Run(() =>
{
bufferBlock.Post(0);
bufferBlock.Post(1);
});
var receive = Task.Run(() =>
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine(bufferBlock.Receive());
}
});
var post2 = Task.Run(() =>
{
bufferBlock.Post(2);
});
// Output:
// 0
// 1
// 2
' Output:
' 0
' 1
' 2
// Output:
// 0
// 1
// 2
' Output:
' 0
' 1
' 2
Un ejemplo completo
En el siguiente ejemplo se muestra el código completo de este documento.
using System;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
// Output:
// 0
// 1
// 2
}
static async Task Main()
{
var bufferBlock = new BufferBlock<int>();
// Output:
// 0
// 1
// 2
// Output:
// 0
// 1
// 2
// Output:
// 0
// 1
// 2
' Output:
' 0
' 1
' 2
End Function
' Output:
' 0
' 1
' 2
' Output:
' 0
' 1
' 2
' Output:
' 0
' 1
' 2
End Class
Pasos siguientes
En este ejemplo se muestra cómo leer y escribir directamente en un bloque de mensajes. También puede conectar
los bloques de flujo de datos para establecer canalizaciones, que son secuencias lineales de bloques de flujo de
datos, o redes, que son gráficos de bloques de flujo de datos. En una canalización o red, los orígenes propagan
datos de forma asincrónica en destinos a medida que esos datos están disponibles. Para obtener un ejemplo en el
que se cree una canalización de flujo de datos básica, consulte Walkthrough: Creating a Dataflow Pipeline (Tutorial:
Crear una canalización de flujo de datos). Para obtener un ejemplo en el que se cree una red de flujo de datos más
compleja, consulte Walkthrough: Using Dataflow in a Windows Forms Application (Tutorial: Usar el flujo de datos
en una aplicación de Windows Forms).
Vea también
Flujo de datos
Procedimiento Implementación de un modelo de
flujo de datos productor-consumidor
16/09/2020 • 7 minutes to read • Edit Online
Este documento describe cómo utilizar la biblioteca de flujos de datos TPL para implementar un modelo productor-
consumidor. En este modelo, el productor envía mensajes a un bloque de mensajes y el consumidor lee los
mensajes de este bloque.
NOTE
La biblioteca de flujos de datos TPL (el espacio de nombres System.Threading.Tasks.Dataflow) no se distribuye con .NET. Para
instalar el espacio de nombres System.Threading.Tasks.Dataflow en Visual Studio, abra el proyecto, seleccione Administrar
paquetes NuGet en el menú Proyecto y busque en línea el paquete System.Threading.Tasks.Dataflow . Como
alternativa, para realizar la instalación con la CLI de .Net Core, ejecute
dotnet add package System.Threading.Tasks.Dataflow .
Ejemplo
En el ejemplo siguiente se muestra un modelo productor-consumidor básico que usa el flujo de datos. El método
Produce escribe matrices que contienen bytes de datos aleatorios en un objeto
System.Threading.Tasks.Dataflow.ITargetBlock<TInput> y el método Consume lee los bytes de un objeto
System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>. Al actuar en las interfaces ISourceBlock<TOutput> y
ITargetBlock<TInput>, en lugar de en sus tipos derivados, puede escribir código reutilizable que puede actuar en
una variedad de tipos de bloques de flujo de datos. Este ejemplo utiliza la clase BufferBlock<T>. Puesto que la clase
BufferBlock<T> actúa como origen y como un bloque de origen y destino, el productor y el consumidor pueden
utilizar un objeto compartido para transferir datos.
El método Produce llama al método Post en un bucle para escribir datos de forma sincrónica en el bloque de
destino. Después de que el método Produce escriba todos los datos en el bloque de destino, llama al método
Complete para indicar que el bloque nunca tendrá datos adicionales disponibles. El método Consume usa los
operadores async y await (Async y Await en Visual Basic) para calcular de forma asincrónica el número total de
bytes recibidos del objeto ISourceBlock<TOutput>. Para que actúe de forma asincrónica, el método Consume llama
al método OutputAvailableAsync para recibir una notificación cuando el bloque de origen tiene datos disponibles y
cuando el bloque de origen nunca va a tener datos adicionales disponibles.
using System;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
// Read from the source buffer until the source buffer has no
// available output data.
while (await source.OutputAvailableAsync())
{
byte[] data = source.Receive();
return bytesProcessed;
}
/* Output:
Processed 102400 bytes.
*/
Imports System.Threading.Tasks
Imports System.Threading.Tasks.Dataflow
' Demonstrates a basic producer and consumer pattern that uses dataflow.
Friend Class DataflowProducerConsumer
' Demonstrates the production end of the producer and consumer pattern.
Private Shared Sub Produce(ByVal target As ITargetBlock(Of Byte()))
' Create a Random object to generate random data.
Dim rand As New Random()
' In a loop, fill a buffer with random data and
' post the buffer to the target block.
For i As Integer = 0 To 99
' Create an array to hold random byte data.
Dim buffer(1023) As Byte
' Set the target to the completed state to signal to the consumer
' that no more data will be available.
target.Complete()
End Sub
' Demonstrates the consumption end of the producer and consumer pattern.
Private Shared async Function ConsumeAsync(ByVal source As ISourceBlock(Of Byte())) As Task(Of Integer)
' Initialize a counter to track the number of bytes that are processed.
Dim bytesProcessed As Integer = 0
' Read from the source buffer until the source buffer has no
' available output data.
Do While await source.OutputAvailableAsync()
Dim data() As Byte = source.Receive()
Return bytesProcessed
End Function
' Output:
'Processed 102400 bytes.
'
Programación sólida
En el ejemplo anterior se usa un solo consumidor para procesar los datos de origen. Si tiene varios consumidores
en la aplicación, use el método TryReceive para leer datos desde el bloque de origen, como se muestra en el
siguiente ejemplo.
// Demonstrates the consumption end of the producer and consumer pattern.
static async Task<int> ConsumeAsync(IReceivableSourceBlock<byte[]> source)
{
// Initialize a counter to track the number of bytes that are processed.
int bytesProcessed = 0;
// Read from the source buffer until the source buffer has no
// available output data.
while (await source.OutputAvailableAsync())
{
byte[] data;
while (source.TryReceive(out data))
{
// Increment the count of bytes received.
bytesProcessed += data.Length;
}
}
return bytesProcessed;
}
' Demonstrates the consumption end of the producer and consumer pattern.
Private Shared async Function ConsumeAsync(ByVal source As IReceivableSourceBlock(Of Byte())) As Task(Of
Integer)
' Initialize a counter to track the number of bytes that are processed.
Dim bytesProcessed As Integer = 0
' Read from the source buffer until the source buffer has no
' available output data.
Do While await source.OutputAvailableAsync()
Dim data() As Byte
Do While source.TryReceive(data)
' Increment the count of bytes received.
bytesProcessed += data.Length
Loop
Loop
Return bytesProcessed
End Function
El método TryReceive devuelve False cuando no hay datos disponibles. Cuando varios consumidores deben tener
acceso simultáneamente al bloque de origen, este mecanismo garantiza que los datos están disponibles después
de la llamada a OutputAvailableAsync.
Vea también
Flujo de datos
Cómo: Realizar una acción cuando un bloque de
flujos de datos recibe datos
16/09/2020 • 9 minutes to read • Edit Online
Los tipos Bloque de flujo de datos de ejecución llaman a un delegado proporcionado por el usuario cuando
reciben datos. Las clases System.Threading.Tasks.Dataflow.ActionBlock<TInput>,
System.Threading.Tasks.Dataflow.TransformBlock<TInput,TOutput> y
System.Threading.Tasks.Dataflow.TransformManyBlock<TInput,TOutput> son tipos de bloques de flujo de datos de
ejecución. Puede usar la palabra clave delegate ( Sub en Visual Basic), Action<T>, Func<T,TResult> o una
expresión lambda cuando proporcione una función de trabajo a un bloque de flujo de datos de ejecución. Este
documento describe cómo usar Func<T,TResult> y expresiones lambda para realizar la acción en bloques de
ejecución.
NOTE
La biblioteca de flujos de datos TPL (el espacio de nombres System.Threading.Tasks.Dataflow) no se distribuye con .NET. Para
instalar el espacio de nombres System.Threading.Tasks.Dataflow en Visual Studio, abra el proyecto, seleccione Administrar
paquetes NuGet en el menú Proyecto y busque en línea el paquete System.Threading.Tasks.Dataflow . Como
alternativa, para realizar la instalación con la CLI de .Net Core, ejecute
dotnet add package System.Threading.Tasks.Dataflow .
Ejemplo
En el ejemplo siguiente se usa el flujo de datos para leer un archivo del disco y se calcula el número de bytes en el
archivo que son iguales a cero. Usa TransformBlock<TInput,TOutput> para leer el archivo y calcular el número de
cero bytes, y ActionBlock<TInput> para imprimir el número de cero bytes en la consola. El objeto
TransformBlock<TInput,TOutput> especifica un objeto Func<T,TResult> para realizar el trabajo cuando los
bloques reciben datos. El objeto ActionBlock<TInput> usa una expresión lambda para imprimir en la consola el
número de cero bytes que se lee.
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
return totalZeroBytesRead;
}
/* Sample output:
tmp4FBE.tmp contains 2081 zero bytes.
*/
Imports System.IO
Imports System.Linq
Imports System.Threading.Tasks
Imports System.Threading.Tasks.Dataflow
Return totalZeroBytesRead
End Function
Programación sólida
En este ejemplo se proporcionada un delegado de tipo Func<T,TResult> para el objeto
TransformBlock<TInput,TOutput>, con el fin de realizar la tarea del bloque de flujo de datos de forma sincrónica.
Para que el bloque de flujo de datos se comporte de forma asincrónica, proporcione un delegado de tipo
Func<TResult> al bloque de flujo de datos. Cuando un bloque de flujo de datos se comporta de forma
asincrónica, la tarea del bloque de flujo de datos se completa solo cuando el objeto Task<TResult> devuelto
finaliza. En el ejemplo siguiente se modifica el método CountBytes y se usan los operadores async y await (Async
y Await en Visual Basic) para calcular de forma asincrónica el número total de bytes que son cero en el archivo
proporcionado. El método ReadAsync realiza las operaciones de lectura de archivo de forma asincrónica.
// Asynchronously computes the number of zero bytes that the provided file
// contains.
static async Task<int> CountBytesAsync(string path)
{
byte[] buffer = new byte[1024];
int totalZeroBytesRead = 0;
using (var fileStream = new FileStream(
path, FileMode.Open, FileAccess.Read, FileShare.Read, 0x1000, true))
{
int bytesRead = 0;
do
{
// Asynchronously read from the file stream.
bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length);
totalZeroBytesRead += buffer.Count(b => b == 0);
} while (bytesRead > 0);
}
return totalZeroBytesRead;
}
' Asynchronously computes the number of zero bytes that the provided file
' contains.
Private Shared async Function CountBytesAsync(ByVal path As String) As Task(Of Integer)
Dim buffer(1023) As Byte
Dim totalZeroBytesRead As Integer = 0
Using fileStream = New FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, &H1000, True)
Dim bytesRead As Integer = 0
Do
' Asynchronously read from the file stream.
bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)
totalZeroBytesRead += buffer.Count(Function(b) b = 0)
Loop While bytesRead > 0
End Using
Return totalZeroBytesRead
End Function
También puede utilizar expresiones lambda asincrónicas para realizar la acción en un bloque de flujo de datos de
ejecución. En el ejemplo siguiente se modifica el objeto TransformBlock<TInput,TOutput> que se utiliza en el
ejemplo anterior para que utilice una expresión lambda para realizar el trabajo de forma asincrónica.
return totalZeroBytesRead;
});
Vea también
Flujo de datos
Tutorial: Creación de una canalización de flujos de
datos
16/09/2020 • 19 minutes to read • Edit Online
Requisitos previos
Lea Flujo de datos antes de empezar este tutorial.
NOTE
La biblioteca de flujos de datos TPL (el espacio de nombres System.Threading.Tasks.Dataflow) no se distribuye con .NET. Para
instalar el espacio de nombres System.Threading.Tasks.Dataflow en Visual Studio, abra el proyecto, seleccione Administrar
paquetes NuGet en el menú Proyecto y busque en línea el paquete System.Threading.Tasks.Dataflow . Como
alternativa, para realizar la instalación con la CLI de .Net Core, ejecute
dotnet add package System.Threading.Tasks.Dataflow .
Agregue el código siguiente a su proyecto para crear la aplicación básica.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks.Dataflow;
Imports System.Net.Http
Imports System.Threading.Tasks.Dataflow
Sub Main()
End Sub
End Module
return words
.Where(word => word.Length > 3)
.Distinct()
.ToArray();
});
' Finds all words in the specified collection whose reverse also
' exists in the collection.
Dim findReversedWords = New TransformManyBlock(Of String(), String)(
Function(words)
Aunque se podrían combinar varios pasos de la canalización de flujo de datos de este ejemplo en un solo paso,
en el ejemplo se muestra el concepto de componer varias tareas de flujo de datos independientes para realizar
una tarea mayor. En el ejemplo se usa TransformBlock<TInput,TOutput> para permitir que cada miembro de la
canalización pueda realizar una operación en sus datos de entrada y enviar los resultados al siguiente paso de la
canalización. El miembro findReversedWords de la canalización es un objeto
TransformManyBlock<TInput,TOutput> porque genera varias salidas independientes para cada entrada. El final
de la canalización, printReversedWords , es un objeto ActionBlock<TInput> porque realiza una acción en su
entrada y no genera una salida.
Formación de la canalización
Agregue el código siguiente para conectar cada bloque con el bloque siguiente en la canalización.
Cuando se llama al método LinkTo para conectar un bloque de flujo de datos de origen a un bloque de flujo de
datos de destino, el bloque de origen propaga los datos al bloque de destino a medida que los datos se
encuentran disponibles. Si también proporciona DataflowLinkOptions con PropagateCompletion establecido en
true, la finalización correcta o incorrecta de un bloque de la canalización conllevará la finalización del siguiente
bloque de la canalización.
//
// Connect the dataflow blocks to form a pipeline.
//
downloadString.LinkTo(createWordList, linkOptions);
createWordList.LinkTo(filterWordList, linkOptions);
filterWordList.LinkTo(findReversedWords, linkOptions);
findReversedWords.LinkTo(printReversedWords, linkOptions);
'
' Connect the dataflow blocks to form a pipeline.
'
downloadString.LinkTo(createWordList, linkOptions)
createWordList.LinkTo(filterWordList, linkOptions)
filterWordList.LinkTo(findReversedWords, linkOptions)
findReversedWords.LinkTo(printReversedWords, linkOptions)
En este ejemplo se usa DataflowBlock.Post para enviar datos de forma sincrónica al encabezado de la
canalización. Use el método DataflowBlock.SendAsync cuando deba enviar datos de forma asincrónica a un nodo
de flujo de datos.
En este ejemplo se envía una dirección URL a través de la canalización de flujo de datos para que se procese. Si
envía más de una entrada a través de una canalización, llame al método IDataflowBlock.Complete después de
enviar toda la entrada. Puede omitir este paso si la aplicación no tiene ningún punto bien definido en el que los
datos ya no están disponibles o la aplicación no tiene que esperar a que finalice la canalización.
// Wait for the last block in the pipeline to process all messages.
printReversedWords.Completion.Wait();
' Wait for the last block in the pipeline to process all messages.
printReversedWords.Completion.Wait()
Puede esperar la finalización del flujo de datos desde cualquier subproceso o desde varios subprocesos al mismo
tiempo.
Ejemplo completo
En el ejemplo siguiente se muestra el código completo de este tutorial.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks.Dataflow;
using System.Threading.Tasks.Dataflow;
return words
.Where(word => word.Length > 3)
.Distinct()
.ToArray();
});
//
// Connect the dataflow blocks to form a pipeline.
// Connect the dataflow blocks to form a pipeline.
//
downloadString.LinkTo(createWordList, linkOptions);
createWordList.LinkTo(filterWordList, linkOptions);
filterWordList.LinkTo(findReversedWords, linkOptions);
findReversedWords.LinkTo(printReversedWords, linkOptions);
// Wait for the last block in the pipeline to process all messages.
printReversedWords.Completion.Wait();
}
}
/* Sample output:
Downloading 'http://www.gutenberg.org/cache/epub/16452/pg16452.txt'...
Creating word list...
Filtering word list...
Finding reversed words...
Found reversed words doom/mood
Found reversed words draw/ward
Found reversed words aera/area
Found reversed words seat/taes
Found reversed words live/evil
Found reversed words port/trop
Found reversed words sleek/keels
Found reversed words area/aera
Found reversed words tops/spot
Found reversed words evil/live
Found reversed words mood/doom
Found reversed words speed/deeps
Found reversed words moor/room
Found reversed words trop/port
Found reversed words spot/tops
Found reversed words spots/stops
Found reversed words stops/spots
Found reversed words reed/deer
Found reversed words keels/sleek
Found reversed words deeps/speed
Found reversed words deer/reed
Found reversed words taes/seat
Found reversed words room/moor
Found reversed words ward/draw
*/
Imports System.Net.Http
Imports System.Threading.Tasks.Dataflow
Sub Main()
'
' Create the members of the pipeline.
'
' Finds all words in the specified collection whose reverse also
' exists in the collection.
Dim findReversedWords = New TransformManyBlock(Of String(), String)(
Function(words)
'
' Connect the dataflow blocks to form a pipeline.
'
downloadString.LinkTo(createWordList, linkOptions)
createWordList.LinkTo(filterWordList, linkOptions)
filterWordList.LinkTo(findReversedWords, linkOptions)
findReversedWords.LinkTo(printReversedWords, linkOptions)
' Wait for the last block in the pipeline to process all messages.
printReversedWords.Completion.Wait()
End Sub
End Module
' Sample output:
'Downloading 'http://www.gutenberg.org/cache/epub/16452/pg16452.txt'...
'Creating word list...
'Filtering word list...
'Finding reversed words...
'Found reversed words aera/area
'Found reversed words doom/mood
'Found reversed words draw/ward
'Found reversed words live/evil
'Found reversed words seat/taes
'Found reversed words area/aera
'Found reversed words port/trop
'Found reversed words sleek/keels
'Found reversed words tops/spot
'Found reversed words evil/live
'Found reversed words speed/deeps
'Found reversed words mood/doom
'Found reversed words moor/room
'Found reversed words spot/tops
'Found reversed words spots/stops
'Found reversed words trop/port
'Found reversed words stops/spots
'Found reversed words reed/deer
'Found reversed words deeps/speed
'Found reversed words deer/reed
'Found reversed words taes/seat
'Found reversed words keels/sleek
'Found reversed words room/moor
'Found reversed words ward/draw
Pasos siguientes
En este ejemplo se envía una dirección URL para procesarla a través de la canalización de flujo de datos. Si envía
más de un valor de entrada a través de una canalización, puede introducir un formulario de paralelismo en la
aplicación que se parezca a cómo se moverían las piezas en una fábrica de automóviles. Cuando el primer
miembro de la canalización envía su resultado al segundo miembro, puede procesar otro elemento en paralelo
mientras el segundo miembro procesa el primer resultado.
El paralelismo que se logra mediante el uso de canalizaciones de flujo de datos se conoce como paralelismo
general porque normalmente consta de menos tareas y más grandes. También puede usar un paralelismo
específico de tareas más pequeñas y breves en una canalización de flujo de datos. En este ejemplo, el miembro
findReversedWords de la canalización usa PLINQ para procesar en paralelo varios elementos de la lista de trabajo.
El uso de paralelismo de grano fino en una canalización de grano grueso puede mejorar el rendimiento global.
También puede conectar un bloque de flujo de datos de origen a varios bloques de destino para crear una red de
flujo de datos. La versión sobrecargada del método LinkTo toma un objeto Predicate<T> que define si el bloque
de destino acepta cada mensaje según su valor. La mayoría de los tipos de bloques de flujo de datos que actúan
como orígenes ofrecen mensajes a todos los bloques de destino conectados, siguiendo el orden en que se
conectaron, hasta que uno de los bloques acepta ese mensaje. Mediante este mecanismo de filtrado, puede crear
sistemas de bloques de flujo de datos conectados que dirigen determinados datos a través de una ruta de acceso
y otros datos a través de otra ruta de acceso. Para ver un ejemplo que usa el filtrado para crear una red de flujo
de datos, consulte Tutorial: Uso de flujos de datos en aplicaciones de Windows Forms.
Vea también
Flujo de datos
Cómo: Desvincular bloques de flujos de datos
16/09/2020 • 6 minutes to read • Edit Online
En este documento se describe cómo desvincular un bloque de flujo de datos de destino de su origen.
NOTE
La biblioteca de flujos de datos TPL (el espacio de nombres System.Threading.Tasks.Dataflow) no se distribuye con .NET. Para
instalar el espacio de nombres System.Threading.Tasks.Dataflow en Visual Studio, abra el proyecto, seleccione Administrar
paquetes NuGet en el menú Proyecto y busque en línea el paquete System.Threading.Tasks.Dataflow . Como
alternativa, para realizar la instalación con la CLI de .Net Core, ejecute
dotnet add package System.Threading.Tasks.Dataflow .
Ejemplo
En el ejemplo siguiente se crean tres objetos TransformBlock<TInput,TOutput>, y cada uno de ellos llama al
método TrySolution para calcular un valor. Este ejemplo requiere solo el resultado de la primera llamada a
TrySolution para finalizar.
using System;
using System.Threading;
using System.Threading.Tasks.Dataflow;
// Return a value.
return n + 42;
}
cts.Dispose();
}
}
/* Sample output:
The solution is 53.
*/
Imports System.Threading
Imports System.Threading.Tasks.Dataflow
' Call the ReceiveFromAny<T> method to receive the result from the
' first TransformBlock<int, int> object to finish.
Dim result As Integer = ReceiveFromAny(trySolution1, trySolution2, trySolution3)
cts.Dispose()
End Sub
End Class
Para recibir el valor del primer objeto TransformBlock<TInput,TOutput> que termina, este ejemplo define el
método ReceiveFromAny(T) . El método ReceiveFromAny(T) acepta una matriz de objetos ISourceBlock<TOutput> y
vincula cada uno de estos objetos a un objeto WriteOnceBlock<T>. Cuando se usa el método LinkTo para vincular
un bloque de flujo de datos de origen a un bloque de destino, el origen propaga mensajes al destino a medida
que los datos están disponibles. Dado que la clase WriteOnceBlock<T> acepta solo el primer mensaje que se
ofrece, el método ReceiveFromAny(T) genera su resultado mediante una llamada al método Receive. Esto produce
el primer mensaje que se ofrece al objeto WriteOnceBlock<T>. El método LinkTo tiene una versión sobrecargada
que adopta un objeto DataflowLinkOptions con una propiedad MaxMessages que, cuando se establece en 1 ,
indica al bloque de origen que se desvincule del destino después de que el destino reciba un mensaje del origen.
Es importante que el objeto WriteOnceBlock<T> se desvincule de sus orígenes porque la relación entre la matriz
de orígenes y el objeto WriteOnceBlock<T> ya no es necesaria una vez que el objeto WriteOnceBlock<T> recibe
un mensaje.
Para habilitar las llamadas restantes a TrySolution para terminar una vez que una de ellas calcula un valor, el
método TrySolution adopta un objeto CancellationToken que se cancela después de llamar a las devoluciones
ReceiveFromAny(T) . El método SpinUntil realiza la devolución cuando este objeto CancellationToken se cancela.
Vea también
Flujo de datos
Tutorial: Usar flujos de datos en aplicaciones de
Windows Forms
16/09/2020 • 22 minutes to read • Edit Online
Este documento muestra cómo crear una red de bloques de flujo de datos que realizan el procesamiento de
imágenes en una aplicación de Windows Forms.
En este ejemplo se cargan archivos de imagen de la carpeta especificada, se crea una imagen compuesta y se
muestra el resultado. En el ejemplo se utiliza el modelo de flujo de datos para distribuir las imágenes por la red.
En el modelo de flujo de datos, los componentes independientes de un programa se comunican entre sí
mediante mensajes. Cuando un componente recibe un mensaje, realiza alguna acción y pasa el resultado a otro
componente. Compare esto con el modelo de flujo de control, en el que una aplicación utiliza estructuras de
control, como por ejemplo, instrucciones condicionales, bucles, etc., para controlar el orden de las operaciones
en un programa.
Requisitos previos
Lea Flujo de datos antes de empezar este tutorial.
NOTE
La biblioteca de flujos de datos TPL (el espacio de nombres System.Threading.Tasks.Dataflow) no se distribuye con .NET.
Para instalar el espacio de nombres System.Threading.Tasks.Dataflow en Visual Studio, abra el proyecto, seleccione
Administrar paquetes NuGet en el menú Proyecto y busque en línea el paquete
System.Threading.Tasks.Dataflow . Como alternativa, para realizar la instalación con la CLI de .Net Core, ejecute
dotnet add package System.Threading.Tasks.Dataflow .
Secciones
Este tutorial contiene las siguientes secciones:
Crear una aplicación de Windows Forms
Crear la red del flujo de datos
Conectar la red del flujo de datos a la interfaz de usuario
Ejemplo completo
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
4. Agregue el método siguiente, CreateImageProcessingNetwork , a la clase Form1 . Este método crea la red
de procesamiento de imágenes.
// Create a dataflow block that displays the provided bitmap on the form.
var displayCompositeBitmap = new ActionBlock<Bitmap>(bitmap =>
{
// Display the bitmap.
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.Image = bitmap;
//
// Connect the network.
//
try
{
// Add the Bitmap object to the collection.
bitmaps.Add(new Bitmap(fileName));
}
catch (Exception)
{
// TODO: A complete application might handle the error.
}
}
}
return bitmaps;
}
unsafe
{
// Compute the average of each color component.
a /= count;
a /= count;
r /= count;
g /= count;
b /= count;
NOTE
La versión de C# del método CreateCompositeBitmap utiliza punteros para permitir un procesamiento eficaz de
los objetos System.Drawing.Bitmap. Por lo tanto, debe habilitar la opción Permitir código no seguro en el
proyecto para utilizar la palabra clave unsafe. Para obtener más información sobre cómo habilitar el código no
seguro en un proyecto de Visual C#, vea Compilar (Página, Diseñador de proyectos) (C#).
M EM B ER T IP O DESC RIP T IO N
Para conectar los bloques de flujo de datos para formar una red, este ejemplo usa el método LinkTo. El método
LinkTo contiene una versión sobrecargada que adopta un objeto Predicate<T> que determina si el bloque de
destino acepta o rechaza un mensaje. Este mecanismo de filtrado permite que los bloques de mensajes reciban
solo ciertos valores. En este ejemplo, la red puede dividirse en ramas de una de dos maneras. La rama principal
carga las imágenes desde el disco, crea la imagen compuesta y la muestra en el formulario. La rama alternativa
cancela la operación actual. Los objetos Predicate<T> permiten que los bloques de flujo de datos en la rama
principal cambien a la rama alternativa al rechazar determinados mensajes. Por ejemplo, si el usuario cancela la
operación, el bloque de flujo de datos createCompositeBitmap produce null ( Nothing en Visual Basic) como
salida. El bloque de flujo de datos displayCompositeBitmap rechaza valores de entrada null y, por lo tanto, el
mensaje se ofrece a operationCancelled . El bloque de flujo de datos operationCancelled acepta todos los
mensajes y, por lo tanto, muestra una imagen para indicar que se ha cancelado la operación.
En la ilustración siguiente se muestra la red de procesamiento de imágenes:
Dado que los bloques de flujo de datos displayCompositeBitmap y operationCancelled actúan sobre la interfaz
de usuario, es importante que esta acción se produzca en el subproceso de interfaz de usuario. Para lograrlo,
durante la construcción, cada uno de estos objetos proporciona un objeto ExecutionDataflowBlockOptions que
tiene la propiedad TaskScheduler establecida como TaskScheduler.FromCurrentSynchronizationContext. El
método TaskScheduler.FromCurrentSynchronizationContext crea un objeto TaskScheduler que funciona en el
contexto de sincronización actual. Como se llama al método CreateImageProcessingNetwork desde el controlador
del botón Elegir carpeta , que se ejecuta en el subproceso de la interfaz de usuario, las acciones para los
bloques de flujo de datos displayCompositeBitmap y operationCancelled también se ejecutan en el subproceso
de la interfaz de usuario.
Este ejemplo usa un token de cancelación compartido en lugar de establecer la propiedad CancellationToken,
porque la propiedad CancellationToken cancela permanentemente la ejecución del bloque de flujo de datos. En
este ejemplo, un token de cancelación permite reutilizar la misma red del flujo de datos varias veces, incluso
cuando el usuario cancela una o varias operaciones. Para obtener un ejemplo que usa CancellationToken para
cancelar la ejecución de un bloque de flujo de datos de modo permanente, vea Cómo: Cancelar un bloque de
flujos de datos.
// Enable the Cancel button and disable the Choose Folder button.
toolStripButton1.Enabled = false;
toolStripButton2.Enabled = true;
3. En el diseñador de formularios del formulario principal, cree un controlador de eventos para el evento
Click del botón Cancelar .
4. Implemente el evento Click del botón Cancelar .
Ejemplo completo
En el ejemplo siguiente se muestra el código completo de este tutorial.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
namespace CompositeImages
{
public partial class Form1 : Form
{
// The head of the dataflow network.
ITargetBlock<string> headBlock = null;
public Form1()
{
InitializeComponent();
}
// Create a dataflow block that displays the provided bitmap on the form.
var displayCompositeBitmap = new ActionBlock<Bitmap>(bitmap =>
{
// Display the bitmap.
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.Image = bitmap;
// Enable the user to select another folder.
toolStripButton1.Enabled = true;
toolStripButton2.Enabled = false;
Cursor = DefaultCursor;
},
// Specify a task scheduler from the current synchronization context
// so that the action runs on the UI thread.
new ExecutionDataflowBlockOptions
{
TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
});
//
// Connect the network.
//
try
{
// Add the Bitmap object to the collection.
bitmaps.Add(new Bitmap(fileName));
}
catch (Exception)
{
// TODO: A complete application might handle the error.
}
}
}
return bitmaps;
}
unsafe
{
// Compute the average of each color component.
a /= count;
r /= count;
g /= count;
b /= count;
// Enable the Cancel button and disable the Choose Folder button.
toolStripButton1.Enabled = false;
toolStripButton2.Enabled = true;
~Form1()
{
cancellationTokenSource.Dispose();
}
}
}
En este ejemplo se explica cómo habilitar la cancelación en la aplicación. Este ejemplo usa Windows Forms para
mostrar dónde están activos los elementos de trabajo en una canalización de flujo de datos y también los efectos
de la canalización.
NOTE
La biblioteca de flujos de datos TPL (el espacio de nombres System.Threading.Tasks.Dataflow) no se distribuye con .NET. Para
instalar el espacio de nombres System.Threading.Tasks.Dataflow en Visual Studio, abra el proyecto, seleccione Administrar
paquetes NuGet en el menú Proyecto y busque en línea el paquete System.Threading.Tasks.Dataflow . Como
alternativa, para realizar la instalación con la CLI de .Net Core, ejecute
dotnet add package System.Threading.Tasks.Dataflow .
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Threading.Tasks.Dataflow
// Connect the two nodes of the pipeline. When the first node completes,
// set the second node also to the completed state.
startWork.LinkTo(
completeWork, new DataflowLinkOptions { PropagateCompletion = true });
decrementProgress.Post(toolStripProgressBar1)
incrementProgress.Post(toolStripProgressBar2)
Return workItem
End Function,
New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token})
' Connect the two nodes of the pipeline. When the first node completes,
' set the second node also to the completed state.
startWork.LinkTo(
completeWork, New DataflowLinkOptions With {.PropagateCompletion = true})
' Create the dataflow action blocks that increment and decrement
' progress bars.
' These blocks use the task scheduler that is associated with
' the UI thread.
End Sub
Dado que los bloques de flujo de datos incrementProgress y decrementProgress actúan sobre la interfaz de
usuario, es importante que esta acción se produzca en el subproceso de interfaz de usuario. Para lograrlo, durante
la construcción, cada uno de estos objetos proporciona un objeto ExecutionDataflowBlockOptions que tiene la
propiedad TaskScheduler establecida como TaskScheduler.FromCurrentSynchronizationContext. El método
TaskScheduler.FromCurrentSynchronizationContext crea un objeto TaskScheduler que funciona en el contexto de
sincronización actual. Dado que al constructor Form1 se le llama desde el subproceso de interfaz de usuario, las
acciones de los bloques de flujo de datos incrementProgress y decrementProgress se ejecutan también en el
subproceso de interfaz de usuario.
Este ejemplo establece la propiedad CancellationToken cuando construye los miembros de la canalización. Dado
que la propiedad CancellationToken cancela de forma permanente la ejecución del bloque de flujo de datos, se
debe volver a crear la canalización completa después de que el usuario cancela la operación, en caso de que
después desee agregar más elementos de trabajo a la canalización. Para consultar un ejemplo en el que se
muestre una forma alternativa de cancelar un bloque de flujo de datos, a fin de que se pueda realizar otro trabajo
después de cancelar una operación, vea Tutorial: uso de flujo de datos en una Aplicación de Windows Forms.
3. En el diseñador de formularios del formulario principal, cree un controlador de eventos Click para el botón
Cancelar .
4. Implemente el controlador de eventos Click para el botón Cancelar .
// Trigger cancellation.
cancellationSource.Cancel();
try
{
// Asynchronously wait for the pipeline to complete processing and for
// the progress bars to update.
await Task.WhenAll(
completeWork.Completion,
incrementProgress.Completion,
decrementProgress.Completion);
}
catch (OperationCanceledException)
{
}
// Reset the progress bars that track the number of active work items.
toolStripProgressBar1.Value = 0;
toolStripProgressBar2.Value = 0;
Try
' Asynchronously wait for the pipeline to complete processing and for
' the progress bars to update.
Await Task.WhenAll(completeWork.Completion, incrementProgress.Completion,
decrementProgress.Completion)
Catch e1 As OperationCanceledException
End Try
' Increment the progress bar that tracks the number of cancelled
' work items by the number of active work items.
toolStripProgressBar4.Value += toolStripProgressBar1.Value
toolStripProgressBar4.Value += toolStripProgressBar2.Value
' Reset the progress bars that track the number of active work items.
toolStripProgressBar1.Value = 0
toolStripProgressBar2.Value = 0
Ejemplo
En el siguiente ejemplo se muestra el código completo de Form1.cs (Form1.vb para Visual Basic).
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
namespace CancellationWinForms
{
public partial class Form1 : Form
{
// A placeholder type that performs work.
class WorkItem
{
// Performs work for the provided number of milliseconds.
public void DoWork(int milliseconds)
{
// For demonstration, suspend the current thread.
Thread.Sleep(milliseconds);
}
}
public Form1()
{
InitializeComponent();
// Connect the two nodes of the pipeline. When the first node completes,
// set the second node also to the completed state.
startWork.LinkTo(
startWork.LinkTo(
completeWork, new DataflowLinkOptions { PropagateCompletion = true });
// Trigger cancellation.
cancellationSource.Cancel();
try
{
// Asynchronously wait for the pipeline to complete processing and for
// the progress bars to update.
await Task.WhenAll(
completeWork.Completion,
incrementProgress.Completion,
decrementProgress.Completion);
}
catch (OperationCanceledException)
{
}
// Increment the progress bar that tracks the number of cancelled
// work items by the number of active work items.
toolStripProgressBar4.Value += toolStripProgressBar1.Value;
toolStripProgressBar4.Value += toolStripProgressBar2.Value;
// Reset the progress bars that track the number of active work items.
toolStripProgressBar1.Value = 0;
toolStripProgressBar2.Value = 0;
~Form1()
{
cancellationSource.Dispose();
}
}
}
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Threading.Tasks.Dataflow
Namespace CancellationWinForms
Partial Public Class Form1
Inherits Form
' A placeholder type that performs work.
Private Class WorkItem
' Performs work for the provided number of milliseconds.
Public Sub DoWork(ByVal milliseconds As Integer)
' For demonstration, suspend the current thread.
Thread.Sleep(milliseconds)
End Sub
End Class
decrementProgress.Post(toolStripProgressBar1)
incrementProgress.Post(toolStripProgressBar2)
Return workItem
End Function,
New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token})
' Connect the two nodes of the pipeline. When the first node completes,
' set the second node also to the completed state.
startWork.LinkTo(
completeWork, New DataflowLinkOptions With {.PropagateCompletion = true})
' Create the dataflow action blocks that increment and decrement
' progress bars.
' These blocks use the task scheduler that is associated with
' the UI thread.
End Sub
Try
' Asynchronously wait for the pipeline to complete processing and for
' the progress bars to update.
Await Task.WhenAll(completeWork.Completion, incrementProgress.Completion,
decrementProgress.Completion)
Catch e1 As OperationCanceledException
End Try
' Increment the progress bar that tracks the number of cancelled
' work items by the number of active work items.
toolStripProgressBar4.Value += toolStripProgressBar1.Value
toolStripProgressBar4.Value += toolStripProgressBar2.Value
' Reset the progress bars that track the number of active work items.
toolStripProgressBar1.Value = 0
toolStripProgressBar2.Value = 0
Vea también
Flujo de datos
Tutorial: Crear tipos de bloques de flujos de datos
personalizados
16/09/2020 • 23 minutes to read • Edit Online
Aunque la biblioteca de flujo de datos TPL proporciona varios tipos de bloques de flujo de datos que permiten una
variedad de funciones, también puede crear tipos de bloques personalizados. En este documento se describe cómo
crear un tipo de bloque de flujo de datos que implementa un comportamiento personalizado.
Requisitos previos
Lea Flujo de datos antes de leer este documento.
NOTE
La biblioteca de flujos de datos TPL (el espacio de nombres System.Threading.Tasks.Dataflow) no se distribuye con .NET. Para
instalar el espacio de nombres System.Threading.Tasks.Dataflow en Visual Studio, abra el proyecto, seleccione Administrar
paquetes NuGet en el menú Proyecto y busque en línea el paquete System.Threading.Tasks.Dataflow . Como
alternativa, para realizar la instalación con la CLI de .Net Core, ejecute
dotnet add package System.Threading.Tasks.Dataflow .
// The target part receives data and adds them to the queue.
var target = new ActionBlock<T>(item =>
{
// Add the item to the queue.
queue.Enqueue(item);
// Remove the oldest item when the queue size exceeds the window size.
if (queue.Count > windowSize)
queue.Dequeue();
// Post the data in the queue to the source block when the queue size
// equals the window size.
if (queue.Count == windowSize)
source.Post(queue.ToArray());
});
// When the target is set to the completed state, propagate out any
// remaining data and set the source to the completed state.
target.Completion.ContinueWith(delegate
{
if (queue.Count > 0 && queue.Count < windowSize)
source.Post(queue.ToArray());
source.Complete();
});
' The source part of the propagator holds arrays of size windowSize
' and propagates data out to any connected targets.
Dim source = New BufferBlock(Of T())()
' The target part receives data and adds them to the queue.
Dim target = New ActionBlock(Of T)(Sub(item)
' Add the item to the queue.
' Remove the oldest item when the queue size exceeds the window
size.
' Post the data in the queue to the source block when the queue
size
' equals the window size.
queue.Enqueue(item)
If queue.Count > windowSize Then
queue.Dequeue()
End If
If queue.Count = windowSize Then
source.Post(queue.ToArray())
End If
End Sub)
' When the target is set to the completed state, propagate out any
' remaining data and set the source to the completed state.
target.Completion.ContinueWith(Sub()
If queue.Count > 0 AndAlso queue.Count < windowSize Then
source.Post(queue.ToArray())
End If
source.Complete()
End Sub)
// The target part receives data and adds them to the queue.
var target = new ActionBlock<T>(item =>
{
// Add the item to the queue.
queue.Enqueue(item);
// Remove the oldest item when the queue size exceeds the window size.
if (queue.Count > windowSize)
queue.Dequeue();
// Post the data in the queue to the source block when the queue size
// equals the window size.
if (queue.Count == windowSize)
source.Post(queue.ToArray());
});
// When the target is set to the completed state, propagate out any
// remaining data and set the source to the completed state.
target.Completion.ContinueWith(delegate
{
if (queue.Count > 0 && queue.Count < windowSize)
source.Post(queue.ToArray());
source.Complete();
});
m_windowSize = windowSize;
m_target = target;
m_source = source;
}
// Attempts to remove all available elements from the source into a new
// array that is returned.
public bool TryReceiveAll(out IList<T[]> items)
{
return m_source.TryReceiveAll(out items);
}
#endregion
#endregion
// Asynchronously passes a message to the target block, giving the target the
// opportunity to consume the message.
DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader,
T messageValue, ISourceBlock<T> source, bool consumeToAccept)
{
return m_target.OfferMessage(messageHeader,
messageValue, source, consumeToAccept);
}
#endregion
// Signals to this target block that it should not accept any more messages,
// nor consume postponed messages.
public void Complete()
{
m_target.Complete();
}
#endregion
}
' The source part of the propagator holds arrays of size windowSize
' and propagates data out to any connected targets.
Dim source = New BufferBlock(Of T())()
' The target part receives data and adds them to the queue.
Dim target = New ActionBlock(Of T)(Sub(item)
' Add the item to the queue.
' Remove the oldest item when the queue size exceeds the
window size.
' Post the data in the queue to the source block when the
queue size
' equals the window size.
queue.Enqueue(item)
If queue.Count > windowSize Then
queue.Dequeue()
End If
If queue.Count = windowSize Then
source.Post(queue.ToArray())
End If
End Sub)
' When the target is set to the completed state, propagate out any
' remaining data and set the source to the completed state.
target.Completion.ContinueWith(Sub()
If queue.Count > 0 AndAlso queue.Count < windowSize Then
source.Post(queue.ToArray())
End If
source.Complete()
End Sub)
m_windowSize = windowSize
m_target = target
m_source = source
End Sub
' Attempts to remove all available elements from the source into a new
' array that is returned.
Public Function TryReceiveAll(<System.Runtime.InteropServices.Out()> ByRef items As IList(Of T())) As
Boolean Implements IReceivableSourceBlock(Of T()).TryReceiveAll
Return m_source.TryReceiveAll(items)
End Function
'#End Region
'#End Region
#End Region
' Asynchronously passes a message to the target block, giving the target the
' opportunity to consume the message.
Private Function OfferMessage(ByVal messageHeader As DataflowMessageHeader, ByVal messageValue As T,
ByVal source As ISourceBlock(Of T), ByVal consumeToAccept As Boolean) As DataflowMessageStatus Implements
ITargetBlock(Of T).OfferMessage
Return m_target.OfferMessage(messageHeader, messageValue, source, consumeToAccept)
End Function
#End Region
' Gets a Task that represents the completion of this dataflow block.
Public ReadOnly Property Completion() As Task Implements IDataflowBlock.Completion
Get
Return m_source.Completion
End Get
End Property
' Signals to this target block that it should not accept any more messages,
' nor consume postponed messages.
Public Sub Complete() Implements IDataflowBlock.Complete
m_target.Complete()
End Sub
#End Region
End Class
Ejemplo completo
En el ejemplo siguiente se muestra el código completo de este tutorial. También se muestra cómo usar los dos
bloques de ventana deslizante en un método que escribe en el bloque, lee en él e imprime los resultados en la
consola.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
// The target part receives data and adds them to the queue.
var target = new ActionBlock<T>(item =>
{
// Add the item to the queue.
queue.Enqueue(item);
// Remove the oldest item when the queue size exceeds the window size.
if (queue.Count > windowSize)
queue.Dequeue();
// Post the data in the queue to the source block when the queue size
// equals the window size.
if (queue.Count == windowSize)
source.Post(queue.ToArray());
});
// When the target is set to the completed state, propagate out any
// remaining data and set the source to the completed state.
target.Completion.ContinueWith(delegate
{
if (queue.Count > 0 && queue.Count < windowSize)
source.Post(queue.ToArray());
source.Complete();
});
// The target part receives data and adds them to the queue.
var target = new ActionBlock<T>(item =>
{
// Add the item to the queue.
queue.Enqueue(item);
// Remove the oldest item when the queue size exceeds the window size.
if (queue.Count > windowSize)
queue.Dequeue();
// Post the data in the queue to the source block when the queue size
// equals the window size.
if (queue.Count == windowSize)
source.Post(queue.ToArray());
});
// When the target is set to the completed state, propagate out any
// remaining data and set the source to the completed state.
target.Completion.ContinueWith(delegate
{
if (queue.Count > 0 && queue.Count < windowSize)
source.Post(queue.ToArray());
source.Complete();
});
m_windowSize = windowSize;
m_target = target;
m_source = source;
}
// Attempts to remove all available elements from the source into a new
// array that is returned.
public bool TryReceiveAll(out IList<T[]> items)
{
return m_source.TryReceiveAll(out items);
}
#endregion
#endregion
// Asynchronously passes a message to the target block, giving the target the
// opportunity to consume the message.
DataflowMessageStatus ITargetBlock<T>.OfferMessage(DataflowMessageHeader messageHeader,
T messageValue, ISourceBlock<T> source, bool consumeToAccept)
{
return m_target.OfferMessage(messageHeader,
messageValue, source, consumeToAccept);
}
#endregion
// Signals to this target block that it should not accept any more messages,
// nor consume postponed messages.
public void Complete()
{
m_target.Complete();
}
#endregion
}
// Set the printer block to the completed state when the sliding window
// block completes.
slidingWindow.Completion.ContinueWith(delegate { printWindow.Complete(); });
// Print an additional newline to the console when the printer block completes.
var completion = printWindow.Completion.ContinueWith(delegate { Console.WriteLine(); });
// Post the provided values to the sliding window block and then wait
// for the sliding window block to complete.
foreach (T value in values)
{
slidingWindow.Post(value);
}
slidingWindow.Complete();
// Wait for the printer to complete and perform its final action.
completion.Wait();
}
Console.WriteLine();
/* Output:
Using the DataflowBlockExtensions.Encapsulate method (T=int, windowSize=3):
{0,1,2}, {1,2,3}, {2,3,4}, {3,4,5}, {4,5,6}, {5,6,7}, {6,7,8}, {7,8,9}
Imports System.Collections.Generic
Imports System.Linq
Imports System.Threading.Tasks
Imports System.Threading.Tasks.Dataflow
' The source part of the propagator holds arrays of size windowSize
' and propagates data out to any connected targets.
Dim source = New BufferBlock(Of T())()
' The target part receives data and adds them to the queue.
Dim target = New ActionBlock(Of T)(Sub(item)
' Add the item to the queue.
' Remove the oldest item when the queue size exceeds the window
size.
' Post the data in the queue to the source block when the queue
size
' equals the window size.
queue.Enqueue(item)
If queue.Count > windowSize Then
queue.Dequeue()
End If
If queue.Count = windowSize Then
source.Post(queue.ToArray())
End If
End Sub)
' When the target is set to the completed state, propagate out any
' remaining data and set the source to the completed state.
target.Completion.ContinueWith(Sub()
If queue.Count > 0 AndAlso queue.Count < windowSize Then
source.Post(queue.ToArray())
End If
source.Complete()
End Sub)
' The source part of the propagator holds arrays of size windowSize
' and propagates data out to any connected targets.
Dim source = New BufferBlock(Of T())()
' The target part receives data and adds them to the queue.
Dim target = New ActionBlock(Of T)(Sub(item)
' Add the item to the queue.
' Remove the oldest item when the queue size exceeds the
window size.
' Post the data in the queue to the source block when the
queue size
' equals the window size.
queue.Enqueue(item)
If queue.Count > windowSize Then
queue.Dequeue()
End If
If queue.Count = windowSize Then
If queue.Count = windowSize Then
source.Post(queue.ToArray())
End If
End Sub)
' When the target is set to the completed state, propagate out any
' remaining data and set the source to the completed state.
target.Completion.ContinueWith(Sub()
If queue.Count > 0 AndAlso queue.Count < windowSize Then
source.Post(queue.ToArray())
End If
source.Complete()
End Sub)
m_windowSize = windowSize
m_target = target
m_source = source
End Sub
' Attempts to remove all available elements from the source into a new
' array that is returned.
Public Function TryReceiveAll(<System.Runtime.InteropServices.Out()> ByRef items As IList(Of T())) As
Boolean Implements IReceivableSourceBlock(Of T()).TryReceiveAll
Return m_source.TryReceiveAll(items)
End Function
'#End Region
' Asynchronously passes a message to the target block, giving the target the
' opportunity to consume the message.
Private Function OfferMessage(ByVal messageHeader As DataflowMessageHeader, ByVal messageValue As T,
ByVal source As ISourceBlock(Of T), ByVal consumeToAccept As Boolean) As DataflowMessageStatus Implements
ITargetBlock(Of T).OfferMessage
Return m_target.OfferMessage(messageHeader, messageValue, source, consumeToAccept)
End Function
#End Region
' Gets a Task that represents the completion of this dataflow block.
Public ReadOnly Property Completion() As Task Implements IDataflowBlock.Completion
Get
Return m_source.Completion
End Get
End Property
' Signals to this target block that it should not accept any more messages,
' nor consume postponed messages.
Public Sub Complete() Implements IDataflowBlock.Complete
m_target.Complete()
End Sub
#End Region
End Class
' Demonstrates usage of the sliding window block by sending the provided
' values to the provided propagator block and printing the output of
' that block to the console.
Private Shared Sub DemonstrateSlidingWindow(Of T)(ByVal slidingWindow As IPropagatorBlock(Of T, T()),
ByVal values As IEnumerable(Of T))
' Create an action block that prints arrays of data to the console.
Dim windowComma As String = String.Empty
Dim printWindow = New ActionBlock(Of T())(Sub(window)
Console.Write(windowComma)
Console.Write("{")
Dim comma As String = String.Empty
For Each item As T In window
Console.Write(comma)
Console.Write(item)
comma = ","
Next item
Console.Write("}")
windowComma = ", "
End Sub)
' Set the printer block to the completed state when the sliding window
' block completes.
slidingWindow.Completion.ContinueWith(Sub() printWindow.Complete())
' Print an additional newline to the console when the printer block completes.
Dim completion = printWindow.Completion.ContinueWith(Sub() Console.WriteLine())
' Post the provided values to the sliding window block and then wait
' for the sliding window block to complete.
For Each value As T In values
For Each value As T In values
slidingWindow.Post(value)
Next value
slidingWindow.Complete()
' Wait for the printer to complete and perform its final action.
completion.Wait()
End Sub
Console.WriteLine()
' Output:
'Using the DataflowBlockExtensions.Encapsulate method (T=int, windowSize=3):
'{0,1,2}, {1,2,3}, {2,3,4}, {3,4,5}, {4,5,6}, {5,6,7}, {6,7,8}, {7,8,9}
'
'Using SlidingWindowBlock<T> (T=char, windowSize=4):
'{A,B,C,D}, {B,C,D,E}, {C,D,E,F}, {D,E,F,G}, {E,F,G,H}, {F,G,H,I}, {G,H,I,J}
'
Vea también
Flujo de datos
Cómo: Usar JoinBlock para leer datos de diferentes
orígenes
16/09/2020 • 10 minutes to read • Edit Online
En este documento se explica cómo utilizar la clase JoinBlock<T1,T2> para realizar una operación cuando los
datos están disponibles en varios orígenes. También se demuestra cómo usar el modo no expansivo para habilitar
varios bloques de combinación para compartir un origen de datos más eficazmente.
NOTE
La biblioteca de flujos de datos TPL (el espacio de nombres System.Threading.Tasks.Dataflow) no se distribuye con .NET. Para
instalar el espacio de nombres System.Threading.Tasks.Dataflow en Visual Studio, abra el proyecto, seleccione Administrar
paquetes NuGet en el menú Proyecto y busque en línea el paquete System.Threading.Tasks.Dataflow . Como
alternativa, para realizar la instalación con la CLI de .Net Core, ejecute
dotnet add package System.Threading.Tasks.Dataflow .
Ejemplo
El siguiente ejemplo define tres tipos de recursos, NetworkResource , FileResource y MemoryResource , y realiza las
operaciones cuando los recursos están disponibles. Este ejemplo requiere un par NetworkResource y
MemoryResource para realizar la primera operación y un par FileResource y MemoryResource para realizar la
segunda operación. Para permitir que estas operaciones se produzcan cuando todos los recursos necesarios están
disponibles, este ejemplo usa la clase JoinBlock<T1,T2>. Cuando un objeto JoinBlock<T1,T2> recibe datos de
todos los orígenes, los propaga en su destino, que en este ejemplo es un objeto ActionBlock<TInput>. Ambos
objetos JoinBlock<T1,T2> leen de un grupo compartido de objetos MemoryResource .
using System;
using System.Threading;
using System.Threading.Tasks.Dataflow;
var joinNetworkAndMemoryResources =
new JoinBlock<NetworkResource, MemoryResource>(
new GroupingDataflowBlockOptions
{
Greedy = false
});
var joinFileAndMemoryResources =
new JoinBlock<FileResource, MemoryResource>(
new GroupingDataflowBlockOptions
{
Greedy = false
});
var networkMemoryAction =
new ActionBlock<Tuple<NetworkResource, MemoryResource>>(
data =>
{
// Perform some action on the resources.
// Print a message.
Console.WriteLine("Network worker: using resources...");
// Print a message.
Console.WriteLine("Network worker: finished using resources...");
var fileMemoryAction =
new ActionBlock<Tuple<FileResource, MemoryResource>>(
data =>
{
// Perform some action on the resources.
// Print a message.
Console.WriteLine("File worker: using resources...");
// Print a message.
Console.WriteLine("File worker: finished using resources...");
// Release the resources back to their respective pools.
fileResources.Post(data.Item1);
memoryResources.Post(data.Item2);
});
networkResources.LinkTo(joinNetworkAndMemoryResources.Target1);
memoryResources.LinkTo(joinNetworkAndMemoryResources.Target2);
fileResources.LinkTo(joinFileAndMemoryResources.Target1);
memoryResources.LinkTo(joinFileAndMemoryResources.Target2);
joinNetworkAndMemoryResources.LinkTo(networkMemoryAction);
joinFileAndMemoryResources.LinkTo(fileMemoryAction);
networkResources.Post(new NetworkResource());
networkResources.Post(new NetworkResource());
networkResources.Post(new NetworkResource());
memoryResources.Post(new MemoryResource());
fileResources.Post(new FileResource());
fileResources.Post(new FileResource());
fileResources.Post(new FileResource());
/* Sample output:
File worker: using resources...
File worker: finished using resources...
Network worker: using resources...
Network worker: finished using resources...
File worker: using resources...
File worker: finished using resources...
Network worker: using resources...
Network worker: finished using resources...
File worker: using resources...
File worker: finished using resources...
File worker: using resources...
File worker: finished using resources...
Network worker: using resources...
Network worker: finished using resources...
Network worker: using resources...
Network worker: finished using resources...
File worker: using resources...
*/
Imports System.Threading
Imports System.Threading.Tasks.Dataflow
networkResources.Post(data.Item1)
memoryResources.Post(data.Item2)
End Sub)
fileResources.Post(data.Item1)
memoryResources.Post(data.Item2)
End Sub)
networkResources.LinkTo(joinNetworkAndMemoryResources.Target1)
memoryResources.LinkTo(joinNetworkAndMemoryResources.Target2)
fileResources.LinkTo(joinFileAndMemoryResources.Target1)
memoryResources.LinkTo(joinFileAndMemoryResources.Target2)
joinNetworkAndMemoryResources.LinkTo(networkMemoryAction)
joinFileAndMemoryResources.LinkTo(fileMemoryAction)
networkResources.Post(New NetworkResource())
networkResources.Post(New NetworkResource())
networkResources.Post(New NetworkResource())
memoryResources.Post(New MemoryResource())
fileResources.Post(New FileResource())
fileResources.Post(New FileResource())
fileResources.Post(New FileResource())
' Allow data to flow through the network for several seconds.
Thread.Sleep(10000)
End Sub
End Class
Para habilitar el uso eficaz del grupo compartido de objetos MemoryResource , este ejemplo especifica un objeto
GroupingDataflowBlockOptions que tiene la propiedad Greedy establecida en False para crear objetos
JoinBlock<T1,T2> que actúan de modo no expansivo. Un bloque de combinación no expansivo pospone todos los
mensajes entrantes hasta que uno esté disponible de cada origen. Si otro bloque aceptó cualquiera de los
mensajes pospuestos, el bloque de combinación reinicia el proceso. El modo no expansivo permite que los
bloques de combinación compartan uno o varios bloques de origen para avanzar mientras otros bloques esperan
datos. En este ejemplo, si un objeto MemoryResource se agrega al grupo memoryResources , el primer bloque de
combinación que recibe su segundo origen de datos puede progresar. Si en este ejemplo se usara el modo
expansivo, que es el predeterminado, un bloque de combinación puede adoptar el objeto MemoryResource y
esperar a que el segundo recurso esté disponible. Sin embargo, si el otro bloque de combinación tiene su
segundo origen de datos disponible, no puede avanzar porque otro bloque de combinación ha adoptado el objeto
MemoryResource .
Programación sólida
El uso de las combinaciones no expansivas también puede ayudar a evitar el interbloqueo en la aplicación. En una
aplicación de software, el interbloqueo se produce cuando dos o más procesos mantienen un recurso y esperan
mutuamente a que el otro proceso libere algún otro recurso. Considere una aplicación que define dos objetos
JoinBlock<T1,T2>. Ambos objetos leen datos de dos bloques de origen compartidos. En modo expansivo, si un
bloque de combinación lee desde el primer origen y el segundo bloque de combinación lee desde el segundo
origen, se podría producir un interbloqueo de la aplicación porque los dos bloques de combinación esperan a que
el otro libere su recurso. En el modo no expansivo, cada bloque de combinación lee de sus orígenes solo cuando
todos los datos están disponibles y, por tanto, se elimina el riesgo del interbloqueo.
Vea también
Flujo de datos
Cómo: Especificar el grado de paralelismo en un
bloque de flujos de datos
16/09/2020 • 7 minutes to read • Edit Online
NOTE
La biblioteca de flujos de datos TPL (el espacio de nombres System.Threading.Tasks.Dataflow) no se distribuye con .NET. Para
instalar el espacio de nombres System.Threading.Tasks.Dataflow en Visual Studio, abra el proyecto, seleccione Administrar
paquetes NuGet en el menú Proyecto y busque en línea el paquete System.Threading.Tasks.Dataflow . Como
alternativa, para realizar la instalación con la CLI de .Net Core, ejecute
dotnet add package System.Threading.Tasks.Dataflow .
Ejemplo
En el ejemplo siguiente se realizan dos cálculos de flujo de datos y se imprime el tiempo transcurrido que se
necesita para cada cálculo. El primer cálculo especifica un grado máximo de paralelismo de 1, que es el valor
predeterminado. Un grado máximo de paralelismo de 1 hace que el bloque de flujo de datos procese los mensajes
en serie. El segundo cálculo es similar al primero, excepto que especifica un grado máximo de paralelismo igual al
número de procesadores disponibles. Esto permite que el bloque de flujo de datos realice varias operaciones en
paralelo.
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks.Dataflow;
TimeSpan elapsed;
/* Sample output:
Processor count = 4.
Degree of parallelism = 1; message count = 4; elapsed time = 4032ms.
Degree of parallelism = 4; message count = 4; elapsed time = 1001ms.
*/
Imports System.Diagnostics
Imports System.Threading
Imports System.Threading.Tasks.Dataflow
' Stop the timer and return the elapsed number of milliseconds.
stopwatch.Stop()
Return stopwatch.Elapsed
End Function
' Perform the computations again. This time, specify the number of
' processors as the maximum degree of parallelism. This causes
' multiple messages to be processed in parallel.
elapsed = TimeDataflowComputations(processorCount, messageCount)
Console.WriteLine("Degree of parallelism = {0}; message count = {1}; " & "elapsed time = {2}ms.",
processorCount, messageCount, CInt(Fix(elapsed.TotalMilliseconds)))
End Sub
End Class
Programación sólida
De forma predeterminada, cada bloque de flujo de datos predefinido propaga los mensajes en el orden con que
se reciben. Aunque cuando se especifica un grado máximo de paralelismo mayor que 1 se procesan
simultáneamente varios mensajes, se siguen propagando en el orden con que se reciben.
Dado que la propiedad MaxDegreeOfParallelism representa el grado máximo de paralelismo, el bloque de flujo de
datos puede ejecutarse con un menor grado de paralelismo que el especificado. El bloque de flujo de datos puede
usar un grado de paralelismo menor para cumplir los requisitos funcionales o porque hay una falta de recursos
del sistema. Un bloque de flujo de datos nunca elige un grado de paralelismo mayor que el especificado.
Vea también
Flujo de datos
Cómo: Especificar un programador de tareas en un
bloque de flujos de datos
16/09/2020 • 14 minutes to read • Edit Online
Este documento muestra cómo asociar un programador de tareas específico al usar un flujo de datos en la
aplicación. En el ejemplo, se usa la clase System.Threading.Tasks.ConcurrentExclusiveSchedulerPair en una
aplicación de Windows Forms para mostrar cuándo están activas las tareas de lectura y cuándo está activa una
tarea de escritura. También se usa el método TaskScheduler.FromCurrentSynchronizationContext para permitir
que un bloque de flujo de datos se ejecute en el subproceso de la interfaz de usuario.
NOTE
La biblioteca de flujos de datos TPL (el espacio de nombres System.Threading.Tasks.Dataflow) no se distribuye con .NET. Para
instalar el espacio de nombres System.Threading.Tasks.Dataflow en Visual Studio, abra el proyecto, seleccione Administrar
paquetes NuGet en el menú Proyecto y busque en línea el paquete System.Threading.Tasks.Dataflow . Como
alternativa, para realizar la instalación con la CLI de .Net Core, ejecute
dotnet add package System.Threading.Tasks.Dataflow .
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Threading.Tasks.Dataflow
7. En el diseñador de formularios del formulario principal, cree un controlador de eventos para el evento Tick
del temporizador.
8. Implemente el evento Tick del temporizador.
// Event handler for the timer.
private void timer1_Tick(object sender, EventArgs e)
{
// Post a value to the broadcaster. The broadcaster
// sends this message to each target.
broadcaster.Post(1000);
}
Dado que el bloque de flujo de datos toggleCheckBox actúa en la interfaz de usuario, es importante que esta
acción se produzca en el subproceso de la interfaz de usuario. Para lograrlo, durante la construcción, este objeto
proporciona un objeto ExecutionDataflowBlockOptions que tiene la propiedad TaskScheduler establecida como
TaskScheduler.FromCurrentSynchronizationContext. El método FromCurrentSynchronizationContext crea un
objeto TaskScheduler que funciona en el contexto de sincronización actual. Dado que al constructor Form1 se le
llama desde el subproceso de la interfaz de usuario, la acción del bloque de flujo de datos toggleCheckBox se
ejecuta también en el subproceso de la interfaz de usuario.
En este ejemplo, también se usa la clase ConcurrentExclusiveSchedulerPair para permitir que algunos bloques de
flujo de datos actúen de forma simultánea y que otro bloque de flujo de datos actúe de forma exclusiva con
respecto a todos los demás bloques de flujo de datos que se ejecutan en el mismo objeto
ConcurrentExclusiveSchedulerPair. Esta técnica es útil cuando varios bloques de flujo de datos comparten un
recurso y algunos requieren acceso exclusivo a ese recurso, ya que evita la necesidad de sincronizar manualmente
el acceso a ese recurso. Al eliminar la sincronización manual, se puede hacer que el código sea más eficiente.
Ejemplo
En el siguiente ejemplo se muestra el código completo de Form1.cs (Form1.vb para Visual Basic).
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
namespace WriterReadersWinForms
{
public partial class Form1 : Form
{
// Broadcasts values to an ActionBlock<int> object that is associated
// with each check box.
BroadcastBlock<int> broadcaster = new BroadcastBlock<int>(null);
public Form1()
{
InitializeComponent();
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Threading.Tasks.Dataflow
Namespace WriterReadersWinForms
Partial Public Class Form1
Inherits Form
' Broadcasts values to an ActionBlock<int> object that is associated
' with each check box.
Private broadcaster As New BroadcastBlock(Of Integer)(Nothing)
Vea también
Flujo de datos
Tutorial: Usar BatchBlock y BatchedJoinBlock para
mejorar la eficacia
16/09/2020 • 34 minutes to read • Edit Online
NOTE
La biblioteca de flujos de datos TPL (el espacio de nombres System.Threading.Tasks.Dataflow) no se distribuye con .NET. Para
instalar el espacio de nombres System.Threading.Tasks.Dataflow en Visual Studio, abra el proyecto, seleccione Administrar
paquetes NuGet en el menú Proyecto y busque en línea el paquete System.Threading.Tasks.Dataflow . Como
alternativa, para realizar la instalación con la CLI de .Net Core, ejecute
dotnet add package System.Threading.Tasks.Dataflow .
Requisitos previos
1. Lea la sección sobre bloques de combinación en el documento Flujo de datos antes de iniciar este tutorial.
2. Asegúrese de que tiene una copia de la base de datos Northwind, Northwind.sdf, disponible en el equipo.
Este archivo se encuentra normalmente en la carpeta %Archivos de programa%\Microsoft SQL Server
Compact Edition\v3.5\Ejemplos\.
IMPORTANT
En algunas versiones de Windows, no se puede conectar a Northwind.sdf si Visual Studio se ejecuta en modo que no
sea de administrador. Para conectarse a Northwind.sdf, inicie Visual Studio o Símbolo del sistema para
desarrolladores de Visual Studio en modo Ejecutar como administrador .
using System;
using System.Collections.Generic;
using System.Data.SqlServerCe;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks.Dataflow;
Imports System.Collections.Generic
Imports System.Data.SqlServerCe
Imports System.Diagnostics
Imports System.IO
Imports System.Threading.Tasks.Dataflow
' TODO: Change this value if you require a different temporary location.
Private Shared ReadOnly scratchDatabase As String = "C:\Temp\Northwind.sdf"
La clase Employee contiene tres propiedades EmployeeID , LastName y FirstName . Estas propiedades
corresponden a las columnas Employee ID , Last Name y First Name en la tabla Employees de la base de datos
Northwind. Para esta demostración, la clase Employee también define el método Random , que crea un objeto
Employee con valores aleatorios para sus propiedades.
connection.Open();
for (int i = 0; i < employees.Length; i++)
{
// Set parameters.
command.Parameters.Clear();
command.Parameters.Add("@lastName", employees[i].LastName);
command.Parameters.Add("@firstName", employees[i].FirstName);
sqlConnection.Open();
try
{
result = (int)sqlCommand.ExecuteScalar();
}
finally
{
sqlConnection.Close();
}
}
return result;
}
// Retrieves the ID of the first employee that has the provided name.
static int GetEmployeeID(string lastName, string firstName,
string connectionString)
{
using (SqlCeConnection connection =
new SqlCeConnection(connectionString))
{
SqlCeCommand command = new SqlCeCommand(
string.Format(
"SELECT [Employee ID] FROM Employees " +
"WHERE [Last Name] = '{0}' AND [First Name] = '{1}'",
lastName, firstName),
connection);
connection.Open();
try
{
return (int)command.ExecuteScalar();
}
finally
{
connection.Close();
}
}
}
' Adds new employee records to the database.
Private Shared Sub InsertEmployees(ByVal employees() As Employee, ByVal connectionString As String)
Using connection As New SqlCeConnection(connectionString)
Try
' Create the SQL command.
Dim command As New SqlCeCommand("INSERT INTO Employees ([Last Name], [First Name])" & "VALUES
(@lastName, @firstName)", connection)
connection.Open()
For i As Integer = 0 To employees.Length - 1
' Set parameters.
command.Parameters.Clear()
command.Parameters.Add("@lastName", employees(i).LastName)
command.Parameters.Add("@firstName", employees(i).FirstName)
sqlConnection.Open()
Try
result = CInt(Fix(sqlCommand.ExecuteScalar()))
Finally
sqlConnection.Close()
End Try
End Using
Return result
End Function
' Retrieves the ID of the first employee that has the provided name.
Private Shared Function GetEmployeeID(ByVal lastName As String, ByVal firstName As String, ByVal
connectionString As String) As Integer
Using connection As New SqlCeConnection(connectionString)
Dim command As New SqlCeCommand(String.Format("SELECT [Employee ID] FROM Employees " & "WHERE [Last
Name] = '{0}' AND [First Name] = '{1}'", lastName, firstName), connection)
connection.Open()
Try
Return CInt(Fix(command.ExecuteScalar()))
Finally
connection.Close()
End Try
End Using
End Function
// Set the dataflow block to the completed state and wait for
// all insert operations to complete.
insertEmployee.Complete();
insertEmployee.Completion.Wait();
}
' Set the dataflow block to the completed state and wait for
' all insert operations to complete.
insertEmployee.Complete()
insertEmployee.Completion.Wait()
End Sub
El método AddEmployees agrega datos del empleado en la base de datos mediante el flujo de datos. También crea
un objeto ActionBlock<TInput> que llama al método InsertEmployees para agregar una entrada de empleado en
la base de datos. A continuación, el método AddEmployees llama al método PostRandomEmployees para enviar
varios objetos Employee al objeto ActionBlock<TInput>. El método AddEmployees espera que todas las
operaciones de inserción finalicen.
Usar el almacenamiento en búfer para agregar datos de empleados en
la base de datos
Agregue a la clase Program el método AddEmployeesBatched .
// When the batch block completes, set the action block also to complete.
batchEmployees.Completion.ContinueWith(delegate { insertEmployees.Complete(); });
// Set the batch block to the completed state and wait for
// all insert operations to complete.
batchEmployees.Complete();
insertEmployees.Completion.Wait();
}
' When the batch block completes, set the action block also to complete.
batchEmployees.Completion.ContinueWith(Sub() insertEmployees.Complete())
' Set the batch block to the completed state and wait for
' all insert operations to complete.
batchEmployees.Complete()
insertEmployees.Completion.Wait()
End Sub
Este método es similar a AddEmployees , salvo que también utiliza la clase BatchBlock<T> para almacenar en búfer
varios objetos Employee antes de enviar esos objetos al objeto ActionBlock<TInput>. Dado que la clase
BatchBlock<T> propaga varios elementos como una colección, el objeto ActionBlock<TInput> se modifica para
actuar sobre una matriz de objetos Employee . Como en el método AddEmployees , AddEmployeesBatched llama al
método PostRandomEmployees para enviar varios objetos Employee ; sin embargo, AddEmployeesBatched envía estos
objetos al objeto BatchBlock<T>. El método AddEmployeesBatched también espera a que todas las operaciones de
inserción finalicen.
// When the batched join block completes, set the action block also to complete.
selectEmployees.Completion.ContinueWith(delegate { printEmployees.Complete(); });
// Set the batched join block to the completed state and wait for
// all retrieval operations to complete.
selectEmployees.Complete();
printEmployees.Completion.Wait();
' Create an action block that prints employee and error information
' to the console.
Dim printEmployees = New ActionBlock(Of Tuple(Of IList(Of Employee), IList(Of Exception)))(Sub(data)
' Print
information about the employees in this batch.
' Print
the error count for this batch.
' Update
total error count.
Console.WriteLine("Received a batch...")
For Each e
As Employee In data.Item1
totalErrors += data.Item2.Count
End Sub)
' When the batched join block completes, set the action block also to complete.
selectEmployees.Completion.ContinueWith(Sub() printEmployees.Complete())
' Set the batched join block to the completed state and wait for
' all retrieval operations to complete.
selectEmployees.Complete()
printEmployees.Completion.Wait()
Este método imprime información sobre empleados aleatorios en la consola. Crea varios objetos Employee
aleatorios y llama al método GetEmployeeID para recuperar el identificador único para cada objeto. Debido a que
el método GetEmployeeID produce una excepción si no hay ningún empleado coincidente con los nombres y
apellidos dados, el método GetRandomEmployees utiliza la clase BatchedJoinBlock<T1,T2> para almacenar objetos
Employee para llamadas correctas a GetEmployeeID y objetos System.Exception para llamadas que dan error. El
objeto ActionBlock<TInput> en este ejemplo actúa sobre un objeto Tuple<T1,T2> que contiene una lista de
objetos Employee y una lista de objetos Exception. El objeto BatchedJoinBlock<T1,T2> propaga estos datos
cuando la suma del objeto Employee y Exception recibido es igual que el tamaño del lote.
Ejemplo completo
El ejemplo siguiente muestra el código completo: El método Main compara el tiempo necesario para realizar
inserciones por lotes de la base de datos frente al tiempo necesario para realizar inserciones sin lotes de la base
de datos. También muestra el uso de una combinación almacenada en búfer para leer datos de empleados de la
base de datos, así como notificar errores.
using System;
using System.Collections.Generic;
using System.Data.SqlServerCe;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks.Dataflow;
connection.Open();
for (int i = 0; i < employees.Length; i++)
{
// Set parameters.
command.Parameters.Clear();
command.Parameters.Add("@lastName", employees[i].LastName);
command.Parameters.Add("@firstName", employees[i].FirstName);
sqlConnection.Open();
try
{
result = (int)sqlCommand.ExecuteScalar();
}
finally
{
sqlConnection.Close();
}
}
return result;
}
// Retrieves the ID of the first employee that has the provided name.
static int GetEmployeeID(string lastName, string firstName,
string connectionString)
{
using (SqlCeConnection connection =
new SqlCeConnection(connectionString))
{
SqlCeCommand command = new SqlCeCommand(
string.Format(
"SELECT [Employee ID] FROM Employees " +
"WHERE [Last Name] = '{0}' AND [First Name] = '{1}'",
lastName, firstName),
connection);
connection.Open();
try
{
return (int)command.ExecuteScalar();
}
finally
{
connection.Close();
}
}
}
// Set the dataflow block to the completed state and wait for
// all insert operations to complete.
insertEmployee.Complete();
insertEmployee.Completion.Wait();
}
// When the batch block completes, set the action block also to complete.
batchEmployees.Completion.ContinueWith(delegate { insertEmployees.Complete(); });
// Set the batch block to the completed state and wait for
// all insert operations to complete.
batchEmployees.Complete();
insertEmployees.Completion.Wait();
}
// When the batched join block completes, set the action block also to complete.
selectEmployees.Completion.ContinueWith(delegate { printEmployees.Complete(); });
// Set the batched join block to the completed state and wait for
// all retrieval operations to complete.
selectEmployees.Complete();
printEmployees.Completion.Wait();
Console.WriteLine();
Console.WriteLine();
Imports System.Collections.Generic
Imports System.Data.SqlServerCe
Imports System.Diagnostics
Imports System.IO
Imports System.Threading.Tasks.Dataflow
' Demonstrates how to use batched dataflow blocks to improve
' the performance of database operations.
Namespace DataflowBatchDatabase
Friend Class Program
' The number of employees to add to the database.
' TODO: Change this value to experiment with different numbers of
' employees to insert into the database.
Private Shared ReadOnly insertCount As Integer = 256
' TODO: Change this value if you require a different temporary location.
Private Shared ReadOnly scratchDatabase As String = "C:\Temp\Northwind.sdf"
connection.Open()
For i As Integer = 0 To employees.Length - 1
' Set parameters.
command.Parameters.Clear()
command.Parameters.Add("@lastName", employees(i).LastName)
command.Parameters.Add("@firstName", employees(i).FirstName)
sqlConnection.Open()
Try
result = CInt(Fix(sqlCommand.ExecuteScalar()))
Finally
sqlConnection.Close()
End Try
End Using
Return result
End Function
' Retrieves the ID of the first employee that has the provided name.
Private Shared Function GetEmployeeID(ByVal lastName As String, ByVal firstName As String, ByVal
connectionString As String) As Integer
Using connection As New SqlCeConnection(connectionString)
Dim command As New SqlCeCommand(String.Format("SELECT [Employee ID] FROM Employees " & "WHERE
[Last Name] = '{0}' AND [First Name] = '{1}'", lastName, firstName), connection)
connection.Open()
Try
Return CInt(Fix(command.ExecuteScalar()))
Finally
connection.Close()
End Try
End Using
End Function
' Set the dataflow block to the completed state and wait for
' all insert operations to complete.
insertEmployee.Complete()
insertEmployee.Completion.Wait()
End Sub
' When the batch block completes, set the action block also to complete.
batchEmployees.Completion.ContinueWith(Sub() insertEmployees.Complete())
' Set the batch block to the completed state and wait for
' all insert operations to complete.
batchEmployees.Complete()
insertEmployees.Completion.Wait()
End Sub
' Create an action block that prints employee and error information
' to the console.
Dim printEmployees = New ActionBlock(Of Tuple(Of IList(Of Employee), IList(Of Exception)))
(Sub(data)
'
Print information about the employees in this batch.
'
Print the error count for this batch.
'
Update total error count.
Console.WriteLine("Received a batch...")
Next e
totalErrors += data.Item2.Count
End
Sub)
' When the batched join block completes, set the action block also to complete.
selectEmployees.Completion.ContinueWith(Sub() printEmployees.Complete())
' Try to retrieve the ID for the employee from the database.
e.EmployeeID = GetEmployeeID(e.LastName, e.FirstName, connectionString)
' Set the batched join block to the completed state and wait for
' all retrieval operations to complete.
selectEmployees.Complete()
printEmployees.Completion.Wait()
' Start with a clean database file by copying the source database to
' the temporary location.
File.Copy(sourceDatabase, scratchDatabase, True)
Console.WriteLine()
Console.WriteLine()
Vea también
Flujo de datos
Usar TPL con otros patrones asincrónicos
16/09/2020 • 2 minutes to read • Edit Online
La biblioteca TPL puede utilizarse con patrones de programación asincrónica de .NET Framework tradicionales de
varias formas.
En esta sección
TPL y la programación asincrónica tradicional de .NET Framework
Se describe cómo se pueden usar los objetos Task junto con el modelo de programación asincrónica (APM) y el
modelo asincrónico basado en eventos (EAP).
Encapsular modelos de EAP en una tarea
Se explica cómo utilizar objetos Task para encapsular modelos de EAP.
Vea también
Biblioteca TPL
TPL y la programación asincrónica tradicional de
.NET Framework
16/09/2020 • 23 minutes to read • Edit Online
.NET Framework proporciona los siguientes dos modelos estándar para realizar las operaciones asincrónicas
enlazadas a E/S y enlazadas a cálculos:
Modelo de programación asincrónica (APM), en el que las operaciones asincrónicas se representan
mediante un par de métodos Begin/End como FileStream.BeginRead y Stream.EndRead.
Modelo asincrónico basado en eventos (EAP) en el que las operaciones asincrónicas se representan
mediante un par método-evento que se denomina OperationNameAsync y OperationNameCompleted;
por ejemplo, WebClient.DownloadStringAsync y WebClient.DownloadStringCompleted. (EAP apareció por
primera vez en .NET Framework versión 2.0).
La biblioteca TPL (Task Parallel Library, biblioteca de procesamiento paralelo basado en tareas) se puede usar de
varias maneras junto con cualquiera de los modelos asincrónicos. Puede exponer las operaciones de APM y EAP
como tareas a los consumidores de la biblioteca o puede exponer los modelos de APM, pero usar objetos de tarea
para implementarlos internamente. En ambos escenarios, al usar los objetos de tarea, puede simplificar el código
y aprovechar la siguiente funcionalidad útil:
Registre las devoluciones de llamada, en el formulario de continuaciones de la tarea, en cualquier
momento después de que se haya iniciado la tarea.
Coordine varias operaciones que se ejecutan en respuesta a un método Begin_ , mediante los métodos
ContinueWhenAll, ContinueWhenAny, WaitAll o WaitAny.
Encapsule las operaciones asincrónicas enlazadas a E/S y enlazadas a cálculos en el mismo objeto de tarea.
Supervise el estado del objeto de tarea.
Serialice las referencias del estado una operación para un objeto de tarea mediante
TaskCompletionSource<TResult>.
En los pocos casos en los que el método Begin tiene más de tres parámetros o contiene parámetros ref o out ,
se proporcionan las sobrecargas FromAsync adicionales que encapsulan sólo el método End .
En el ejemplo siguiente, se muestra la signatura para la sobrecarga FromAsync que coincide con los métodos
FileStream.BeginRead y FileStream.EndRead. Esta sobrecarga toma los tres parámetros de entrada siguientes.
public Task<TResult> FromAsync<TArg1, TArg2, TArg3>(
Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod, //BeginRead
Func<IAsyncResult, TResult> endMethod, //EndRead
TArg1 arg1, // the byte[] buffer
TArg2 arg2, // the offset in arg1 at which to start writing data
TArg3 arg3, // the maximum number of bytes to read
object state // optional state information
)
El primer parámetro es un delegado Func<T1,T2,T3,T4,T5,TResult> que coincide con la signatura del método
FileStream.BeginRead. El segundo parámetro es un delegado Func<T,TResult> que toma una interfaz IAsyncResult
y devuelve TResult . Dado que EndRead devuelve un entero, el compilador deduce el tipo de TResult como
Int32 y el tipo de la tarea como Task. Los últimos cuatro parámetros son idénticos a los del método
FileStream.BeginRead:
Búfer donde se van a almacenar los datos de archivo.
Desplazamiento en el búfer donde deben comenzar a escribirse los datos.
Cantidad máxima de datos que se van a leer del archivo.
Un objeto opcional que almacena los datos de estado definidos por el usuario que se van a pasar a la
devolución de llamada.
Usar ContinueWith para la funcionalidad de devolución de llamada
Si necesita obtener acceso a los datos del archivo, en contraposición a solo el número de bytes, el método
FromAsync no es suficiente. En su ligar, use Task, cuya propiedad Result contiene los datos de archivo. Puede
hacer si agrega una continuación a la tarea original. La continuación realiza el trabajo que normalmente realizaría
el delegado AsyncCallback. Se invoca cuando se completa el antecedente y se ha rellenado el búfer de datos. (El
objeto FileStream se debería cerrar antes de devolver un valor).
En el siguiente ejemplo se muestra cómo devolver un objeto Task que encapsula el par BeginRead/EndRead de la
clase FileStream.
const int MAX_FILE_SIZE = 14000000;
public static Task<string> GetFileStringAsync(string path)
{
FileInfo fi = new FileInfo(path);
byte[] data = null;
data = new byte[fi.Length];
End Function
Task<string> t = GetFileStringAsync(path);
try
{
Console.WriteLine(t.Result.Substring(0, 500));
}
catch (AggregateException ae)
{
Console.WriteLine(ae.InnerException.Message);
}
Try
Console.WriteLine(myTask.Result.Substring(0, 500))
Catch ex As AggregateException
Console.WriteLine(ex.InnerException.Message)
End Try
Proporcionar los datos de estado personalizados
En las operaciones IAsyncResult típicas, si el delegado AsyncCallback requiere algún dato de estado
personalizado, tiene que pasarlo a través del último parámetro Begin para que los datos se puedan empaquetar
en el objeto IAsyncResult que se pasará finalmente al método de devolución de llamada. Normalmente no se
requiere esto cuando se usan los métodos FromAsync . Si los datos personalizados son conocidos para la
continuación, se pueden capturar directamente en el delegado de continuación. El siguiente ejemplo se parece el
ejemplo anterior, pero en lugar de examinar la propiedad Result del antecedente, la continuación examina los
datos de estado personalizados que son directamente accesibles al delegado de usuario de la continuación.
Return myTask.ContinueWith(Function(antecedent)
fs.Close()
' Capture custom state data directly in the user delegate.
' No need to pass it through the FromAsync method.
If (state.StateData.Contains("New York, New York")) Then
Return "Start spreading the news!"
End If
' If we did not receive the entire file, the end of the
' data buffer will contain garbage.
If (antecedent.Result < data.Length) Then
Array.Resize(data, antecedent.Result)
End If
'/ Will be returned in the Result property of the Task<string>
'/ at some future point after the asynchronous file I/O operation
completes.
Return New UTF8Encoding().GetString(data)
End Function)
End Function
return t;
}
Algunas clases que admiten EAP, por ejemplo, WebClient, admiten la cancelación y esa funcionalidad de
cancelación nativa se puede integrar mediante los tokens de cancelación.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
#region callback
// Specify the callback for the DownloadStringCompleted
// event that will be raised by this WebClient instance.
webClients[i].DownloadStringCompleted += (obj, args) =>
{
// Associate the results with the url, and add new string to the array that
// the underlying Task object will return in its Result property.
lock (m_lock)
{
results.Add(String.Format("{0} has {1} instances of {2}", args.UserState, nameCount, name));
Imports System.Collections.Generic
Imports System.Net
Imports System.Threading
Imports System.Threading
Imports System.Threading.Tasks
' If the user cancels the CancellationToken, then we can use the
' WebClient's ability to cancel its own async operations.
token.Register(Sub()
For Each wc As WebClient In webClients
If wc IsNot Nothing Then
wc.CancelAsync()
End If
Next
End Sub)
' Associate the results with the url, and add new string to the array that
' the underlying Task object will return in its Result property.
SyncLock (m_lock)
SyncLock (m_lock)
results.Add(String.Format("{0} has {1} instances of {2}", args.UserState, nameCount,
nameToSearch))
count = count + 1
If (count = addresses.Length) Then
tcs.TrySetResult(results.ToArray())
End If
End SyncLock
End If
End Sub
End Class
Para obtener un ejemplo más completo, en donde se incluye control de excepciones adicional y se muestra cómo
llamar al método desde código de cliente, vea Procedimiento: Encapsulado de patrones de EAP en una tarea.
Recuerde que TaskCompletionSource iniciará cualquier tarea creada por TaskCompletionSource<TResult> y, por
consiguiente, el código de usuario no debería llamar al método Start en esa tarea.
End Function
Private Function Compute(ByVal decimalPlaces As Integer)
Console.WriteLine("Calling compute on thread {0}", Thread.CurrentThread.ManagedThreadId)
Class CalculatorClient
Shared decimalPlaces As Integer
Shared Sub Main()
Dim calc As New Calculator
Dim places As Integer = 35
Dim callback As New AsyncCallback(AddressOf PrintResult)
Dim ar As IAsyncResult = calc.BeginCalculate(places, callback, calc)
End Class
Vea también
Biblioteca TPL
Cómo: Encapsular modelos de EAP en una tarea
16/09/2020 • 5 minutes to read • Edit Online
En el ejemplo siguiente se muestra cómo exponer una secuencia arbitraria de operaciones de modelo asincrónico
basado en eventos (EAP) como una tarea mediante el uso de una clase TaskCompletionSource<TResult>. En el
ejemplo también se muestra cómo usar una clase CancellationToken para invocar los métodos de cancelación
integrados en los objetos WebClient.
Ejemplo
class WebDataDownloader
{
// Do some other work here unless the method has already completed.
if (!webTask.IsCompleted)
{
// Simulate some work.
Thread.SpinWait(5000000);
}
#region callback
// Specify the callback for the DownloadStringCompleted
// event that will be raised by this WebClient instance.
webClients[i].DownloadStringCompleted += (obj, args) =>
{
if (args.Cancelled == true)
{
tcs.TrySetCanceled();
return;
}
else if (args.Error != null)
{
// Pass through to the underlying Task
// any exceptions thrown by the WebClient
// during the asynchronous operation.
tcs.TrySetException(args.Error);
return;
}
else
{
// Split the string into an array of words,
// then count the number of elements that match
// the search term.
string[] words = null;
words = args.Result.Split(' ');
string NAME = name.ToUpper();
int nameCount = (from word in words.AsParallel()
where word.ToUpper().Contains(NAME)
select word)
.Count();
// Associate the results with the url, and add new string to the array that
// the underlying Task object will return in its Result property.
results.Add(String.Format("{0} has {1} instances of {2}", args.UserState, nameCount,
name));
}
Class WebDataDownLoader
' Using a neutral search term that is sure to get some hits on English web sites.
' Please substitute your favorite search term.
downloader.nameToSearch = "the"
Dim webTask As Task(Of String()) = downloader.GetWordCounts(downloader.addresses,
downloader.nameToSearch, cts.Token)
' Do some other work here unless the method has already completed.
If (webTask.IsCompleted = False) Then
' Simulate some work
Thread.SpinWait(5000000)
End If
Next
Finally
cts.Dispose()
End Try
Public Function GetWordCounts(ByVal urls() As String, ByVal str As String, ByVal token As
CancellationToken) As Task(Of String())
' If the user cancels the CancellationToken, then we can use the
' WebClient's ability to cancel its own async operations.
token.Register(Sub()
For Each wc As WebClient In webClients
If Not wc Is Nothing Then
wc.CancelAsync()
End If
Next
End Sub)
Next
' Associate the results with the url, and add new string to the array that
' the underlying Task object will return in its Result property.
results.Add(String.Format("{0} has {1} instances of {2}", args.UserState, nameCount,
nameToSearch))
End If
SyncLock (m_lock)
count = count + 1
If (count = addresses.Length) Then
tcs.TrySetResult(results.ToArray())
End If
End SyncLock
End Sub
Vea también
TPL y la programación asincrónica tradicional de .NET Framework
Problemas potenciales en el paralelismo de datos y
tareas
16/09/2020 • 15 minutes to read • Edit Online
En muchos casos, Parallel.For y Parallel.ForEach pueden proporcionar importantes mejoras de rendimiento con
respecto a los bucles secuenciales normales. Sin embargo, el trabajo de paralelizar el bucle aporta una
complejidad que puede conducir a problemas que, en código secuencial, no son tan comunes o no se producen en
ningún caso. En este tema se indican algunas prácticas que se deben evitar al escribir bucles paralelos.
FileStream fs = File.OpenWrite(path);
byte[] bytes = new Byte[10000000];
// ...
Parallel.For(0, bytes.Length, (i) => fs.WriteByte(bytes[i]));
NOTE
Puede comprobarlo si inserta algunas llamadas a WriteLine en las consultas. Aunque este método se usa en los ejemplos de
la documentación para fines de demostración, no debe usarlo en bucles paralelos a menos que sea necesario.
If j = Environment.ProcessorCount Then
Console.WriteLine("Set on {0} with value of {1}",
Thread.CurrentThread.ManagedThreadId, j)
mres.Set()
Else
Console.WriteLine("Waiting on {0} with value of {1}",
Thread.CurrentThread.ManagedThreadId, j)
mres.Wait()
End If
End Sub) ' deadlocks
En este ejemplo, una iteración establece un evento y el resto de las iteraciones esperan el evento. Ninguna de las
iteraciones que esperan puede completarse hasta que se haya completado la iteración del valor de evento. Sin
embargo, es posible que las iteraciones que esperan bloqueen todos los subprocesos que se utilizan para ejecutar
el bucle paralelo antes de que la iteración del valor de evento haya tenido oportunidad de ejecutarse. Esto produce
un interbloqueo: la iteración del valor de evento nunca se ejecutará y las iteraciones que esperan nunca se
activarán.
En concreto, una iteración de un bucle paralelo no debe esperar nunca otra iteración del bucle para progresar. Si el
bucle paralelo decide programar las iteraciones secuencialmente pero en el orden contrario, se producirá un
interbloqueo.
En el siguiente ejemplo se muestra cómo evitar el interbloqueo mediante la ejecución del bucle dentro de una
instancia de la tarea. El bucle no bloquea el subproceso de la interfaz de usuario y se puede procesar el mensaje.
Parallel LINQ (PLINQ) es una implementación en paralelo del patrón Language-Integrated Query (LINQ).
PLINQ implementa el conjunto completo de operadores de consulta estándar de LINQ como métodos de
extensión para el espacio de nombres System.Linq y tiene operadores adicionales para las operaciones en
paralelo. PLINQ combina la simplicidad y legibilidad de la sintaxis de LINQ con la eficacia de la
programación en paralelo.
TIP
Si no está familiarizado con LINQ, ofrece un modelo unificado para consultar cualquier origen de datos enumerable
en un modo con seguridad de tipos. LINQ to Objects es el nombre de las consultas LINQ que se ejecutan en
colecciones en memoria, como List<T> y matrices. En este artículo, se da por echo que posee un conocimiento
básico de LINQ. Para obtener más información, vea Language Integrated Query (LINQ).
NOTE
En esta documentación, se utilizan expresiones lambda para definir delegados en PLINQ. Si no está familiarizado con
las expresiones lambda de C# o Visual Basic, consulte Expresiones lambda en PLINQ y TPL.
En el resto del artículo se ofrece información general de las principales clases PLINQ y se analiza cómo crear
consultas PLINQ. Cada sección incluye vínculos a información más detallada y ejemplos de código.
La clase ParallelEnumerable
La clase System.Linq.ParallelEnumerable expone casi toda la funcionalidad de PLINQ. Junto con el resto de
los tipos de espacios de nombres System.Linq, se compilan en el ensamblado System.Core.dll. Los
proyectos C# y Visual Basic predeterminados en Visual Studio hacen referencia al ensamblado e importan el
espacio de nombres.
ParallelEnumerable incluye implementaciones de todos los operadores de consulta estándar que LINQ to
Objects admite, a pesar de que no intenta ejecutar en paralelo cada uno de ellos. Si no está familiarizado
con LINK, consulte Introducción a LINQ (C#) e Introducción a LINQ (Visual Basic).
Además de los operadores de consulta estándar, la clase ParallelEnumerable contiene un conjunto de
métodos que permiten comportamientos específicos de la ejecución en paralelo. Estos métodos específicos
de PLINQ se muestran en la tabla siguiente.
El modelo de participación
Cuando escribe una consulta, participa en PLINQ al invocar el método de extensión
ParallelEnumerable.AsParallel en el origen de datos, tal como se muestra en el ejemplo siguiente.
var source = Enumerable.Range(1, 10000);
El método de extensión AsParallel enlaza los operadores de consulta subsiguientes, en este caso, where y
select , a las implementaciones System.Linq.ParallelEnumerable.
Modos de ejecución
De manera predeterminada, PLINQ es conservador. En tiempo de ejecución, la infraestructura de PLINQ
analiza la estructura general de la consulta. Si existe la probabilidad de que la consulta genere aumentos de
velocidad a través de la paralelización, PLINQ particiona la secuencia de origen en tareas que se pueden
ejecutar en simultáneo. Si ejecutar una consulta en paralelo no es seguro, PLINQ simplemente la ejecuta en
secuencia. Si PLINQ puede elegir entre un algoritmo en paralelo posiblemente costoso y un algoritmo
secuencial económico, de manera predeterminada elegirá el algoritmo secuencial. Puede usar el método
WithExecutionMode y la enumeración System.Linq.ParallelExecutionMode para indicar a PLINQ que
seleccione el algoritmo paralelo. Esto resulta útil cuando, a través de pruebas y mediciones, sabe que una
consulta determinada se ejecuta más rápido en paralelo. Para obtener más información, vea Cómo:
Especificar el modo de ejecución en PLINQ.
Grado de paralelismo
De manera predeterminada, PLINQ usa todos los procesadores del equipo host. Puede indicar a PLINQ que
use más de un número especificado de procesadores mediante el método WithDegreeOfParallelism. Esto
resulta útil cuando desea asegurarse de que otros procesos que estén en ejecución en el equipo reciban
cierta cantidad de tiempo de CPU. El siguiente fragmento de código limita la consulta para que solo use un
máximo de dos procesadores.
En casos en los que una consulta realiza una cantidad considerable de trabajo no enlazado a proceso, como
E/S de archivo, puede se recomendable especificar un grado de paralelismo mayor que el número de
núcleos de la máquina.
El operador ForAll
En las consultas LINK secuenciales, la ejecución se aplaza hasta que la consulta se enumera en un bucle
foreach ( For Each en Visual Basic) o mediante la invocación de un método como ToList, ToArray o
ToDictionary. En PLINQ, también puede usar foreach para ejecutar la consulta e iterar a través de los
resultados. Sin embargo, foreach no se ejecuta en paralelo y, por lo tanto, requiere que el resultado de
todas las tareas en paralelo se combinen nuevamente en el subproceso en el que se ejecuta el bucle. En
PLINQ, puede usar foreach cuando deba conservar la ordenación final de los resultados de la consulta y
también cada vez que procese los resultados de forma serial; por ejemplo, cuando llama a
Console.WriteLine para cada elemento. Para lograr un ejecución más rápida de las consultas cuando no es
necesario conservar el orden y cuando el procesamiento mismo de los resultados se puede ejecutar en
paralelo, use el método ForAll para ejecutar una consulta PLINQ. ForAll no lleva a cabo este paso de
combinación final. En el ejemplo de código siguiente, se muestra cómo se utiliza el método ForAll. Aquí se
usa System.Collections.Concurrent.ConcurrentBag<T> porque está optimizado para varios subprocesos, lo
que agrega simultaneidad sin intentar quitar ningún elemento.
En la ilustración siguiente se muestra la diferencia entre foreach y ForAll con respecto a la ejecución de la
consulta.
Cancelación
PLINQ se integra con los tipos de cancelación en .NET Framework 4. (Para más información, consulte el
tema sobre la cancelación en subprocesos administrados). Por lo tanto, a diferencia de las consultas LINQ to
Objects secuenciales, las consultas PLINQ se pueden cancelar. Para crear una consulta PLINQ cancelable, use
el operador WithCancellation en la consulta y proporcione una instancia CancellationToken como
argumento. Cuando la propiedad IsCancellationRequested del token se establece en true, PLINQ lo tendrá
en cuenta, detendrá el procesamiento de todos los subprocesos e iniciará OperationCanceledException.
Es posible que una consulta PLINQ siga procesando algunos elementos una vez establecido el token de
cancelación.
Para una mayor capacidad de respuesta, también puede responder a las solicitudes de cancelación en
delegados de usuario de ejecución prolongada. Para obtener más información, vea Cómo: Cancelar una
consulta PLINQ.
Excepciones
Cuando se ejecuta una consulta PLINQ, distintos subprocesos pueden generar varias excepciones de forma
simultánea. Además, el código para controlar la excepción puede estar en un subproceso distinto al del
código que generó la excepción. PLINQ usa el tipo AggregateException para encapsular todas las
excepciones que generó una consulta y calcular las referencias de esas excepciones nuevamente en el
subproceso que realiza la llamada. En el subproceso que realiza la llamada, solo se requiere un bloque try-
catch. Sin embargo, puede iterar a través de todas las excepciones que están encapsuladas en
AggregateException y capturar cualquiera desde la que pueda realizar una recuperación de forma segura.
En raras ocasiones, se pueden generar algunas excepciones que no se ajustan en AggregateException, y
ThreadAbortException tampoco se ajusta.
Cuando las excepciones pueden propagarse de nuevo al subproceso de unión, es posible que una consulta
continúe procesando algunos elementos después de que se haya producido la excepción.
Para obtener más información, vea Cómo: Controlar excepciones en una consulta PLINQ.
Particionadores personalizados
En algunos casos, puede mejorar el rendimiento de las consultas si escribe un particionador personalizado
que aproveche algunas características de los datos de origen. En la consulta, el mismo particionador
personalizado es el objeto enumerable que se consulta.
PLINQ admite una cantidad fija de particiones (a pesar de que los datos se pueden reasignar de forma
dinámica a esas particiones durante el tiempo de ejecución para el equilibrio de carga). For y ForEach
admiten solo la partición dinámica, lo que significa que el número de particiones cambia en tiempo de
ejecución. Para más información, consulte Custom Partitioners for PLINQ and TPL (Particionadores
personalizados para PLINQ y TPL).
Vea también
Parallel LINQ (PLINQ)
Introducción a la velocidad en PLINQ
Introducción a la velocidad en PLINQ
16/09/2020 • 12 minutes to read • Edit Online
El objetivo principal de PLINQ es acelerar la ejecución de consultas LINQ to Objects mediante la ejecución de los
delegados de consulta en paralelo en equipos de varios núcleos. PLINQ funciona mejor cuando el
procesamiento de cada elemento de una colección de origen es independiente, sin ningún estado compartido
implicado entre los delegados individuales. Estas operaciones son comunes en LINQ to Objects y PLINQ y a
menudo se denominan "perfectamente paralelas" porque se prestan fácilmente a la programación en varios
subprocesos. Sin embargo, no todas las consultas constan de operaciones paralelas perfectas; en la mayoría de
los casos, una consulta incluye algunos operadores que no se pueden paralelizar o que ralentizan la ejecución
en paralelo. E incluso con las consultas que son perfectamente paralelas, PLINQ debe crear particiones del
origen de datos y programar el trabajo en los subprocesos y generalmente tiene que combinar los resultados
cuando finaliza la consulta. Todas estas operaciones aumentan el costo computacional de la paralelización; a
estos costos derivados de agregar la paralelización se les denomina sobrecarga. Para lograr un rendimiento
óptimo de una consulta PLINQ, el objetivo es maximizar las partes que son perfectamente paralelas y minimizar
las partes que requieren sobrecarga. Este artículo proporciona información que le ayudará a escribir consultas
PLINQ tan eficaces como sea posible mientras se siguen produciendo resultados correctos.
Vea también
Parallel LINQ (PLINQ)
Conservar el orden en PLINQ
16/09/2020 • 10 minutes to read • Edit Online
En PLINQ, el objetivo es maximizar el rendimiento manteniendo la exactitud. Una consulta se debería ejecutar lo
más rápido que fuese posible pero con resultados correctos. La exactitud exige que se conserve el orden de la
secuencia de origen en algunos casos; sin embargo, la ordenación puede suponer la utilización de muchos
recursos de computación. Por consiguiente, de forma predeterminada, PLINQ no conserva el orden de la
secuencia de origen. En este sentido, PLINQ se parece a LINQ to SQL, pero es diferente de LINQ to Objects, que
conserva el orden.
Para reemplazar el comportamiento predeterminado, puede activar la capacidad de conservar el orden utilizando
el operador AsOrdered en la secuencia de origen. Después, puede desactivarla en la consulta, utilizando el método
AsUnordered. Con ambos métodos, se procesa la consulta basándose en la heurística que determina si la consulta
se debe ejecutar de forma paralela o secuencial. Para más información, consulte Introducción a la velocidad en
PLINQ.
En el siguiente ejemplo se muestra una consulta paralela no ordenada que filtra todos los elementos que
coinciden con una condición, sin intentar ordenar los resultados de forma alguna.
Esta consulta no obtiene necesariamente las 1000 primeras ciudades de la secuencia de origen que cumplen la
condición, sino que algún conjunto de las 1000 ciudades que la cumplen. Los operadores de consulta PLINQ
particionan la secuencia de origen en varias secuencias secundarias que se procesan como tareas simultáneas. Si
no se especifica que se conserve el orden, los resultados de cada partición se presentan a la siguiente etapa de la
consulta con un orden arbitrario. Por otra parte, una partición puede producir un subconjunto de los resultados
antes de continuar procesando los elementos restantes. El orden resultante puede ser diferente cada vez. Una
aplicación no puede controlar este hecho, porque depende de cómo programe los subprocesos el sistema
operativo.
En el siguiente ejemplo se reemplaza el comportamiento predeterminado utilizando al operador AsOrdered en la
secuencia de origen. De esta forma se garantiza que el método Take devuelve las 1000 primeras ciudades de la
secuencia de origen que cumplen la condición.
Sin embargo, esta consulta probablemente no se ejecute tan rápido como la versión no ordenada, porque debe
realizar el seguimiento del orden original en todas las particiones y, en el momento de la fusión mediante
combinación, garantizar que el orden es coherente. Por consiguiente, recomendamos usar AsOrdered solo cuando
sea estrictamente necesario y únicamente para las partes de la consulta que lo requieran. Cuando ya no sea
necesario conservar el orden, use AsUnordered para desactivarlo. En el siguiente ejemplo se consigue mediante la
creación de dos consultas.
Observe que PLINQ conserva el orden de una secuencia generada por operadores que imponen el orden para el
resto de la consulta. En otras palabras, los operadores de tipo OrderBy y ThenBy se tratan como si fuesen
seguidos de una llamada a AsOrdered.
Los resultados desordenados no se ordenan aleatoriamente de forma activa; simplemente no se les aplica
ninguna lógica de ordenación especial. En algunos casos, una consulta desordenada puede conservar el orden de
la secuencia de origen. En las consultas que usan el operador Select indizado, PLINQ garantiza que los elementos
de salida aparecerán en el orden de los índices ascendentes, pero no ofrece ninguna garantía sobre qué índices se
asignarán a qué elementos.
Vea también
Parallel LINQ (PLINQ)
Programación en paralelo
Opciones de fusión mediante combinación en PLINQ
16/09/2020 • 7 minutes to read • Edit Online
Cuando una consulta se ejecuta en paralelo, PLINQ crea particiones de la secuencia de origen para que varios
subprocesos puedan funcionar en diferentes partes al mismo tiempo, por lo general en subprocesos
independientes. Si los resultados se van a usar en un subproceso, por ejemplo, en un bucle foreach ( For Each en
Visual Basic), los resultados de cada subproceso deben volver a combinarse en una secuencia. El tipo de
combinación que PLINQ realiza depende de los operadores que están presentes en la consulta. Por ejemplo, los
operadores que imponen un nuevo orden de los resultados deben almacenar en búfer todos los elementos de
todos los subprocesos. Desde la perspectiva del subproceso utilizado (que también es el del usuario de la
aplicación), una consulta totalmente almacenada en búfer podría ejecutarse durante un período de tiempo
considerable antes de generar su primer resultado. Otros operadores, de forma predeterminada, están
parcialmente almacenados en búfer; producen sus resultados en lotes. Un operador, ForAll, no se almacena en
búfer de forma predeterminada. Genera inmediatamente todos los elementos de todos los subprocesos.
Mediante el uso del método WithMergeOptions, como se muestra en el ejemplo siguiente, puede proporcionar
una sugerencia a PLINQ que indica qué tipo de combinación se debe llevar a cabo.
Para obtener el ejemplo completo, vea Cómo: Especificar opciones de fusión mediante combinación en PLINQ.
Si la consulta determinada no puede admitir la opción solicitada, simplemente se omitirá la opción. En la mayoría
de los casos, no es necesario especificar una opción de combinación para una consulta PLINQ. Sin embargo, en
algunos casos puede observar mediante pruebas y mediciones que una consulta se ejecuta mejor en un modo no
predeterminado. Un uso común de esta opción es forzar a un operador de combinación de fragmentos a
transmitir por secuencias sus resultados con el fin de proporcionar una interfaz de usuario más dinámica.
ParallelMergeOptions
La enumeración ParallelMergeOptions incluye las siguientes opciones que especifican, para las formas de consulta
compatibles, cómo se produjo el resultado final de la consulta cuando se usan los resultados en un subproceso:
Not Buffered
La opción NotBuffered hace que cada elemento procesado devuelva cada subproceso en cuanto se
produzca. Este comportamiento es análogo a la salida "streaming". Si el operador AsOrdered está presente
en la consulta, NotBuffered conserva el orden de los elementos de origen. Aunque NotBuffered empieza a
producir resultados en cuanto están disponibles, el tiempo total para generar todos los resultados puede
ser más largo con respecto al uso de alguna de las demás opciones de combinación.
Auto Buffered
La opción AutoBuffered hace que la consulta recopile los elementos en un búfer y, a continuación,
proporcionará periódicamente el contenido del búfer a la vez para el subproceso utilizado. Esto es análogo
a producir los datos de origen en "fragmentos" en lugar de usar el comportamiento de "streaming" de
NotBuffered . AutoBuffered puede tardar más que NotBuffered en habilitar el primer elemento en el
subproceso utilizado. El tamaño del búfer y el comportamiento productivo exacto no se pueden configurar
y pueden variar en función de varios factores relacionados con la consulta.
FullyBuffered
La opción FullyBuffered hace que el resultado de la consulta completa se almacene en búfer antes de que
se produzca cualquiera de los elementos. Al usar esta opción, puede tardar más tiempo antes de que el
primer elemento esté disponible en el subproceso utilizado, pero los resultados completos pueden
producirse más rápido en comparación con el uso de otras opciones.
AsEnumerable None
Cast None
DefaultIfEmpty None
OfType None
Select None
SelectMany None
Skip None
Take None
Where None
Todos los demás operadores de consulta PLINQ podrían omitir opciones de combinación proporcionadas por el
usuario. Algunos operadores de consulta, por ejemplo Reverse y OrderBy, no pueden proporcionar todos los
elementos hasta que no se hayan producido y reordenado. Por lo tanto, cuando se utiliza ParallelMergeOptions en
una consulta que también contiene un operador como Reverse, el comportamiento de combinación no se aplicará
en la consulta hasta después de que el operador genere sus resultados.
La capacidad de algunos operadores para controlar las opciones de combinación depende del tipo de la secuencia
de origen y de si el operador AsOrdered se usó anteriormente en la consulta. ForAll siempre es NotBuffered;
produce inmediatamente sus elementos. OrderBy siempre es FullyBuffered; debe ordenar toda la lista antes de
producirla.
Vea también
Parallel LINQ (PLINQ)
Cómo: Especificar opciones de fusión mediante combinación en PLINQ
Posibles problemas con PLINQ
16/09/2020 • 11 minutes to read • Edit Online
En muchos casos, PLINQ puede proporcionar importantes mejoras de rendimiento con respecto a las consultas
secuenciales LINQ to Objects. Sin embargo, el trabajo de paralelizar la ejecución de consultas aporta una
complejidad que puede conducir a problemas que, en código secuencial, no son tan comunes o no se producen en
ningún caso. En este tema se indican algunas prácticas que se deben evitar al escribir consultas PLINQ.
En este caso, es mejor paralelizar únicamente el origen de datos exterior (clientes), a menos que se cumplan una o
varias de las siguientes condiciones:
Se sabe que el origen de datos interno (cust.Orders) es muy largo.
Se realiza un cálculo costoso en cada pedido (la operación que se muestra en el ejemplo no es costosa).
Se sabe que el sistema de destino tiene suficientes procesadores como para controlar el número de
subprocesos que se producirán al paralelizar la consulta de cust.Orders .
En todos los casos, la mejor manera de determinar la forma óptima de la consulta es mediante la prueba y la
medición. Para obtener más información, vea Cómo: Medir el rendimiento de consultas PLINQ.
FileStream fs = File.OpenWrite(...);
a.AsParallel().Where(...).OrderBy(...).Select(...).ForAll(x => fs.Write(x));
NOTE
Puede comprobarlo si inserta algunas llamadas a WriteLine en las consultas. Aunque este método se usa en los ejemplos de
la documentación para fines de demostración, no debe usarlo en consultas PLINQ.
En este ejemplo, una iteración establece un evento y el resto de las iteraciones esperan el evento. Ninguna de las
iteraciones que esperan puede completarse hasta que se haya completado la iteración del valor de evento. Sin
embargo, es posible que las iteraciones que esperan bloqueen todos los subprocesos que se utilizan para ejecutar
el bucle paralelo antes de que la iteración del valor de evento haya tenido oportunidad de ejecutarse. Esto produce
un interbloqueo: la iteración del valor de evento nunca se ejecutará y las iteraciones que esperan nunca se
activarán.
En concreto, una iteración de un bucle paralelo no debe esperar nunca otra iteración del bucle para progresar. Si el
bucle paralelo decide programar las iteraciones secuencialmente pero en el orden contrario, se producirá un
interbloqueo.
Vea también
Parallel LINQ (PLINQ)
Procedimiento para crear y ejecutar una consulta
PLINQ simple
16/09/2020 • 3 minutes to read • Edit Online
En el ejemplo de este artículo se muestra cómo crear una consulta sencilla de Parallel Language Integrated Query
(LINQ); para ello, se utiliza el método de extensión ParallelEnumerable.AsParallel en la secuencia de origen y se
ejecuta la consulta con el método ForAll.
NOTE
En esta documentación, se utilizan expresiones lambda para definir delegados en PLINQ. Si no está familiarizado con las
expresiones lambda de C# o Visual Basic, consulte Expresiones lambda en PLINQ y TPL.
Ejemplo
using System;
using System.Linq;
// You can also use ToArray, ToList, etc as with LINQ to Objects.
var parallelQuery2 = (from num in source.AsParallel()
where num % 10 == 0
select num).ToArray();
Module Example
Public Sub Main()
Dim source = Enumerable.Range(100, 20000)
' You can also use ToArray, ToList, etc, as with LINQ to Objects.
Dim parallelQuery2 = (From num In source.AsParallel()
Where num Mod 10 = 0
Select num).ToArray()
Console.WriteLine()
Console.WriteLine("Press any key to exit...")
Console.ReadLine()
End Sub
En este ejemplo se muestra el patrón básico para la creación y ejecución de cualquier consulta Parallel LINQ cuando
el orden de la secuencia de resultados no importa. Por lo general, las consultas desordenadas son más rápidas que
las ordenadas. La consulta crea particiones del origen, dividiéndolo en tareas que se ejecutan de forma asincrónica
en varios subprocesos. El orden en el que se completa cada tarea no depende solo de la cantidad de trabajo
necesario para procesar los elementos de la partición, sino también de factores externos como, por ejemplo, el
modo en que el sistema operativo programa cada uno de los subprocesos. La finalidad de este ejemplo es mostrar
el uso, y puede que su ejecución no sea tan rápida como la de la consulta LINQ to Objects secuencial equivalente.
Para más información sobre la velocidad, vea Introducción a la velocidad en PLINQ. Para obtener más información
sobre cómo mantener el orden de los elementos de una consulta, vea Procedimiento para controlar la ordenación
en una consulta PLINQ.
Vea también
Parallel LINQ (PLINQ)
Cómo: Controlar la ordenación en una consulta
PLINQ
16/09/2020 • 4 minutes to read • Edit Online
En estos ejemplos se muestra cómo controlar la ordenación de una consulta PLINQ con el método de extensión
AsOrdered.
WARNING
La finalidad principal de estos ejemplos es mostrar el uso, y puede que su ejecución sea o no tan rápida como la de las
consultas LINQ to Objects secuenciales equivalentes.
Ejemplo
En el ejemplo siguiente se conserva el orden de la secuencia de origen. Esto a veces es necesario; por ejemplo,
algunos operadores de consulta requieren una secuencia de origen ordenada para generar resultados correctos.
Sub OrderedQuery()
End Sub
Ejemplo
En el ejemplo siguiente se muestran algunos operadores de consulta cuya secuencia de origen probablemente se
espera que esté ordenada. Estos operadores pueden funcionar en secuencias no ordenadas, pero podrían producir
resultados inesperados.
End Sub
Para ejecutar este método, péguelo en la clase PLINQDataSample en el ejemplo de datos de PLINQ del proyecto y
presione F5.
Ejemplo
En el ejemplo siguiente se muestra cómo se conserva el orden de la primera parte de una consulta, después se
quita el orden para aumentar el rendimiento de una cláusula de combinación y, por último, se vuelve a aplicar el
orden de la secuencia del resultado final.
// Paste into PLINQDataSample class.
static void OrderedThenUnordered()
{
var q2 = orders.AsParallel()
.Where(o => o.OrderDate < DateTime.Parse("07/04/1997"))
.Select(o => o)
.OrderBy(o => o.CustomerID) // Preserve original ordering for Take operation.
.Take(20)
.AsUnordered() // Remove ordering constraint to make join faster.
.Join(
orderDetails.AsParallel(),
ord => ord.OrderID,
od => od.OrderID,
(ord, od) =>
new
{
ID = ord.OrderID,
Customer = ord.CustomerID,
Product = od.ProductID
}
)
.OrderBy(i => i.Product); // Apply new ordering to final result sequence.
Para ejecutar este método, péguelo en la clase PLINQDataSample en el ejemplo de datos de PLINQ del proyecto y
presione F5.
Vea también
ParallelEnumerable
Parallel LINQ (PLINQ)
Procedimiento para combinar consultas LINQ
paralelas y secuenciales
16/09/2020 • 2 minutes to read • Edit Online
Este ejemplo muestra cómo utilizar el método AsSequential para indicar a PLINQ que procese secuencialmente
todos los operadores subsiguientes en la consulta. Aunque el procesamiento secuencial suele ser más lento que el
paralelo, a veces es necesario para generar resultados correctos.
NOTE
La finalidad de este ejemplo es mostrar el uso, y puede que su ejecución no sea tan rápida como la de la consulta LINQ to
Objects secuencial equivalente. Para más información sobre la velocidad, vea Introducción a la velocidad en PLINQ.
Ejemplo
En el ejemplo siguiente se muestra un escenario en el que AsSequential es necesario, concretamente, para
conservar el orden que se estableció en una cláusula de la consulta anterior.
Vea también
Parallel LINQ (PLINQ)
Cómo: Controlar excepciones en una consulta PLINQ
16/09/2020 • 6 minutes to read • Edit Online
El primer ejemplo de este tema muestra cómo controlar la clase System.AggregateException que se puede iniciar
desde una consulta PLINQ cuando se ejecuta. El segundo ejemplo muestra cómo colocar bloques try-catch dentro
de los delegados, lo más cerca posible de donde se producirá la excepción. De este modo, se pueden detectar en
cuanto se producen y posiblemente continuar con la ejecución de la consulta. Cuando las excepciones pueden
propagarse de nuevo al subproceso de unión, es posible que una consulta continúe procesando algunos
elementos después de que se haya producido la excepción.
En algunos casos, cuando PLINQ vuelve a realizar la ejecución por secuencias y cuando se produce una excepción,
esta puede propagarse directamente y no ajustarse en una excepción AggregateException. Además, las
excepciones ThreadAbortException siempre se propagan directamente.
NOTE
Cuando está habilitada la opción "Solo mi código", Visual Studio se interrumpe en la línea que produce la excepción y
muestra el mensaje de error "Excepción no controlada por el código de usuario". Este error es benigno. Puede presionar F5
para continuar y ver el comportamiento de control de excepciones que se muestra en estos ejemplos. Para evitar que Visual
Studio se interrumpa con el primer error, desactive la casilla "Solo mi código" en Herramientas, Opciones, Depurar,
General.
La finalidad de este ejemplo es mostrar el uso, y puede que su ejecución no sea tan rápida como la de la consulta LINQ to
Objects secuencial equivalente. Para más información sobre la velocidad, vea Introducción a la velocidad en PLINQ.
Ejemplo
Este ejemplo muestra cómo colocar los bloques try-catch alrededor del código que ejecuta la consulta para
detectar cualquier excepción System.AggregateException que se produzca.
// Paste into PLINQDataSample class.
static void PLINQExceptions_1()
{
// Using the raw string array here. See PLINQ Data Sample.
string[] customers = GetCustomersAsStrings().ToArray();
' Using the raw string array here. See PLINQ Data Sample.
Dim customers As String() = GetCustomersAsStrings().ToArray()
'throws indexoutofrange
Dim query = From cust In customers.AsParallel() _
Let fields = cust.Split(","c) _
Where fields(3).StartsWith("C") _
Select fields
Try
' We use ForAll although it doesn't really improve performance
' since all output is serialized through the Console.
query.ForAll(Sub(e)
Console.WriteLine("City: {0}, Thread:{1}")
End Sub)
Catch e As AggregateException
' In this design, we stop query processing when the exception occurs.
For Each ex In e.InnerExceptions
Console.WriteLine(ex.Message)
If TypeOf ex Is IndexOutOfRangeException Then
Console.WriteLine("The data source is corrupt. Query stopped.")
End If
Next
End Try
End Sub
En este ejemplo, la consulta no puede continuar después de que se produce la excepción. En el momento en que el
código de la aplicación detecta la excepción, PLINQ ya ha detenido la consulta en todos los subprocesos.
Ejemplo
En el ejemplo siguiente se muestra cómo colocar un bloque try-catch en un delegado para que sea posible
detectar una excepción y continuar con la ejecución de la consulta.
Try
Catch e As IndexOutOfRangeException
Compilar el código
Para compilar y ejecutar estos ejemplos, cópielos en el ejemplo de datos de PLINQ y llame al método desde
Main.
Programación sólida
No se detecta ninguna excepción a menos que sepa cómo controlarla para que no se dañe el estado del programa.
Vea también
ParallelEnumerable
Parallel LINQ (PLINQ)
Procedimiento para cancelar una consulta PLINQ
16/09/2020 • 9 minutes to read • Edit Online
En los siguientes ejemplos se muestran dos formas de cancelar una consulta PLINQ. El primer ejemplo muestra
cómo cancelar una consulta que se compone principalmente de un cruce seguro de datos. El segundo ejemplo
muestra cómo cancelar una consulta que contiene una función de usuario que es cara desde el punto de vista
computacional.
NOTE
Cuando está habilitada la opción "Solo mi código", Visual Studio se interrumpe en la línea que produce la excepción y
muestra el mensaje de error "Excepción no controlada por el código de usuario". Este error es benigno. Puede presionar F5
para continuar y ver el comportamiento de control de excepciones que se muestra en estos ejemplos. Para evitar que Visual
Studio se interrumpa con el primer error, desactive la casilla "Solo mi código" en Herramientas, Opciones, Depurar,
General.
La finalidad de este ejemplo es mostrar el uso, y puede que su ejecución no sea tan rápida como la de la consulta LINQ to
Objects secuencial equivalente. Para más información sobre la velocidad, vea Introducción a la velocidad en PLINQ.
Ejemplo
namespace PLINQCancellation_1
{
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using static System.Console;
class Program
{
static void Main(string[] args)
{
int[] source = Enumerable.Range(1, 10000000).ToArray();
var cts = new CancellationTokenSource();
if (results != null)
{
foreach (var v in results)
WriteLine(v);
}
WriteLine();
ReadKey();
}
Console.WriteLine(e.Message)
Catch ae As AggregateException
Console.ReadKey()
End Sub
Ejemplo
En el ejemplo siguiente se muestra cómo controlar la cancelación cuando se tiene una función cara desde el punto
de vista computacional en el código de usuario.
namespace PLINQCancellation_2
{
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using static System.Console;
class Program
{
static void Main(string[] args)
{
int[] source = Enumerable.Range(1, 10000000).ToArray();
var cts = new CancellationTokenSource();
if (results != null)
{
foreach (var v in results)
WriteLine(v);
}
WriteLine();
ReadKey();
ReadKey();
}
Class Program2
Private Shared Sub Main(ByVal args As String())
UserClicksTheCancelButton(cs)
End Sub)
Console.WriteLine(e.Message)
Catch ae As AggregateException
If ae.InnerExceptions IsNot Nothing Then
For Each e As Exception In ae.InnerExceptions
Console.WriteLine(e.Message)
Next
End If
Finally
Finally
cs.Dispose()
End Try
Console.ReadKey()
End Sub
End If
End Sub
End Class
Al controlar la cancelación en el código de usuario, no tiene que usar WithCancellation en la definición de consulta.
Sin embargo, se recomienda el uso de WithCancellation, porque WithCancellation no tiene ningún efecto en el
rendimiento de las consultas y permite que los operadores de consulta y el código de usuario controlen la
cancelación.
Con el fin de garantizar la capacidad de respuesta del sistema, se recomienda que compruebe la cancelación una
vez por cada milisegundo; sin embargo, cualquier período que transcurre hasta diez milisegundos se considera
aceptable. Esta frecuencia no debe tener un impacto negativo en el rendimiento del código.
Cuando se elimina un enumerador, por ejemplo cuando el código desencadena un bucle foreach (For Each en
Visual Basic) que está iterando en los resultados de la consulta, se cancela la consulta, pero no se inicia ninguna
excepción.
Vea también
ParallelEnumerable
Parallel LINQ (PLINQ)
Cancelación en subprocesos administrados
Cómo: Escribir una función de agregado
personalizada de PLINQ
16/09/2020 • 3 minutes to read • Edit Online
En este ejemplo se muestra cómo utilizar el método Aggregate para aplicar una función de agregación
personalizada a una secuencia de origen.
WARNING
La finalidad de este ejemplo es mostrar el uso, y puede que su ejecución no sea tan rápida como la de la consulta LINQ to
Objects secuencial equivalente. Para más información sobre la velocidad, vea Introducción a la velocidad en PLINQ.
Ejemplo
En el ejemplo siguiente se calcula la desviación estándar de una secuencia de enteros.
namespace PLINQAggregation
{
using System;
using System.Linq;
class aggregation
{
static void Main(string[] args)
{
Console.ReadLine()
End Sub
End Class
Este ejemplo utiliza una sobrecarga del operador de consulta estándar agregado que sea único en PLINQ. Esta
sobrecarga adopta un delegado adicional System.Func<T1,T2,TResult> como tercer parámetro de entrada. Este
delegado combina los resultados de todos los subprocesos antes de realizar el cálculo final de los resultados
agregados. En este ejemplo se agregan las sumas de todos los subprocesos.
Tenga en cuenta que, cuando un cuerpo de expresiones lambda consta de una única expresión, el valor devuelto del
delegado System.Func<T,TResult> es el valor de la expresión.
Vea también
ParallelEnumerable
Parallel LINQ (PLINQ)
Procedimiento para especificar el modo de ejecución
en PLINQ
16/09/2020 • 2 minutes to read • Edit Online
En este ejemplo se muestra cómo forzar a PLINQ a omitir su heurística predeterminada y paralelizar una consulta,
independientemente de la forma de la consulta.
NOTE
La finalidad de este ejemplo es mostrar el uso y puede que su ejecución no sea tan rápida como la de la consulta LINQ to
Objects secuencial equivalente. Para más información sobre la velocidad, vea Introducción a la velocidad en PLINQ.
Ejemplo
// Paste into PLINQDataSample class.
static void ForceParallel()
{
var customers = GetCustomers();
var parallelQuery = (from cust in customers.AsParallel()
.WithExecutionMode(ParallelExecutionMode.ForceParallelism)
where cust.City == "Berlin"
select cust.CustomerName)
.ToList();
}
PLINQ está diseñado para aprovechar las oportunidades para la paralelización. Sin embargo, no todas las
consultas se beneficiarán de la ejecución en paralelo. Por ejemplo, cuando una consulta contiene un delegado de
usuario único que realiza poco trabajo, la consulta normalmente se ejecutará con mayor rapidez de forma
secuencial. La ejecución secuencial es más rápida porque la sobrecarga necesaria para habilitar la ejecución en
paralelo es más cara que la velocidad que se obtiene. Por lo tanto, PLINQ no paraleliza automáticamente todas las
consultas. Primero examina la forma de la consulta y los diversos operadores que la componen. En función de este
análisis, PLINQ en el modo de ejecución predeterminado puede decidir ejecutar algunas o todas las consultas de
forma secuencial. Sin embargo, en algunos casos, puede saber más sobre su consulta de lo que PLINQ es capaz de
determinar a partir de su análisis. Por ejemplo, puede saber que un delegado es caro y que la consulta se
beneficiará definitivamente de la paralelización. En tales casos, puede usar el método WithExecutionMode y
especificar el valor ForceParallelism para indicar a PLINQ que ejecute siempre la consulta de forma paralela.
Compilar el código
Corte y pegue este código en el ejemplo de datos de PLINQ y llame al método desde Main .
Vea también
AsSequential
Parallel LINQ (PLINQ)
Cómo: Especificar opciones de combinación en
PLINQ
16/09/2020 • 2 minutes to read • Edit Online
En este ejemplo se muestra cómo especificar las opciones de combinación que se aplicarán a todos los operadores
subsiguientes en una consulta PLINQ. No es necesario establecer las opciones de combinación explícitamente,
pero, en caso de establecerlas, se puede mejorar el rendimiento. Para más información sobre las opciones de
combinación, consulte las opciones de combinación en PLINQ.
WARNING
La finalidad de este ejemplo es mostrar el uso, y puede que su ejecución no sea tan rápida como la de la consulta LINQ to
Objects secuencial equivalente. Para más información sobre la velocidad, vea Introducción a la velocidad en PLINQ.
Ejemplo
En el ejemplo siguiente se muestra el comportamiento de las opciones de combinación en un escenario básico
que tiene un origen no ordenado y aplica una función cara a cada elemento.
namespace MergeOptions
{
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
class Program
{
static void Main(string[] args)
{
Stopwatch sw = Stopwatch.StartNew();
foreach (var line in scanLines)
{
Console.WriteLine(line);
}
Sub DoMergeOptions()
Dim sw = System.Diagnostics.Stopwatch.StartNew()
For Each line In scanLines
Console.WriteLine(line)
Next
End Sub
' A function that demonstrates what a fly
' sees when it watches television :-)
Function ExpensiveFunc(ByVal i As Integer) As String
System.Threading.Thread.SpinWait(2000000)
Return String.Format("{0} *****************************************", i)
End Function
End Class
En casos donde la opción AutoBuffered incurre en una latencia indeseable antes de que se produzca el primer
elemento, pruebe la opción NotBuffered para proporcionar elementos de resultados más rápido y sin problemas.
Vea también
ParallelMergeOptions
Parallel LINQ (PLINQ)
Procedimiento para iterar directorios con PLINQ
16/09/2020 • 5 minutes to read • Edit Online
En este artículo se muestran dos maneras de paralelizar operaciones en directorios de archivos. La primera
consulta utiliza el método GetFiles para rellenar una matriz de nombres de archivo en un directorio y en todos los
subdirectorios. Este método puede generar latencia al principio de la operación porque no realiza ninguna
devolución hasta que se rellena toda la matriz. Sin embargo, una vez que se rellena la matriz, PLINQ puede
procesarla en paralelo rápidamente.
La segunda consulta utiliza los métodos estáticos EnumerateDirectories y EnumerateFiles, que empiezan a
devolver resultados inmediatamente. Este enfoque puede ser más rápido cuando esté iterando los árboles de
directorio de gran tamaño, pero el tiempo de procesamiento en comparación con el primer ejemplo depende de
muchos factores.
NOTE
La finalidad de estos ejemplos es mostrar el uso, y puede que su ejecución no sea tan rápida como la de la consulta LINQ to
Objects secuencial equivalente. Para más información sobre la velocidad, vea Introducción a la velocidad en PLINQ.
Ejemplo de GetFiles
En el ejemplo siguiente se muestra cómo iterar directorios de archivos en escenarios sencillos cuando se tiene
acceso a todos los directorios del árbol, los tamaños de archivo no son grandes y los tiempos de acceso no son
significativos. Este enfoque implica un período de latencia al principio, mientras que la matriz de nombres de
archivo se está construyendo.
struct FileResult
{
public string Text;
public string FileName;
}
// Use Directory.GetFiles to get the source sequence of file names.
public static void FileIteration_1(string path)
{
var sw = Stopwatch.StartNew();
int count = 0;
string[] files = null;
try
{
files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
}
catch (UnauthorizedAccessException e)
{
Console.WriteLine("You do not have permission to access one or more folders in this directory tree.");
return;
}
catch (FileNotFoundException)
{
Console.WriteLine("The specified directory {0} was not found.", path);
}
try
{
foreach (var item in fileContents)
{
Console.WriteLine(Path.GetFileName(item.FileName) + ":" + item.Text.Length);
count++;
}
}
catch (AggregateException ae)
{
ae.Handle((ex) =>
{
if (ex is UnauthorizedAccessException)
{
Console.WriteLine(ex.Message);
return true;
}
return false;
});
}
Ejemplo de EnumerateFiles
En el ejemplo siguiente se muestra cómo iterar directorios de archivos en escenarios sencillos cuando se tiene
acceso a todos los directorios del árbol, los tamaños de archivo no son grandes y los tiempos de acceso no son
significativos. Este enfoque comienza con la generación de resultados con más rapidez que en el ejemplo anterior.
struct FileResult
{
public string Text;
public string FileName;
}
// Use Directory.EnumerateDirectories and EnumerateFiles to get the source sequence of file names.
public static void FileIteration_2(string path) //225512 ms
{
var count = 0;
var sw = Stopwatch.StartNew();
var fileNames = from dir in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories)
select dir;
var fileContents = from file in fileNames.AsParallel() // Use AsOrdered to preserve source ordering
let extension = Path.GetExtension(file)
where extension == ".txt" || extension == ".htm"
let Text = File.ReadAllText(file)
select new { Text, FileName = file }; //Or ReadAllBytes, ReadAllLines, etc.
try
{
foreach (var item in fileContents)
{
Console.WriteLine(Path.GetFileName(item.FileName) + ":" + item.Text.Length);
count++;
}
}
catch (AggregateException ae)
{
ae.Handle((ex) =>
{
if (ex is UnauthorizedAccessException)
{
Console.WriteLine(ex.Message);
return true;
}
return false;
});
}
Cuando se usa GetFiles, asegúrese de que tiene suficientes permisos en todos los directorios del árbol. De lo
contrario, se producirá una excepción y no se devolverá ningún resultado. Cuando se usa EnumerateDirectories en
una consulta PLINQ, resulta problemático controlar las excepciones de E/S de una forma correcta que le permita
continuar la iteración. Si el código debe administrar E/S o excepciones de acceso no autorizado, debe considerar el
enfoque descrito en Procedimiento Iteración de directorios de archivos con la clase Parallel.
Si la latencia de E/S es un problema, por ejemplo con E/S de archivos a través de una red, considere la posibilidad
de usar una de las técnicas de E/S asincrónicas descritas en TPL y la programación asincrónica tradicional de .NET
Framework y en esta entrada de blog .
Vea también
Parallel LINQ (PLINQ)
Procedimiento para medir el rendimiento de
consultas PLINQ
16/09/2020 • 2 minutes to read • Edit Online
En este ejemplo se muestra cómo utilizar la clase Stopwatch para medir el tiempo que tarda en ejecutarse una
consulta PLINQ.
Ejemplo
Este ejemplo utiliza un bucle foreach vacío ( For Each en Visual Basic) para medir el tiempo que tarda en
ejecutarse la consulta. En el código real, el bucle normalmente contiene pasos de procesamiento adicional que se
agregan al tiempo de ejecución de consulta total. Verá que el cronómetro no se inicia hasta justo antes del bucle,
ya que ese es el momento en el que comienza la ejecución de las consultas. Si necesita una medición más
específica, puede utilizar la propiedad ElapsedTicks en lugar de ElapsedMilliseconds .
using System;
using System.Diagnostics;
using System.Linq;
class Example
{
static void Main()
{
var source = Enumerable.Range(0, 3000000);
Console.WriteLine("Measuring...");
sw.Stop();
long elapsed = sw.ElapsedMilliseconds; // or sw.ElapsedTicks
Console.WriteLine("Total query time: {0} ms", elapsed);
Console.WriteLine("Measuring...")
sw.Stop()
Dim elapsed As Long = sw.ElapsedMilliseconds ' or sw.ElapsedTicks
Console.WriteLine("Total query time: {0} ms.", elapsed)
El tiempo de ejecución total es una métrica útil cuando se experimenta con implementaciones de consulta, pero no
siempre muestra todo el panorama. Para obtener una vista más detallada y completa de la interacción de los
subprocesos de consulta entre sí y con otros procesos en ejecución, use el Visualizador de simultaneidad.
Vea también
Parallel LINQ (PLINQ)
Ejemplo de datos de PLINQ
16/09/2020 • 20 minutes to read • Edit Online
Este ejemplo contiene datos de ejemplo en formato .csv, junto con métodos que los transforman en colecciones
en memoria de clientes, productos, pedidos y detalles de pedidos. Si desea realizar más pruebas con PLINQ,
puede pegar los ejemplos de código de otros temas concretos en el código de este tema e invocarlo desde el
método Main . También puede usar estos datos con sus propias consultas PLINQ.
Los datos representan un subconjunto de la base de datos Northwind. Se incluyen cincuenta (50) registros de
clientes, pero no todos los campos. Se incluye un subconjunto de filas de los pedidos y los datos
correspondientes de los detalles de los pedidos para cada cliente. Se incluyen todos los productos.
NOTE
El conjunto de datos no es lo suficientemente grande para mostrar que PLINQ es más rápido que LINQ to Objects para
consultas que contienen solo las cláusulas básicas where y select . Para observar aumentos de velocidad de conjuntos
de datos pequeños como este, use consultas que contengan operaciones caras desde el punto de vista del cálculo en cada
elemento del conjunto de datos.
Customer count: 50
Product count: 77
Order count: 190
Order Details count: 483
Press any key to exit.
#region DataClasses
public class Order
{
private Lazy<OrderDetail[]> _orderDetails;
public Order()
{
_orderDetails = new Lazy<OrderDetail[]>(() => GetOrderDetailsForOrder(OrderID));
}
public int OrderID { get; set; }
public string CustomerID { get; set; }
public DateTime OrderDate { get; set; }
public DateTime ShippedDate { get; set; }
public OrderDetail[] OrderDetails { get { return _orderDetails.Value; } }
}
return orderDetailStrings.ToArray();
}
}
' This class contains a subset of data from the Northwind database
' in the form of string arrays. The methods such as GetCustomers, GetOrders, and so on
' transform the strings into object arrays that you can query in an object-oriented way.
' Many of the code examples in the PLINQ How-to topics are designed to be pasted into
' the PLINQDataSample class and invoked from the Main method.
' IMPORTANT: This data set is not large enough for meaningful comparisons of PLINQ vs. LINQ performance.
Class PLINQDataSample
Shared Sub Main(ByVal args As String())
#Region "DataClasses"
Class Order
Public Sub New()
_OrderDetails = New Lazy(Of OrderDetail())(Function() GetOrderDetailsForOrder(OrderID))
End Sub
Private _OrderID As Integer
Public Property OrderID() As Integer
Get
Return _OrderID
End Get
Set(ByVal value As Integer)
_OrderID = value
End Set
End Property
Private _CustomerID As String
Public Property CustomerID() As String
Get
Return _CustomerID
End Get
Set(ByVal value As String)
_CustomerID = value
End Set
End Property
Private _OrderDate As DateTime
Public Property OrderDate() As DateTime
Get
Return _OrderDate
End Get
Set(ByVal value As DateTime)
_OrderDate = value
End Set
End Property
Private _ShippedDate As DateTime
Public Property ShippedDate() As DateTime
Get
Return _ShippedDate
End Get
Set(ByVal value As DateTime)
_ShippedDate = value
End Set
End Property
Private _OrderDetails As Lazy(Of OrderDetail())
Public ReadOnly Property OrderDetails As OrderDetail()
Get
Return _OrderDetails.Value
End Get
End Property
End Class
Class Customer
Class Product
Private _ProductName As String
Public Property ProductName() As String
Get
Return _ProductName
End Get
Set(ByVal value As String)
_ProductName = value
End Set
End Property
Private _ProductID As Integer
Public Property ProductID() As Integer
Get
Return _ProductID
End Get
Set(ByVal value As Integer)
_ProductID = value
End Set
End Property
Private _UnitPrice As Double
Public Property UnitPrice() As Double
Public Property UnitPrice() As Double
Get
Return _UnitPrice
End Get
Set(ByVal value As Double)
_UnitPrice = value
End Set
End Property
End Class
Class OrderDetail
Private _OrderID As Integer
Public Property OrderID() As Integer
Get
Return _OrderID
End Get
Set(ByVal value As Integer)
_OrderID = value
End Set
End Property
Private _ProductID As Integer
Public Property ProductID() As Integer
Get
Return _ProductID
End Get
Set(ByVal value As Integer)
_ProductID = value
End Set
End Property
Private _UnitPrice As Double
Public Property UnitPrice() As Double
Get
Return _UnitPrice
End Get
Set(ByVal value As Double)
_UnitPrice = value
End Set
End Property
Private _Quantity As Double
Public Property Quantity() As Double
Get
Return _Quantity
End Get
Set(ByVal value As Double)
_Quantity = value
End Set
End Property
Private _Discount As Double
Public Property Discount() As Double
Get
Return _Discount
End Get
Set(ByVal value As Double)
_Discount = value
End Set
End Property
End Class
#End Region
Return System.IO.File.ReadAllLines("..\..\plinqdata.csv").
SkipWhile(Function(line) line.StartsWith("CUSTOMERS") = False).
Skip(1).
TakeWhile(Function(line) line.StartsWith("END CUSTOMERS") = False)
End Function
Shared Function GetCustomers() As IEnumerable(Of Customer)
Dim customers = System.IO.File.ReadAllLines("..\..\plinqdata.csv").
SkipWhile(Function(line) line.StartsWith("CUSTOMERS") = False).
Skip(1).
TakeWhile(Function(line) line.StartsWith("END CUSTOMERS") = False)
End Function
Return orderStrings.ToArray()
End Function
Shared Function GetProducts() As IEnumerable(Of Product)
End Function
End Class
data
CUSTOMERS
ALFKI,Alfreds Futterkiste,Obere Str. 57,Berlin,12209
ANATR,Ana Trujillo Emparedados y helados,Avda. de la Constitución 2222,México D.F.,05021
ANTON,Antonio Moreno Taquería,Mataderos 2312,México D.F.,05023
AROUT,Around the Horn,120 Hanover Sq.,London,WA1 1DP
BERGS,Berglunds snabbköp,Berguvsvägen 8,Luleå,S-958 22
BLAUS,Blauer See Delikatessen,Forsterstr. 57,Mannheim,68306
BLONP,Blondesddsl père et fils,24, place Kléber,Strasbourg,67000
BOLID,Bólido Comidas preparadas,C/ Araquil, 67,Madrid,28023
BONAP,Bon app',12, rue des Bouchers,Marseille,13008
BOTTM,Bottom-Dollar Markets,23 Tsawassen Blvd.,Tsawassen,T2F 8M4
BSBEV,B's Beverages,Fauntleroy Circus,London,EC2 5NT
CACTU,Cactus Comidas para llevar,Cerrito 333,Buenos Aires,1010
CENTC,Centro comercial Moctezuma,Sierras de Granada 9993,México D.F.,05022
CHOPS,Chop-suey Chinese,Hauptstr. 29,Bern,3012
COMMI,Comércio Mineiro,Av. dos Lusíadas, 23,Sao Paulo,05432-043
CONSH,Consolidated Holdings,Berkeley Gardens 12 Brewery,London,WX1 6LT
DRACD,Drachenblut Delikatessen,Walserweg 21,Aachen,52066
DUMON,Du monde entier,67, rue des Cinquante Otages,Nantes,44000
EASTC,Eastern Connection,35 King George,London,WX3 6FW
ERNSH,Ernst Handel,Kirchgasse 6,Graz,8010
FAMIA,Familia Arquibaldo,Rua Orós, 92,Sao Paulo,05442-030
FISSA,FISSA Fabrica Inter. Salchichas S.A.,C/ Moralzarzal, 86,Madrid,28034
FOLIG,Folies gourmandes,184, chaussée de Tournai,Lille,59000
FOLKO,Folk och fä HB,Åkergatan 24,Bräcke,S-844 67
FRANK,Frankenversand,Berliner Platz 43,München,80805
FRANR,France restauration,54, rue Royale,Nantes,44000
FRANS,Franchi S.p.A.,Via Monte Bianco 34,Torino,10100
FURIB,Furia Bacalhau e Frutos do Mar,Jardim das rosas n. 32,Lisboa,1675
GALED,Galería del gastrónomo,Rambla de Cataluña, 23,Barcelona,08022
GODOS,Godos Cocina Típica,C/ Romero, 33,Sevilla,41101
GOURL,Gourmet Lanchonetes,Av. Brasil, 442,Campinas,04876-786
GREAL,Great Lakes Food Market,2732 Baker Blvd.,Eugene,97403
GROSR,GROSELLA-Restaurante,5ª Ave. Los Palos Grandes,Caracas,1081
HANAR,Hanari Carnes,Rua do Paço, 67,Rio de Janeiro,05454-876
HILAA,HILARION-Abastos,Carrera 22 con Ave. Carlos Soublette #8-35,San Cristóbal,5022
HUNGC,Hungry Coyote Import Store,City Center Plaza 516 Main St.,Elgin,97827
HUNGO,Hungry Owl All-Night Grocers,8 Johnstown Road,Cork,
ISLAT,Island Trading,Garden House Crowther Way,Cowes,PO31 7PJ
KOENE,Königlich Essen,Maubelstr. 90,Brandenburg,14776
LACOR,La corne d'abondance,67, avenue de l'Europe,Versailles,78000
LAMAI,La maison d'Asie,1 rue Alsace-Lorraine,Toulouse,31000
LAUGB,Laughing Bacchus Wine Cellars,1900 Oak St.,Vancouver,V3F 2K1
LAZYK,Lazy K Kountry Store,12 Orchestra Terrace,Walla Walla,99362
LEHMS,Lehmanns Marktstand,Magazinweg 7,Frankfurt a.M.,60528
LETSS,Let's Stop N Shop,87 Polk St. Suite 5,San Francisco,94117
LILAS,LILA-Supermercado,Carrera 52 con Ave. Bolívar #65-98 Llano Largo,Barquisimeto,3508
LINOD,LINO-Delicateses,Ave. 5 de Mayo Porlamar,I. de Margarita,4980
LONEP,Lonesome Pine Restaurant,89 Chiaroscuro Rd.,Portland,97219
MAGAA,Magazzini Alimentari Riuniti,Via Ludovico il Moro 22,Bergamo,24100
MAISD,Maison Dewey,Rue Joseph-Bens 532,Bruxelles,B-1180
END CUSTOMERS
ORDERS
10250,HANAR,7/8/1996 12:00:00 AM,7/12/1996 12:00:00 AM
10253,HANAR,7/10/1996 12:00:00 AM,7/16/1996 12:00:00 AM
10254,CHOPS,7/11/1996 12:00:00 AM,7/23/1996 12:00:00 AM
10257,HILAA,7/16/1996 12:00:00 AM,7/22/1996 12:00:00 AM
10258,ERNSH,7/17/1996 12:00:00 AM,7/23/1996 12:00:00 AM
10263,ERNSH,7/23/1996 12:00:00 AM,7/31/1996 12:00:00 AM
10264,FOLKO,7/24/1996 12:00:00 AM,8/23/1996 12:00:00 AM
10265,BLONP,7/25/1996 12:00:00 AM,8/12/1996 12:00:00 AM
10267,FRANK,7/29/1996 12:00:00 AM,8/6/1996 12:00:00 AM
10275,MAGAA,8/7/1996 12:00:00 AM,8/9/1996 12:00:00 AM
10278,BERGS,8/12/1996 12:00:00 AM,8/16/1996 12:00:00 AM
10279,LEHMS,8/13/1996 12:00:00 AM,8/16/1996 12:00:00 AM
10280,BERGS,8/14/1996 12:00:00 AM,9/12/1996 12:00:00 AM
10283,LILAS,8/16/1996 12:00:00 AM,8/23/1996 12:00:00 AM
10284,LEHMS,8/19/1996 12:00:00 AM,8/27/1996 12:00:00 AM
10289,BSBEV,8/26/1996 12:00:00 AM,8/28/1996 12:00:00 AM
10290,COMMI,8/27/1996 12:00:00 AM,9/3/1996 12:00:00 AM
10296,LILAS,9/3/1996 12:00:00 AM,9/11/1996 12:00:00 AM
10297,BLONP,9/4/1996 12:00:00 AM,9/10/1996 12:00:00 AM
10298,HUNGO,9/5/1996 12:00:00 AM,9/11/1996 12:00:00 AM
10300,MAGAA,9/9/1996 12:00:00 AM,9/18/1996 12:00:00 AM
10303,GODOS,9/11/1996 12:00:00 AM,9/18/1996 12:00:00 AM
10307,LONEP,9/17/1996 12:00:00 AM,9/25/1996 12:00:00 AM
10309,HUNGO,9/19/1996 12:00:00 AM,10/23/1996 12:00:00 AM
10315,ISLAT,9/26/1996 12:00:00 AM,10/3/1996 12:00:00 AM
10317,LONEP,9/30/1996 12:00:00 AM,10/10/1996 12:00:00 AM
10318,ISLAT,10/1/1996 12:00:00 AM,10/4/1996 12:00:00 AM
10321,ISLAT,10/3/1996 12:00:00 AM,10/11/1996 12:00:00 AM
10323,KOENE,10/7/1996 12:00:00 AM,10/14/1996 12:00:00 AM
10325,KOENE,10/9/1996 12:00:00 AM,10/14/1996 12:00:00 AM
10327,FOLKO,10/11/1996 12:00:00 AM,10/14/1996 12:00:00 AM
10328,FURIB,10/14/1996 12:00:00 AM,10/17/1996 12:00:00 AM
10330,LILAS,10/16/1996 12:00:00 AM,10/28/1996 12:00:00 AM
10331,BONAP,10/16/1996 12:00:00 AM,10/21/1996 12:00:00 AM
10335,HUNGO,10/22/1996 12:00:00 AM,10/24/1996 12:00:00 AM
10337,FRANK,10/24/1996 12:00:00 AM,10/29/1996 12:00:00 AM
10340,BONAP,10/29/1996 12:00:00 AM,11/8/1996 12:00:00 AM
10342,FRANK,10/30/1996 12:00:00 AM,11/4/1996 12:00:00 AM
10343,LEHMS,10/31/1996 12:00:00 AM,11/6/1996 12:00:00 AM
10347,FAMIA,11/6/1996 12:00:00 AM,11/8/1996 12:00:00 AM
10350,LAMAI,11/11/1996 12:00:00 AM,12/3/1996 12:00:00 AM
10351,ERNSH,11/11/1996 12:00:00 AM,11/20/1996 12:00:00 AM
10352,FURIB,11/12/1996 12:00:00 AM,11/18/1996 12:00:00 AM
10355,AROUT,11/15/1996 12:00:00 AM,11/20/1996 12:00:00 AM
10357,LILAS,11/19/1996 12:00:00 AM,12/2/1996 12:00:00 AM
10358,LAMAI,11/20/1996 12:00:00 AM,11/27/1996 12:00:00 AM
10360,BLONP,11/22/1996 12:00:00 AM,12/2/1996 12:00:00 AM
10362,BONAP,11/25/1996 12:00:00 AM,11/28/1996 12:00:00 AM
10363,DRACD,11/26/1996 12:00:00 AM,12/4/1996 12:00:00 AM
10364,EASTC,11/26/1996 12:00:00 AM,12/4/1996 12:00:00 AM
10365,ANTON,11/27/1996 12:00:00 AM,12/2/1996 12:00:00 AM
10366,GALED,11/28/1996 12:00:00 AM,12/30/1996 12:00:00 AM
10368,ERNSH,11/29/1996 12:00:00 AM,12/2/1996 12:00:00 AM
10370,CHOPS,12/3/1996 12:00:00 AM,12/27/1996 12:00:00 AM
10371,LAMAI,12/3/1996 12:00:00 AM,12/24/1996 12:00:00 AM
10373,HUNGO,12/5/1996 12:00:00 AM,12/11/1996 12:00:00 AM
10375,HUNGC,12/6/1996 12:00:00 AM,12/9/1996 12:00:00 AM
10378,FOLKO,12/10/1996 12:00:00 AM,12/19/1996 12:00:00 AM
10380,HUNGO,12/12/1996 12:00:00 AM,1/16/1997 12:00:00 AM
10381,LILAS,12/12/1996 12:00:00 AM,12/13/1996 12:00:00 AM
10382,ERNSH,12/13/1996 12:00:00 AM,12/16/1996 12:00:00 AM
10383,AROUT,12/16/1996 12:00:00 AM,12/18/1996 12:00:00 AM
10384,BERGS,12/16/1996 12:00:00 AM,12/20/1996 12:00:00 AM
10386,FAMIA,12/18/1996 12:00:00 AM,12/25/1996 12:00:00 AM
10389,BOTTM,12/20/1996 12:00:00 AM,12/24/1996 12:00:00 AM
10391,DRACD,12/23/1996 12:00:00 AM,12/31/1996 12:00:00 AM
10394,HUNGC,12/25/1996 12:00:00 AM,1/3/1997 12:00:00 AM
10395,HILAA,12/26/1996 12:00:00 AM,1/3/1997 12:00:00 AM
10396,FRANK,12/27/1996 12:00:00 AM,1/6/1997 12:00:00 AM
10400,EASTC,1/1/1997 12:00:00 AM,1/16/1997 12:00:00 AM
10404,MAGAA,1/3/1997 12:00:00 AM,1/8/1997 12:00:00 AM
10405,LINOD,1/6/1997 12:00:00 AM,1/22/1997 12:00:00 AM
10408,FOLIG,1/8/1997 12:00:00 AM,1/14/1997 12:00:00 AM
10410,BOTTM,1/10/1997 12:00:00 AM,1/15/1997 12:00:00 AM
10411,BOTTM,1/10/1997 12:00:00 AM,1/21/1997 12:00:00 AM
10413,LAMAI,1/14/1997 12:00:00 AM,1/16/1997 12:00:00 AM
10414,FAMIA,1/14/1997 12:00:00 AM,1/17/1997 12:00:00 AM
10415,HUNGC,1/15/1997 12:00:00 AM,1/24/1997 12:00:00 AM
10422,FRANS,1/22/1997 12:00:00 AM,1/31/1997 12:00:00 AM
10423,GOURL,1/23/1997 12:00:00 AM,2/24/1997 12:00:00 AM
10425,LAMAI,1/24/1997 12:00:00 AM,2/14/1997 12:00:00 AM
10426,GALED,1/27/1997 12:00:00 AM,2/6/1997 12:00:00 AM
10431,BOTTM,1/30/1997 12:00:00 AM,2/7/1997 12:00:00 AM
10434,FOLKO,2/3/1997 12:00:00 AM,2/13/1997 12:00:00 AM
10436,BLONP,2/5/1997 12:00:00 AM,2/11/1997 12:00:00 AM
10444,BERGS,2/12/1997 12:00:00 AM,2/21/1997 12:00:00 AM
10445,BERGS,2/13/1997 12:00:00 AM,2/20/1997 12:00:00 AM
10449,BLONP,2/18/1997 12:00:00 AM,2/27/1997 12:00:00 AM
10453,AROUT,2/21/1997 12:00:00 AM,2/26/1997 12:00:00 AM
10456,KOENE,2/25/1997 12:00:00 AM,2/28/1997 12:00:00 AM
10457,KOENE,2/25/1997 12:00:00 AM,3/3/1997 12:00:00 AM
10460,FOLKO,2/28/1997 12:00:00 AM,3/3/1997 12:00:00 AM
10464,FURIB,3/4/1997 12:00:00 AM,3/14/1997 12:00:00 AM
10466,COMMI,3/6/1997 12:00:00 AM,3/13/1997 12:00:00 AM
10467,MAGAA,3/6/1997 12:00:00 AM,3/11/1997 12:00:00 AM
10468,KOENE,3/7/1997 12:00:00 AM,3/12/1997 12:00:00 AM
10470,BONAP,3/11/1997 12:00:00 AM,3/14/1997 12:00:00 AM
10471,BSBEV,3/11/1997 12:00:00 AM,3/18/1997 12:00:00 AM
10473,ISLAT,3/13/1997 12:00:00 AM,3/21/1997 12:00:00 AM
10476,HILAA,3/17/1997 12:00:00 AM,3/24/1997 12:00:00 AM
10480,FOLIG,3/20/1997 12:00:00 AM,3/24/1997 12:00:00 AM
10484,BSBEV,3/24/1997 12:00:00 AM,4/1/1997 12:00:00 AM
10485,LINOD,3/25/1997 12:00:00 AM,3/31/1997 12:00:00 AM
10486,HILAA,3/26/1997 12:00:00 AM,4/2/1997 12:00:00 AM
10488,FRANK,3/27/1997 12:00:00 AM,4/2/1997 12:00:00 AM
10490,HILAA,3/31/1997 12:00:00 AM,4/3/1997 12:00:00 AM
10491,FURIB,3/31/1997 12:00:00 AM,4/8/1997 12:00:00 AM
10492,BOTTM,4/1/1997 12:00:00 AM,4/11/1997 12:00:00 AM
10494,COMMI,4/2/1997 12:00:00 AM,4/9/1997 12:00:00 AM
10497,LEHMS,4/4/1997 12:00:00 AM,4/7/1997 12:00:00 AM
10497,LEHMS,4/4/1997 12:00:00 AM,4/7/1997 12:00:00 AM
10501,BLAUS,4/9/1997 12:00:00 AM,4/16/1997 12:00:00 AM
10507,ANTON,4/15/1997 12:00:00 AM,4/22/1997 12:00:00 AM
10509,BLAUS,4/17/1997 12:00:00 AM,4/29/1997 12:00:00 AM
10511,BONAP,4/18/1997 12:00:00 AM,4/21/1997 12:00:00 AM
10512,FAMIA,4/21/1997 12:00:00 AM,4/24/1997 12:00:00 AM
10519,CHOPS,4/28/1997 12:00:00 AM,5/1/1997 12:00:00 AM
10521,CACTU,4/29/1997 12:00:00 AM,5/2/1997 12:00:00 AM
10522,LEHMS,4/30/1997 12:00:00 AM,5/6/1997 12:00:00 AM
10528,GREAL,5/6/1997 12:00:00 AM,5/9/1997 12:00:00 AM
10529,MAISD,5/7/1997 12:00:00 AM,5/9/1997 12:00:00 AM
10532,EASTC,5/9/1997 12:00:00 AM,5/12/1997 12:00:00 AM
10535,ANTON,5/13/1997 12:00:00 AM,5/21/1997 12:00:00 AM
10538,BSBEV,5/15/1997 12:00:00 AM,5/16/1997 12:00:00 AM
10539,BSBEV,5/16/1997 12:00:00 AM,5/23/1997 12:00:00 AM
10541,HANAR,5/19/1997 12:00:00 AM,5/29/1997 12:00:00 AM
10544,LONEP,5/21/1997 12:00:00 AM,5/30/1997 12:00:00 AM
10550,GODOS,5/28/1997 12:00:00 AM,6/6/1997 12:00:00 AM
10551,FURIB,5/28/1997 12:00:00 AM,6/6/1997 12:00:00 AM
10558,AROUT,6/4/1997 12:00:00 AM,6/10/1997 12:00:00 AM
10568,GALED,6/13/1997 12:00:00 AM,7/9/1997 12:00:00 AM
10573,ANTON,6/19/1997 12:00:00 AM,6/20/1997 12:00:00 AM
10581,FAMIA,6/26/1997 12:00:00 AM,7/2/1997 12:00:00 AM
10582,BLAUS,6/27/1997 12:00:00 AM,7/14/1997 12:00:00 AM
10589,GREAL,7/4/1997 12:00:00 AM,7/14/1997 12:00:00 AM
10600,HUNGC,7/16/1997 12:00:00 AM,7/21/1997 12:00:00 AM
10614,BLAUS,7/29/1997 12:00:00 AM,8/1/1997 12:00:00 AM
10616,GREAL,7/31/1997 12:00:00 AM,8/5/1997 12:00:00 AM
10617,GREAL,7/31/1997 12:00:00 AM,8/4/1997 12:00:00 AM
10621,ISLAT,8/5/1997 12:00:00 AM,8/11/1997 12:00:00 AM
10629,GODOS,8/12/1997 12:00:00 AM,8/20/1997 12:00:00 AM
10634,FOLIG,8/15/1997 12:00:00 AM,8/21/1997 12:00:00 AM
10635,MAGAA,8/18/1997 12:00:00 AM,8/21/1997 12:00:00 AM
10638,LINOD,8/20/1997 12:00:00 AM,9/1/1997 12:00:00 AM
10643,ALFKI,8/25/1997 12:00:00 AM,9/2/1997 12:00:00 AM
10645,HANAR,8/26/1997 12:00:00 AM,9/2/1997 12:00:00 AM
10649,MAISD,8/28/1997 12:00:00 AM,8/29/1997 12:00:00 AM
10652,GOURL,9/1/1997 12:00:00 AM,9/8/1997 12:00:00 AM
10656,GREAL,9/4/1997 12:00:00 AM,9/10/1997 12:00:00 AM
10660,HUNGC,9/8/1997 12:00:00 AM,10/15/1997 12:00:00 AM
10662,LONEP,9/9/1997 12:00:00 AM,9/18/1997 12:00:00 AM
10665,LONEP,9/11/1997 12:00:00 AM,9/17/1997 12:00:00 AM
10677,ANTON,9/22/1997 12:00:00 AM,9/26/1997 12:00:00 AM
10685,GOURL,9/29/1997 12:00:00 AM,10/3/1997 12:00:00 AM
10690,HANAR,10/2/1997 12:00:00 AM,10/3/1997 12:00:00 AM
10692,ALFKI,10/3/1997 12:00:00 AM,10/13/1997 12:00:00 AM
10697,LINOD,10/8/1997 12:00:00 AM,10/14/1997 12:00:00 AM
10702,ALFKI,10/13/1997 12:00:00 AM,10/21/1997 12:00:00 AM
10707,AROUT,10/16/1997 12:00:00 AM,10/23/1997 12:00:00 AM
10709,GOURL,10/17/1997 12:00:00 AM,11/20/1997 12:00:00 AM
10710,FRANS,10/20/1997 12:00:00 AM,10/23/1997 12:00:00 AM
10726,EASTC,11/3/1997 12:00:00 AM,12/5/1997 12:00:00 AM
10729,LINOD,11/4/1997 12:00:00 AM,11/14/1997 12:00:00 AM
10731,CHOPS,11/6/1997 12:00:00 AM,11/14/1997 12:00:00 AM
10734,GOURL,11/7/1997 12:00:00 AM,11/12/1997 12:00:00 AM
10746,CHOPS,11/19/1997 12:00:00 AM,11/21/1997 12:00:00 AM
10753,FRANS,11/25/1997 12:00:00 AM,11/27/1997 12:00:00 AM
10760,MAISD,12/1/1997 12:00:00 AM,12/10/1997 12:00:00 AM
10763,FOLIG,12/3/1997 12:00:00 AM,12/8/1997 12:00:00 AM
10782,CACTU,12/17/1997 12:00:00 AM,12/22/1997 12:00:00 AM
10789,FOLIG,12/22/1997 12:00:00 AM,12/31/1997 12:00:00 AM
10797,DRACD,12/25/1997 12:00:00 AM,1/5/1998 12:00:00 AM
10807,FRANS,12/31/1997 12:00:00 AM,1/30/1998 12:00:00 AM
10819,CACTU,1/7/1998 12:00:00 AM,1/16/1998 12:00:00 AM
10825,DRACD,1/9/1998 12:00:00 AM,1/14/1998 12:00:00 AM
10835,ALFKI,1/15/1998 12:00:00 AM,1/21/1998 12:00:00 AM
10853,BLAUS,1/27/1998 12:00:00 AM,2/3/1998 12:00:00 AM
10872,GODOS,2/5/1998 12:00:00 AM,2/9/1998 12:00:00 AM
10874,GODOS,2/6/1998 12:00:00 AM,2/11/1998 12:00:00 AM
10881,CACTU,2/11/1998 12:00:00 AM,2/18/1998 12:00:00 AM
10881,CACTU,2/11/1998 12:00:00 AM,2/18/1998 12:00:00 AM
10887,GALED,2/13/1998 12:00:00 AM,2/16/1998 12:00:00 AM
10892,MAISD,2/17/1998 12:00:00 AM,2/19/1998 12:00:00 AM
10896,MAISD,2/19/1998 12:00:00 AM,2/27/1998 12:00:00 AM
10928,GALED,3/5/1998 12:00:00 AM,3/18/1998 12:00:00 AM
10937,CACTU,3/10/1998 12:00:00 AM,3/13/1998 12:00:00 AM
10952,ALFKI,3/16/1998 12:00:00 AM,3/24/1998 12:00:00 AM
10969,COMMI,3/23/1998 12:00:00 AM,3/30/1998 12:00:00 AM
10987,EASTC,3/31/1998 12:00:00 AM,4/6/1998 12:00:00 AM
11026,FRANS,4/15/1998 12:00:00 AM,4/28/1998 12:00:00 AM
11036,DRACD,4/20/1998 12:00:00 AM,4/22/1998 12:00:00 AM
11042,COMMI,4/22/1998 12:00:00 AM,5/1/1998 12:00:00 AM
END ORDERS
ORDER DETAILS
10250,41,7.7000,10,0
10250,51,42.4000,35,0.15
10250,65,16.8000,15,0.15
10253,31,10.0000,20,0
10253,39,14.4000,42,0
10253,49,16.0000,40,0
10254,24,3.6000,15,0.15
10254,55,19.2000,21,0.15
10254,74,8.0000,21,0
10257,27,35.1000,25,0
10257,39,14.4000,6,0
10257,77,10.4000,15,0
10258,2,15.2000,50,0.2
10258,5,17.0000,65,0.2
10258,32,25.6000,6,0.2
10263,16,13.9000,60,0.25
10263,24,3.6000,28,0
10263,30,20.7000,60,0.25
10263,74,8.0000,36,0.25
10264,2,15.2000,35,0
10264,41,7.7000,25,0.15
10265,17,31.2000,30,0
10265,70,12.0000,20,0
10267,40,14.7000,50,0
10267,59,44.0000,70,0.15
10267,76,14.4000,15,0.15
10275,24,3.6000,12,0.05
10275,59,44.0000,6,0.05
10278,44,15.5000,16,0
10278,59,44.0000,15,0
10278,63,35.1000,8,0
10278,73,12.0000,25,0
10279,17,31.2000,15,0.25
10280,24,3.6000,12,0
10280,55,19.2000,20,0
10280,75,6.2000,30,0
10283,15,12.4000,20,0
10283,19,7.3000,18,0
10283,60,27.2000,35,0
10283,72,27.8000,3,0
10284,27,35.1000,15,0.25
10284,44,15.5000,21,0
10284,60,27.2000,20,0.25
10284,67,11.2000,5,0.25
10289,3,8.0000,30,0
10289,64,26.6000,9,0
10290,5,17.0000,20,0
10290,29,99.0000,15,0
10290,49,16.0000,15,0
10290,77,10.4000,10,0
10296,11,16.8000,12,0
10296,16,13.9000,30,0
10296,69,28.8000,15,0
10297,39,14.4000,60,0
10297,72,27.8000,20,0
10297,72,27.8000,20,0
10298,2,15.2000,40,0
10298,36,15.2000,40,0.25
10298,59,44.0000,30,0.25
10298,62,39.4000,15,0
10300,66,13.6000,30,0
10300,68,10.0000,20,0
10303,40,14.7000,40,0.1
10303,65,16.8000,30,0.1
10303,68,10.0000,15,0.1
10307,62,39.4000,10,0
10307,68,10.0000,3,0
10309,4,17.6000,20,0
10309,6,20.0000,30,0
10309,42,11.2000,2,0
10309,43,36.8000,20,0
10309,71,17.2000,3,0
10315,34,11.2000,14,0
10315,70,12.0000,30,0
10317,1,14.4000,20,0
10318,41,7.7000,20,0
10318,76,14.4000,6,0
10321,35,14.4000,10,0
10323,15,12.4000,5,0
10323,25,11.2000,4,0
10323,39,14.4000,4,0
10325,6,20.0000,6,0
10325,13,4.8000,12,0
10325,14,18.6000,9,0
10325,31,10.0000,4,0
10325,72,27.8000,40,0
10327,2,15.2000,25,0.2
10327,11,16.8000,50,0.2
10327,30,20.7000,35,0.2
10327,58,10.6000,30,0.2
10328,59,44.0000,9,0
10328,65,16.8000,40,0
10328,68,10.0000,10,0
10330,26,24.9000,50,0.15
10330,72,27.8000,25,0.15
10331,54,5.9000,15,0
10335,2,15.2000,7,0.2
10335,31,10.0000,25,0.2
10335,32,25.6000,6,0.2
10335,51,42.4000,48,0.2
10337,23,7.2000,40,0
10337,26,24.9000,24,0
10337,36,15.2000,20,0
10337,37,20.8000,28,0
10337,72,27.8000,25,0
10340,18,50.0000,20,0.05
10340,41,7.7000,12,0.05
10340,43,36.8000,40,0.05
10342,2,15.2000,24,0.2
10342,31,10.0000,56,0.2
10342,36,15.2000,40,0.2
10342,55,19.2000,40,0.2
10343,64,26.6000,50,0
10343,68,10.0000,4,0.05
10343,76,14.4000,15,0
10347,25,11.2000,10,0
10347,39,14.4000,50,0.15
10347,40,14.7000,4,0
10347,75,6.2000,6,0.15
10350,50,13.0000,15,0.1
10350,69,28.8000,18,0.1
10351,38,210.8000,20,0.05
10351,41,7.7000,13,0
10351,44,15.5000,77,0.05
10351,65,16.8000,10,0.05
10351,65,16.8000,10,0.05
10352,24,3.6000,10,0
10352,54,5.9000,20,0.15
10355,24,3.6000,25,0
10355,57,15.6000,25,0
10357,10,24.8000,30,0.2
10357,26,24.9000,16,0
10357,60,27.2000,8,0.2
10358,24,3.6000,10,0.05
10358,34,11.2000,10,0.05
10358,36,15.2000,20,0.05
10360,28,36.4000,30,0
10360,29,99.0000,35,0
10360,38,210.8000,10,0
10360,49,16.0000,35,0
10360,54,5.9000,28,0
10362,25,11.2000,50,0
10362,51,42.4000,20,0
10362,54,5.9000,24,0
10363,31,10.0000,20,0
10363,75,6.2000,12,0
10363,76,14.4000,12,0
10364,69,28.8000,30,0
10364,71,17.2000,5,0
10365,11,16.8000,24,0
10366,65,16.8000,5,0
10366,77,10.4000,5,0
10368,21,8.0000,5,0.1
10368,28,36.4000,13,0.1
10368,57,15.6000,25,0
10368,64,26.6000,35,0.1
10370,1,14.4000,15,0.15
10370,64,26.6000,30,0
10370,74,8.0000,20,0.15
10371,36,15.2000,6,0.2
10373,58,10.6000,80,0.2
10373,71,17.2000,50,0.2
10375,14,18.6000,15,0
10375,54,5.9000,10,0
10378,71,17.2000,6,0
10380,30,20.7000,18,0.1
10380,53,26.2000,20,0.1
10380,60,27.2000,6,0.1
10380,70,12.0000,30,0
10381,74,8.0000,14,0
10382,5,17.0000,32,0
10382,18,50.0000,9,0
10382,29,99.0000,14,0
10382,33,2.0000,60,0
10382,74,8.0000,50,0
10383,13,4.8000,20,0
10383,50,13.0000,15,0
10383,56,30.4000,20,0
10384,20,64.8000,28,0
10384,60,27.2000,15,0
10386,24,3.6000,15,0
10386,34,11.2000,10,0
10389,10,24.8000,16,0
10389,55,19.2000,15,0
10389,62,39.4000,20,0
10389,70,12.0000,30,0
10391,13,4.8000,18,0
10394,13,4.8000,10,0
10394,62,39.4000,10,0
10395,46,9.6000,28,0.1
10395,53,26.2000,70,0.1
10395,69,28.8000,8,0
10396,23,7.2000,40,0
10396,71,17.2000,60,0
10396,72,27.8000,21,0
10400,29,99.0000,21,0
10400,35,14.4000,35,0
10400,49,16.0000,30,0
10404,26,24.9000,30,0.05
10404,42,11.2000,40,0.05
10404,49,16.0000,30,0.05
10405,3,8.0000,50,0
10408,37,20.8000,10,0
10408,54,5.9000,6,0
10408,62,39.4000,35,0
10410,33,2.0000,49,0
10410,59,44.0000,16,0
10411,41,7.7000,25,0.2
10411,44,15.5000,40,0.2
10411,59,44.0000,9,0.2
10413,1,14.4000,24,0
10413,62,39.4000,40,0
10413,76,14.4000,14,0
10414,19,7.3000,18,0.05
10414,33,2.0000,50,0
10415,17,31.2000,2,0
10415,33,2.0000,20,0
10422,26,24.9000,2,0
10423,31,10.0000,14,0
10423,59,44.0000,20,0
10425,55,19.2000,10,0.25
10425,76,14.4000,20,0.25
10426,56,30.4000,5,0
10426,64,26.6000,7,0
10431,17,31.2000,50,0.25
10431,40,14.7000,50,0.25
10431,47,7.6000,30,0.25
10434,11,16.8000,6,0
10434,76,14.4000,18,0.15
10436,46,9.6000,5,0
10436,56,30.4000,40,0.1
10436,64,26.6000,30,0.1
10436,75,6.2000,24,0.1
10444,17,31.2000,10,0
10444,26,24.9000,15,0
10444,35,14.4000,8,0
10444,41,7.7000,30,0
10445,39,14.4000,6,0
10445,54,5.9000,15,0
10449,10,24.8000,14,0
10449,52,5.6000,20,0
10449,62,39.4000,35,0
10453,48,10.2000,15,0.1
10453,70,12.0000,25,0.1
10456,21,8.0000,40,0.15
10456,49,16.0000,21,0.15
10457,59,44.0000,36,0
10460,68,10.0000,21,0.25
10460,75,6.2000,4,0.25
10464,4,17.6000,16,0.2
10464,43,36.8000,3,0
10464,56,30.4000,30,0.2
10464,60,27.2000,20,0
10466,11,16.8000,10,0
10466,46,9.6000,5,0
10467,24,3.6000,28,0
10467,25,11.2000,12,0
10468,30,20.7000,8,0
10468,43,36.8000,15,0
10470,18,50.0000,30,0
10470,23,7.2000,15,0
10470,64,26.6000,8,0
10471,7,24.0000,30,0
10471,56,30.4000,20,0
10473,33,2.0000,12,0
10473,71,17.2000,12,0
10476,55,19.2000,2,0.05
10476,70,12.0000,12,0
10480,47,7.6000,30,0
10480,59,44.0000,12,0
10484,21,8.0000,14,0
10484,40,14.7000,10,0
10484,51,42.4000,3,0
10485,2,15.2000,20,0.1
10485,3,8.0000,20,0.1
10485,55,19.2000,30,0.1
10485,70,12.0000,60,0.1
10486,11,16.8000,5,0
10486,51,42.4000,25,0
10486,74,8.0000,16,0
10488,59,44.0000,30,0
10488,73,12.0000,20,0.2
10490,59,44.0000,60,0
10490,68,10.0000,30,0
10490,75,6.2000,36,0
10491,44,15.5000,15,0.15
10491,77,10.4000,7,0.15
10492,25,11.2000,60,0.05
10492,42,11.2000,20,0.05
10494,56,30.4000,30,0
10497,56,30.4000,14,0
10497,72,27.8000,25,0
10497,77,10.4000,25,0
10501,54,7.4500,20,0
10507,43,46.0000,15,0.15
10507,48,12.7500,15,0.15
10509,28,45.6000,3,0
10511,4,22.0000,50,0.15
10511,7,30.0000,50,0.15
10511,8,40.0000,10,0.15
10512,24,4.5000,10,0.15
10512,46,12.0000,9,0.15
10512,47,9.5000,6,0.15
10512,60,34.0000,12,0.15
10519,10,31.0000,16,0.05
10519,56,38.0000,40,0
10519,60,34.0000,10,0.05
10521,35,18.0000,3,0
10521,41,9.6500,10,0
10521,68,12.5000,6,0
10522,1,18.0000,40,0.2
10522,8,40.0000,24,0
10522,30,25.8900,20,0.2
10522,40,18.4000,25,0.2
10528,11,21.0000,3,0
10528,33,2.5000,8,0.2
10528,72,34.8000,9,0
10529,55,24.0000,14,0
10529,68,12.5000,20,0
10529,69,36.0000,10,0
10532,30,25.8900,15,0
10532,66,17.0000,24,0
10535,11,21.0000,50,0.1
10535,40,18.4000,10,0.1
10535,57,19.5000,5,0.1
10535,59,55.0000,15,0.1
10538,70,15.0000,7,0
10538,72,34.8000,1,0
10539,13,6.0000,8,0
10539,21,10.0000,15,0
10539,33,2.5000,15,0
10539,49,20.0000,6,0
10539,49,20.0000,6,0
10541,24,4.5000,35,0.1
10541,38,263.5000,4,0.1
10541,65,21.0500,36,0.1
10541,71,21.5000,9,0.1
10544,28,45.6000,7,0
10544,67,14.0000,7,0
10550,17,39.0000,8,0.1
10550,19,9.2000,10,0
10550,21,10.0000,6,0.1
10550,61,28.5000,10,0.1
10551,16,17.4500,40,0.15
10551,35,18.0000,20,0.15
10551,44,19.4500,40,0
10558,47,9.5000,25,0
10558,51,53.0000,20,0
10558,52,7.0000,30,0
10558,53,32.8000,18,0
10558,73,15.0000,3,0
10568,10,31.0000,5,0
10573,17,39.0000,18,0
10573,34,14.0000,40,0
10573,53,32.8000,25,0
10581,75,7.7500,50,0.2
10582,57,19.5000,4,0
10582,76,18.0000,14,0
10589,35,18.0000,4,0
10600,54,7.4500,4,0
10600,73,15.0000,30,0
10614,11,21.0000,14,0
10614,21,10.0000,8,0
10614,39,18.0000,5,0
10616,38,263.5000,15,0.05
10616,56,38.0000,14,0
10616,70,15.0000,15,0.05
10616,71,21.5000,15,0.05
10617,59,55.0000,30,0.15
10621,19,9.2000,5,0
10621,23,9.0000,10,0
10621,70,15.0000,20,0
10621,71,21.5000,15,0
10629,29,123.7900,20,0
10629,64,33.2500,9,0
10634,7,30.0000,35,0
10634,18,62.5000,50,0
10634,51,53.0000,15,0
10634,75,7.7500,2,0
10635,4,22.0000,10,0.1
10635,5,21.3500,15,0.1
10635,22,21.0000,40,0
10638,45,9.5000,20,0
10638,65,21.0500,21,0
10638,72,34.8000,60,0
10643,28,45.6000,15,0.25
10643,39,18.0000,21,0.25
10643,46,12.0000,2,0.25
10645,18,62.5000,20,0
10645,36,19.0000,15,0
10649,28,45.6000,20,0
10649,72,34.8000,15,0
10652,30,25.8900,2,0.25
10652,42,14.0000,20,0
10656,14,23.2500,3,0.1
10656,44,19.4500,28,0.1
10656,47,9.5000,6,0.1
10660,20,81.0000,21,0
10662,68,12.5000,10,0
10665,51,53.0000,20,0
10665,59,55.0000,1,0
10665,76,18.0000,10,0
10665,76,18.0000,10,0
10677,26,31.2300,30,0.15
10677,33,2.5000,8,0.15
10685,10,31.0000,20,0
10685,41,9.6500,4,0
10685,47,9.5000,15,0
10690,56,38.0000,20,0.25
10690,77,13.0000,30,0.25
10692,63,43.9000,20,0
10697,19,9.2000,7,0.25
10697,35,18.0000,9,0.25
10697,58,13.2500,30,0.25
10697,70,15.0000,30,0.25
10702,3,10.0000,6,0
10702,76,18.0000,15,0
10707,55,24.0000,21,0
10707,57,19.5000,40,0
10707,70,15.0000,28,0.15
10709,8,40.0000,40,0
10709,51,53.0000,28,0
10709,60,34.0000,10,0
10710,19,9.2000,5,0
10710,47,9.5000,5,0
10726,4,22.0000,25,0
10726,11,21.0000,5,0
10729,1,18.0000,50,0
10729,21,10.0000,30,0
10729,50,16.2500,40,0
10731,21,10.0000,40,0.05
10731,51,53.0000,30,0.05
10734,6,25.0000,30,0
10734,30,25.8900,15,0
10734,76,18.0000,20,0
10746,13,6.0000,6,0
10746,42,14.0000,28,0
10746,62,49.3000,9,0
10746,69,36.0000,40,0
10753,45,9.5000,4,0
10753,74,10.0000,5,0
10760,25,14.0000,12,0.25
10760,27,43.9000,40,0
10760,43,46.0000,30,0.25
10763,21,10.0000,40,0
10763,22,21.0000,6,0
10763,24,4.5000,20,0
10782,31,12.5000,1,0
10789,18,62.5000,30,0
10789,35,18.0000,15,0
10789,63,43.9000,30,0
10789,68,12.5000,18,0
10797,11,21.0000,20,0
10807,40,18.4000,1,0
10819,43,46.0000,7,0
10819,75,7.7500,20,0
10825,26,31.2300,12,0
10825,53,32.8000,20,0
10835,59,55.0000,15,0
10835,77,13.0000,2,0.2
10853,18,62.5000,10,0
10872,55,24.0000,10,0.05
10872,62,49.3000,20,0.05
10872,64,33.2500,15,0.05
10872,65,21.0500,21,0.05
10874,10,31.0000,10,0
10881,73,15.0000,10,0
10887,25,14.0000,5,0
10892,59,55.0000,40,0.05
10896,45,9.5000,15,0
10896,56,38.0000,16,0
10928,47,9.5000,5,0
10928,47,9.5000,5,0
10928,76,18.0000,5,0
10937,28,45.6000,8,0
10937,34,14.0000,20,0
10952,6,25.0000,16,0.05
10952,28,45.6000,2,0
10969,46,12.0000,9,0
10987,7,30.0000,60,0
10987,43,46.0000,6,0
10987,72,34.8000,20,0
11026,18,62.5000,8,0
11026,51,53.0000,10,0
11036,13,6.0000,7,0
11036,59,55.0000,30,0
11042,44,19.4500,15,0
11042,61,28.5000,4,0
END ORDER DETAILS
PRODUCTS
1,Chai,18.0000
2,Chang,19.0000
3,Aniseed Syrup,10.0000
4,Chef Anton's Cajun Seasoning,22.0000
5,Chef Anton's Gumbo Mix,21.3500
6,Grandma's Boysenberry Spread,25.0000
7,Uncle Bob's Organic Dried Pears,30.0000
8,Northwoods Cranberry Sauce,40.0000
9,Mishi Kobe Niku,97.0000
10,Ikura,31.0000
11,Queso Cabrales,21.0000
12,Queso Manchego La Pastora,38.0000
13,Konbu,6.0000
14,Tofu,23.2500
15,Genen Shouyu,15.5000
16,Pavlova,17.4500
17,Alice Mutton,39.0000
18,Carnarvon Tigers,62.5000
19,Teatime Chocolate Biscuits,9.2000
20,Sir Rodney's Marmalade,81.0000
21,Sir Rodney's Scones,10.0000
22,Gustaf's Knäckebröd,21.0000
23,Tunnbröd,9.0000
24,Guaraná Fantástica,4.5000
25,NuNuCa Nuß-Nougat-Creme,14.0000
26,Gumbär Gummibärchen,31.2300
27,Schoggi Schokolade,43.9000
28,Rössle Sauerkraut,45.6000
29,Thüringer Rostbratwurst,123.7900
30,Nord-Ost Matjeshering,25.8900
31,Gorgonzola Telino,12.5000
32,Mascarpone Fabioli,32.0000
33,Geitost,2.5000
34,Sasquatch Ale,14.0000
35,Steeleye Stout,18.0000
36,Inlagd Sill,19.0000
37,Gravad lax,26.0000
38,Côte de Blaye,263.5000
39,Chartreuse verte,18.0000
40,Boston Crab Meat,18.4000
41,Jack's New England Clam Chowder,9.6500
42,Singaporean Hokkien Fried Mee,14.0000
43,Ipoh Coffee,46.0000
44,Gula Malacca,19.4500
45,Rogede sild,9.5000
46,Spegesild,12.0000
47,Zaanse koeken,9.5000
48,Chocolade,12.7500
49,Maxilaku,20.0000
50,Valkoinen suklaa,16.2500
51,Manjimup Dried Apples,53.0000
51,Manjimup Dried Apples,53.0000
52,Filo Mix,7.0000
53,Perth Pasties,32.8000
54,Tourtière,7.4500
55,Pâté chinois,24.0000
56,Gnocchi di nonna Alice,38.0000
57,Ravioli Angelo,19.5000
58,Escargots de Bourgogne,13.2500
59,Raclette Courdavault,55.0000
60,Camembert Pierrot,34.0000
61,Sirop d'érable,28.5000
62,Tarte au sucre,49.3000
63,Vegie-spread,43.9000
64,Wimmers gute Semmelknödel,33.2500
65,Louisiana Fiery Hot Pepper Sauce,21.0500
66,Louisiana Hot Spiced Okra,17.0000
67,Laughing Lumberjack Lager,14.0000
68,Scottish Longbreads,12.5000
69,Gudbrandsdalsost,36.0000
70,Outback Lager,15.0000
71,Flotemysost,21.5000
72,Mozzarella di Giovanni,34.8000
73,Röd Kaviar,15.0000
74,Longlife Tofu,10.0000
75,Rhönbräu Klosterbier,7.7500
76,Lakkalikööri,18.0000
77,Original Frankfurter grüne Soße,13.0000
END PRODUCTS
Vea también
Parallel LINQ (PLINQ)
Estructuras de datos para la programación paralela
16/09/2020 • 7 minutes to read • Edit Online
.NET Framework 4 incorpora varios tipos nuevos que son útiles en la programación paralela, incluido un conjunto
de clases de colecciones simultáneas, primitivos de sincronización ligera y tipos para la inicialización diferida.
Puede usar estos tipos con cualquier código de aplicación multiproceso, como la biblioteca TPL y PLINQ.
T IP O DESC RIP T IO N
Primitivos de sincronización
Los nuevos primitivos de sincronización del espacio de nombres System.Threading permiten un rendimiento más
rápido y una simultaneidad específica al evitar mecanismos de bloqueo caros que se encuentran en el código
multithreading heredado. Algunos de los nuevos tipos, como System.Threading.Barrier y
System.Threading.CountdownEvent, no tienen ningún homólogo en versiones anteriores de .NET Framework.
En la tabla siguiente se enumeran los nuevos tipos de sincronización:
T IP O DESC RIP T IO N
T IP O DESC RIP T IO N
Agregar excepciones
El tipo System.AggregateException puede usarse para capturar varias excepciones que se producen
simultáneamente en subprocesos independientes y devolverlas al subproceso combinado como una única
excepción. Los tipos System.Threading.Tasks.Task y System.Threading.Tasks.Parallel y PLINQ usan
AggregateException de forma amplia para este propósito. Para obtener más información, vea Control de
excepciones y Cómo: Controlar excepciones en una consulta PLINQ.
Vea también
System.Collections.Concurrent
System.Threading
Programación en paralelo
Herramientas de diagnóstico paralelo
16/09/2020 • 2 minutes to read • Edit Online
Visual Studio proporciona amplia compatibilidad para depurar aplicaciones de varios subprocesos y generar
perfiles de estas.
Depuración
El depurador de Visual Studio agrega ventanas nuevas para la depuración de aplicaciones paralelas. Para obtener
más información, consulta los temas siguientes:
Uso de la ventana Pilas paralelas
Usar la ventana Tareas
Tutorial: Depurar una aplicación paralela.
Generación de perfiles
Las vistas del informe Visualizador de simultaneidad permite visualizar cómo los subprocesos de un programa en
paralelo interactúan entre sí y con subprocesos de otros procesos del sistema. Para más información, consulte
Visualizador de simultaneidad.
Vea también
Programación en paralelo
Particionadores personalizados para PLINQ y TPL
16/09/2020 • 20 minutes to read • Edit Online
Para paralelizar una operación en un origen de datos, uno de los pasos esenciales es particionar el origen en
varias secciones a las que pueden acceder varios subprocesos al mismo tiempo. PLINQ y la biblioteca TPL
proporcionan particionadores predeterminados que funcionan de manera transparente al escribir un bucle
ForEach o una consulta en paralelo. Para escenarios más avanzados, puede conectar su propio particionador.
Tipos de particiones
Hay muchas maneras de particionar un origen de datos. En los enfoques más eficaces, varios subprocesos
cooperan para procesar la secuencia de origen original, en lugar de separar físicamente el origen en varias
secuencias secundarias. Para matrices y otros orígenes indexados como colecciones IList donde la longitud se
conoce de antemano, la creación de particiones por rangos es el tipo más sencillo de creación de particiones.
Cada subproceso recibe índices exclusivos de apertura y cierre, para poder procesar su rango del origen sin
sobrescribir subprocesos ni ser sobrescrito por algún subproceso. La única sobrecarga implicada en la creación
de particiones por rangos es el trabajo inicial de crear los rangos; después de eso, no se requiere ninguna
sincronización adicional. Por lo tanto, puede proporcionar un buen rendimiento siempre y cuando la carga de
trabajo se divida de manera uniforme. Una desventaja de la creación de particiones por rangos es que, si un
subproceso finaliza de forma anticipada, no puede ayudar a los otros subprocesos a finalizar su trabajo.
Para listas vinculadas u otras colecciones cuya longitud no se conoce, puede usar la creación de particiones por
fragmentos. En la creación de particiones por fragmentos, cada subproceso o tarea de una consulta o bucle
paralelos consumen algunos elementos de origen de un fragmento, los procesan y vuelven a activarse para
recuperar elementos adicionales. El particionador garantiza que todos los elementos se distribuyan y que no se
dupliquen. Un fragmento puede tener cualquier tamaño. Por ejemplo, el particionador que se muestra en Cómo:
Implementar las particiones dinámicas crea fragmentos que contienen un solo elemento. Siempre que los
fragmentos no sean demasiado grandes, este tipo de creación de particiones tiene un equilibrio de carga
inherente, porque la asignación de elementos a los subprocesos no es predeterminada. Sin embargo, el
particionador no incurre en sobrecarga de sincronización cada vez que el subproceso necesita obtener otro
fragmento. La cantidad de sincronización en que se incurre en estos casos es inversamente proporcional al
tamaño de los fragmentos.
En general, la creación de particiones por rangos solo es más rápida cuando el tiempo de ejecución del delegado
es de bajo a moderado y el origen tiene un gran número de elementos y el trabajo total de cada partición es
más o menos equivalente. Por tanto, la creación de particiones por fragmentos suele ser más rápida en la
mayoría de los casos. En los orígenes con un número reducido de elementos o con tiempos de ejecución más
largos para el delegado, el rendimiento de la creación de particiones por fragmentos y rangos es prácticamente
el mismo.
Los particionadores de TPL también admiten un número de particiones dinámico. Esto significa que se pueden
crear particiones sobre la marcha, por ejemplo, cuando el bucle ForEach genera una nueva tarea. Esta
característica permite al particionador escalarse junto con el propio bucle. Los particionadores dinámicos
también tienen un equilibrio de carga inherente. Cuando se crea un particionador personalizado, debe admitir la
creación de particiones dinámicas para poder usarlas desde un bucle ForEach.
Configuración de particionadores de equilibrio de carga para PLINQ
Algunas sobrecargas del método Partitioner.Create permiten crear un particionador para una matriz o un origen
IList y especificar si debe intentar equilibrar la carga de trabajo entre los subprocesos. Cuando se configura el
particionador para equilibrar la carga, se emplea la creación de particiones por fragmentos, y los elementos se
entregan a cada partición en pequeños fragmentos a medida que se solicitan. Este enfoque ayuda a garantizar
que todas las particiones tienen elementos para procesar hasta que se completa totalmente un bucle o una
consulta. Se puede usar una sobrecarga adicional para proporcionar particiones de equilibrio de carga de
cualquier origen IEnumerable.
En general, el equilibrio de carga requiere que las particiones soliciten elementos con relativa frecuencia desde
el particionador. Por el contrario, un particionador que crea particiones estáticas puede asignar todos los
elementos a cada particionador al mismo tiempo mediante el uso de la creación de particiones por rangos o por
fragmentos. Esto requiere menos sobrecarga que el equilibrio de carga, pero es posible que tarde más tiempo
en ejecutarse si un subproceso termina significativamente con más trabajo que los demás. De forma
predeterminada, cuando se pasa una interfaz IList o una matriz, PLINQ siempre utiliza la creación de particiones
por rangos sin equilibrio de carga. Para habilitar el equilibrio de carga para PLINQ, use el método
Partitioner.Create , tal como se muestra en el ejemplo siguiente.
q.ForAll((x) =>
{
ProcessData(x);
});
q.ForAll(Sub(x) ProcessData(x))
La mejor forma de determinar si usar el equilibrio de carga en un escenario concreto es experimentar y medir el
tiempo que las operaciones tardan en completarse con cargas representativas y configuraciones de equipos. Por
ejemplo, el particionamiento estático podría proporcionar un aumento significativo de la velocidad en un equipo
de varios núcleos que tenga solo unos pocos núcleos, pero podría dar como resultado una ralentización de los
equipos que tienen relativamente muchos núcleos.
En la siguiente tabla se enumeran las sobrecargas disponibles del método Create. Estos particionadores no están
limitados para utilizarse solo con PLINQ o Task. También se pueden utilizar con cualquier construcción paralela
personalizada.
Create<TSource>(IEnumerable<TSource>) Siempre
SO B REC A RGA USA EL EQ UIL IB RIO DE C A RGA
class Program
{
static void Main()
{
Module PartitionDemo
Sub Main()
' Source must be array or IList.
Dim source = Enumerable.Range(0, 100000).ToArray()
End Sub
End Module
Cada subproceso del bucle recibe su propio Tuple<T1,T2> que contiene los valores de índice de inicio y fin en el
subrango especificado. El bucle for interno usa los valores fromInclusive y toExclusive para recorrer en
bucle la matriz o IList directamente.
Una de las sobrecargas Create le permite especificar el tamaño de las particiones y el número de particiones.
Esta sobrecarga puede usarse en escenarios en los que el trabajo por elemento es tan bajo que incluso una
llamada a un método virtual por elemento tiene un impacto considerable en el rendimiento.
Particionadores personalizados
En algunos casos, podría merecer la pena o incluso ser necesario implementar un particionador propio. Por
ejemplo, podría tener una clase de colección personalizada que puede crear particiones de forma más eficaz que
los particionadores predeterminados, según su conocimiento de la estructura interna de la clase. O bien, puede
que desee crear particiones por rangos de tamaños diferentes basándose en su conocimiento de cuánto tiempo
se tardará en procesar los elementos en ubicaciones distintas de la colección de origen.
Para crear un particionador personalizado básico, derive una clase de
System.Collections.Concurrent.Partitioner<TSource> e invalide los métodos virtuales, tal como se describe en la
tabla siguiente.
GetPartitions El subproceso principal llama una vez a este método y se
devuelve IList(IEnumerator(TSource)). Cada subproceso de
trabajo del bucle o la consulta puede llamar a
GetEnumerator en la lista para recuperar IEnumerator<T>
a través de una partición distinta.
Si los resultados se pueden ordenar o precisa de acceso indexado a los elementos, realice una derivación de
System.Collections.Concurrent.OrderablePartitioner<TSource> e invalide los métodos virtuales como se
describe en la tabla siguiente.
En la tabla siguiente se proporcionan detalles adicionales sobre cómo los tres tipos de particionadores de
equilibrio de carga implementan la clase OrderablePartitioner<TSource>.
Particiones dinámicas
Si pretende que el particionador se use en un método ForEach, debe tener la capacidad de devolver un número
dinámico de particiones. Esto significa que el particionador puede proporcionar un enumerador para una nueva
partición a petición en cualquier momento durante la ejecución del bucle. Básicamente, siempre que el bucle
agrega una nueva tarea en paralelo, solicita una nueva partición de esa tarea. Si necesita que los datos se
puedan ordenar, realice la derivación de System.Collections.Concurrent.OrderablePartitioner<TSource>, para
que a cada elemento de cada partición se le asigne un índice único.
Para obtener más información y un ejemplo, vea Cómo: Implementar las particiones dinámicas.
Contrato para particionadores
Al implementar un particionador personalizado, siga estas instrucciones para ayudar a garantizar la interacción
correcta con PLINQ y ForEach en la biblioteca TPL:
Si se llama a GetPartitions con un argumento de cero o menos para partitionsCount , se produce
ArgumentOutOfRangeException. Aunque PLINQ y TPL nunca pasarán una clase partitionCount igual a 0,
no obstante, se recomienda adoptar medidas preventivas para evitar esta posibilidad.
GetPartitions y GetOrderablePartitions siempre deben devolver una serie de particiones partitionsCount .
Si el particionador agota los datos y no puede crear tantas particiones como se han solicitado, el método
debe devolver un enumerador vacío para cada una de las particiones restantes. De lo contrario, PLINQ y
TPL producirán una excepción InvalidOperationException.
GetPartitions, GetOrderablePartitions, GetDynamicPartitions y GetOrderableDynamicPartitions nunca
deberían devolver null ( Nothing en Visual Basic). Si lo hacen, PLINQ/TPL producirán una excepción
InvalidOperationException.
Los métodos que devuelven particiones siempre deberían devolver particiones que puedan enumerar
completamente y de forma única el origen de datos. No debería haber ninguna duplicación del origen de
datos ni elementos omitidos a menos que lo requiera específicamente el diseño del particionador. Si no
se respeta esta regla, se puede alterar el orden de salida.
Los siguientes captadores booleanos deben devolver siempre con precisión los siguientes valores para
que no se altere el orden de salida:
KeysOrderedInEachPartition : cada partición devuelve elementos con índices de claves crecientes.
KeysOrderedAcrossPartitions : para todas las particiones que se devuelven, los índices de clave de
la partición i son más altos que los índices de clave de la partición i-1.
: todos los índices de claves aumentan ininterrumpidamente sin espacios,
KeysNormalized
empezando desde cero.
Todos los índices deben ser únicos. No puede haber índices duplicados. Si no se respeta esta regla, se
puede alterar el orden de salida.
Todos los índices deben ser no negativos. Si no se respeta esta regla, PLINQ/TPL pueden producir
excepciones.
Vea también
Programación en paralelo
Implementar las particiones dinámicas
Implementar un particionador para particionamiento estático
Cómo: Implementar las particiones dinámicas
16/09/2020 • 4 minutes to read • Edit Online
Ejemplo
Cada vez que se llama a una partición MoveNext en el enumerador, este proporciona la partición con un
elemento de lista. En el caso de PLINQ y ForEach, la partición es una instancia Task. Dado que las solicitudes se
producen simultáneamente en varios subprocesos, se sincroniza el acceso al índice actual.
//
// An orderable dynamic partitioner for lists
//
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Numerics;
class ConsumerClass
{
static void Main()
{
var nums = Enumerable.Range(0, 10000).ToArray();
OrderableListPartitioner<int> partitioner = new OrderableListPartitioner<int>(nums);
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Collections.Concurrent
Module Module1
Public Class OrderableListPartitioner(Of TSource)
Inherits OrderablePartitioner(Of TSource)
For i = 0 To partitionCount - 1
partitions(i) = dynamicPartitions.GetEnumerator()
Next
Return partitions
End Function
End Class
End Class
Class ConsumerClass
Console.BufferHeight = 20000
Dim nums = Enumerable.Range(0, 2000).ToArray()
Console.WriteLine("PLINQ -----------------------------------")
End Module
Este es un ejemplo de creación de particiones por fragmentos, donde cada fragmento consta de un elemento.
Proporcionando más elementos a la vez, podría reducir la contención sobre el bloqueo y teóricamente lograr un
rendimiento más rápido. Sin embargo, en algún momento, los fragmentos más grandes podrían requerir lógica
de equilibrio de carga adicional con el fin de mantener todos los subprocesos ocupados hasta que se completa
todo el trabajo.
Vea también
Particionadores personalizados para PLINQ y TPL
Implementar un particionador para particionamiento estático
Cómo: Implementar un particionador para
particionamiento estático
16/09/2020 • 4 minutes to read • Edit Online
En el ejemplo siguiente se muestra una forma de implementar un particionador personalizado sencillo para PLINQ
que realiza una partición estática. Dado que el particionador no admite las particiones dinámicas, no se puede usar
desde Parallel.ForEach. Este particionador particular puede proporcionar velocidad con respecto al particionador
por rangos predeterminado para los orígenes de datos, para los que cada elemento requieren una cantidad
creciente de tiempo de procesamiento.
Ejemplo
// A static range partitioner for sources that require
// a linear increase in processing time for each succeeding element.
// The range sizes are calculated based on the rate of increase
// with the first partition getting the most elements and the
// last partition getting the least.
class MyPartitioner : Partitioner<int>
{
int[] source;
double rateOfIncrease = 0;
_list.Add(GetItemsForPartition(start, end));
// For demonstratation.
Console.WriteLine("start = {0} b (end) = {1}", start, end);
}
return (IList<IEnumerator<int>>)_list;
}
/*
*
*
* B
// Model increasing workloads as a right triangle / |
divided into equal areas along vertical lines. / | |
Each partition is taller and skinnier / | |
than the last. / | | |
/ | | |
/ | | |
/ | | | |
/ | | | |
A /______|____|___|__| C
*/
private int[] CalculatePartitions(int partitionCount, int sourceLength)
{
// Corresponds to the opposite side of angle A, which corresponds
// to an index into the source array.
int[] partitionLimits = new int[partitionCount];
partitionLimits[0] = 0;
// Represent total work as rectangle of source length times "most expensive element"
// Note: RateOfIncrease can be factored out of equation.
double totalWork = sourceLength * (sourceLength * rateOfIncrease);
// Divide by two to get the triangle whose slope goes from zero on the left to "most"
// on the right. Then divide by number of partitions to get area of each partition.
totalWork /= 2;
double partitionArea = totalWork / partitionCount;
// Solve for base given the area and the slope of the hypotenuse.
partitionLimits[i] = (int)Math.Floor(Math.Sqrt((2 * area) / rateOfIncrease));
}
return partitionLimits;
}
class Consumer
{
public static void Main2()
{
var source = Enumerable.Range(0, 10000).ToArray();
Stopwatch sw = Stopwatch.StartNew();
MyPartitioner partitioner = new MyPartitioner(source, .5);
sw = Stopwatch.StartNew();
Las particiones de este ejemplo se basan en el supuesto de un aumento lineal del tiempo de procesamiento de
cada elemento. En el mundo real, puede que sea difícil predecir los tiempos de procesamiento de esta manera. Si
usa un particionador estático con un origen de datos específico, puede optimizar la fórmula de partición del
origen, agregar la lógica de equilibrio de carga o usar un enfoque de partición por fragmentos, como se explica en
Cómo: Implementar las particiones dinámicas.
Vea también
Particionadores personalizados para PLINQ y TPL
Expresiones lambda en PLINQ y TPL
16/09/2020 • 4 minutes to read • Edit Online
La biblioteca TPL contiene muchos métodos que adoptan una de las familias System.Func<TResult> o
System.Action de delegados como parámetros de entrada. Puede usar estos delegados para pasar de la
lógica personalizada del programa al bucle, tarea o consulta en paralelo. Los ejemplos de código de TPL,
igual que igual que de PLINQ, usan expresiones lambda para crear instancias de esos delegados como
bloques de código insertados. En este tema se proporciona una breve introducción a Func y Action, y
muestra cómo usar las expresiones lambda en la biblioteca TPL y PLINQ.
NOTE
Para más información sobre los delegados en general, vea Delegados y Delegados. Para obtener más información
sobre las expresiones lambda en C# y Visual Basic, vea Expresiones lambda y Expresiones lambda.
Func (delegado)
Un delegado Func encapsula un método que devuelve un valor. En una signatura Func , el último
parámetro de tipo o el ubicado más a la derecha siempre especifica el tipo de devolución. Una causa común
de los errores de compilador es intentar pasar dos parámetros de entrada a un System.Func<T,TResult>; de
hecho, este tipo solo acepta un parámetro de entrada. .NET define 17 versiones de Func :
System.Func<TResult>, System.Func<T,TResult>, System.Func<T1,T2,TResult>, etc. hasta
System.Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult>.
Action (delegado)
Un delegado System.Action encapsula un método (Sub en Visual Basic) que no devuelve ningún valor. En
una signatura de tipo Action , los parámetros de tipo representan solo parámetros de entrada. Al igual que
Func , .NET define 17 versiones de Action , desde una versión que no tiene parámetros de tipo hasta una
versión que tiene 16 de ellos.
Ejemplo
El siguiente ejemplo del método Parallel.ForEach<TSource,TLocal>(IEnumerable<TSource>, Func<TLocal>,
Func<TSource,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) muestra cómo expresar delegados Func y
Action mediante expresiones lambda.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class ForEachWithThreadLocal
{
// Demonstrated features:
// Parallel.ForEach()
// Thread-local state
// Expected results:
// This example sums up the elements of an int[] in parallel.
// Each thread maintains a local sum. When a thread is initialized, that local sum is set to
0.
// On every iteration the current element is added to the local sum.
// When a thread is done, it safely adds its local sum to the global sum.
// After the loop is complete, the global sum is printed out.
// Documentation:
// http://msdn.microsoft.com/library/dd990270(VS.100).aspx
static void Main()
{
// The sum of these elements is 40.
int[] input = { 4, 1, 6, 2, 9, 5, 10, 3 };
int sum = 0;
try
{
Parallel.ForEach(
input, // source collection
() => 0, // thread local initializer
(n, loopState, localSum) => // body
{
localSum += n;
Console.WriteLine("Thread={0}, n={1}, localSum={2}",
Thread.CurrentThread.ManagedThreadId, n, localSum);
return localSum;
},
(localSum) => Interlocked.Add(ref sum, localSum) // thread local aggregator
);
Console.WriteLine("\nSum={0}", sum);
}
// No exception is expected in this example, but if one is still thrown from a task,
// it will be wrapped in AggregateException and propagated to the main thread.
catch (AggregateException e)
{
Console.WriteLine("Parallel.ForEach has thrown an exception. THIS WAS NOT EXPECTED.\n{0}",
e);
}
}
}
Imports System.Threading
Imports System.Threading.Tasks
Module ForEachDemo
Try
' source collection
Parallel.ForEach(input,
Function()
' thread local initializer
Return 0
End Function,
Function(n, loopState, localSum)
' body
localSum += n
Console.WriteLine("Thread={0}, n={1}, localSum={2}",
Thread.CurrentThread.ManagedThreadId, n, localSum)
Return localSum
End Function,
Sub(localSum)
' thread local aggregator
Interlocked.Add(sum, localSum)
End Sub)
End Module
Vea también
Programación en paralelo
Más información (Programación paralela)
16/09/2020 • 2 minutes to read • Edit Online
Los siguientes recursos contienen información adicional sobre programación paralela en .NET:
En el documento Patterns for Parallel Programming: Understanding and Applying Parallel Patterns with the
.NET Framework 4 (Patrones de programación paralela: descripción y aplicación de patrones paralelos con
.NET Framework 4) se describen patrones paralelos comunes y procedimientos recomendados para
desarrollar componentes paralelos con el uso de esos patrones.
En el libro Design Patterns for Decomposition and Coordination on Multicore Architectures (Diseño de
patrones para descomposición y coordinación en arquitecturas multinúcleo) se describen los patrones de
programación paralela que usan la compatibilidad con la programación paralela introducida en .NET
Framework 4.
El blog sobre programación paralela con .NET contiene muchos artículos detallados sobre la programación
paralela en .NET.
La página Ejemplos de programación paralela con .NET Core y .NET Standard contiene muchos ejemplos
que muestran las técnicas de programación paralela intermedias y avanzadas.
Vea también
Centro para desarrolladores de informática en paralelo
Programación en paralelo en Visual C++
Subprocesamiento administrado
16/09/2020 • 3 minutes to read • Edit Online
Tanto si va a desarrollar aplicaciones para equipos con uno o varios procesadores, desea que la aplicación
proporcione la interacción con mayor capacidad de respuesta con el usuario, incluso si la aplicación actualmente
hace otro trabajo. Usar varios subprocesos de ejecución es una de las formas más eficaces de mantener que la
aplicación siga respondiendo al usuario y, al mismo tiempo, usar el procesador entre eventos de usuario o durante
los mismos. Si bien esta sección presenta los conceptos básicos del subprocesamiento, se centra en los conceptos
del subprocesamiento administrado y su uso.
NOTE
A partir de .NET Framework 4, la programación multiproceso se ha simplificado significativamente con las clases
System.Threading.Tasks.Parallel y System.Threading.Tasks.Task, Parallel LINQ (PLINQ), clases de colecciones simultáneas
nuevas en el espacio de nombres System.Collections.Concurrent y un nuevo modelo de programación basado en el concepto
de tareas en lugar de subprocesos. Para más información, consulte Programación en paralelo.
En esta sección
Principios básicos del subprocesamiento administrado
Proporciona información general sobre el subprocesamiento administrado y describe cuándo usar varios
subprocesos.
Usar subprocesos y subprocesamiento
Explica cómo crear, iniciar, pausar, reanudar y anular subprocesos.
Procedimientos recomendados para el subprocesamiento administrado
Se describen los niveles de sincronización, cómo evitar interbloqueos y condiciones de carrera y otros problemas
de subprocesos.
Objetos y características de subprocesos
Describe las clases administradas que puede usar para sincronizar las actividades de subprocesamientos y los
datos de objetos accedidos en distintos subprocesos, y proporciona información general sobre los subprocesos de
grupos de subprocesos.
Referencia
System.Threading
Contiene clases para usar y sincronizar subprocesos administrados.
System.Collections.Concurrent
Contiene clases de colección seguras para usarlas con varios subprocesos.
System.Threading.Tasks
Contiene clases para crear y programar tareas de procesamiento simultáneo.
Secciones relacionadas
Dominios de aplicación
Proporciona información genera sobre los dominios de aplicación y de su uso en Common Language
Infrastructure.
E/S de archivos asincrónica
Describe las ventajas de rendimiento y el funcionamiento básico de la E/S asincrónica.
Modelo asincrónico basado en tareas (TAP)
Proporciona una introducción del patrón recomendado para programación asincrónica en. NET.
Llamada a métodos sincrónicos de forma asincrónica
Explica cómo llamar a métodos en los subprocesos de grupos de subprocesos con características integradas de
delegados.
Programación en paralelo
Describe las bibliotecas de programación en paralelo, las que simplifican el uso de varios subprocesos en las
aplicaciones.
Parallel LINQ (PLINQ)
Describe un sistema para ejecutar consultas en paralelo a fin de aprovechar los distintos procesadores.
Serialización en .NET
16/09/2020 • 2 minutes to read • Edit Online
Referencia
System.Runtime.Serialization
Contiene clases que se pueden usar para serializar y deserializar objetos.
System.Xml.Serialization
Contiene clases que se pueden utilizar para serializar objetos en documentos o secuencias de formato XML.
System.Text.Json
Contiene clases que se pueden usar para serializar objetos en documentos o secuencias de formato JSON.
Serialización y deserialización de JSON en .NET:
información general
16/09/2020 • 2 minutes to read • Edit Online
El espacio de nombres System.Text.Json proporciona funcionalidad para serializar y deserializar desde JSON
(notaciones de objetos JavaScript).
El diseño de biblioteca resalta el rendimiento elevado y la asignación de memoria baja en un amplio conjunto de
características. La compatibilidad integrada con UTF-8 optimiza el proceso de lectura y escritura de texto JSON
codificado como UTF-8, que es la codificación más frecuente de los datos en Internet y los archivos en disco.
La biblioteca también proporciona clases para trabajar con un Document Object Model (DOM) en memoria. Esta
característica permite el acceso de solo lectura aleatorio de los elementos de una cadena o archivo JSON.
Recursos adicionales
Cómo usar la biblioteca
Procedimiento para migrar desde Newtonsoft.Json
Procedimiento para escribir convertidores
Código fuente System.Text.Json
Referencia de API System.Text.Json
Referencia de API de System.Text.Json.Serialization
Procedimiento para serializar y deserializar (calcular
referencias y resolver referencias) JSON en .NET
16/09/2020 • 49 minutes to read • Edit Online
En este artículo se muestra cómo usar el espacio de nombres System.Text.Json para serializar y deserializar a y
desde la notación de objetos JavaScript (JSON). Si va a portar el código existente de Newtonsoft.Json , consulte
Procedimiento para migrar a System.Text.Json .
Las instrucciones y el código de ejemplo usan la biblioteca directamente, no a través de un marco como
ASP.NET Core.
La mayor parte del código de ejemplo de la serialización establece JsonSerializerOptions.WriteIndented en true
para "imprimir correctamente" el JSON (con sangría y espacio en blanco para mayor legibilidad). Para su uso en
producción, normalmente aceptaría el valor predeterminado de false para este valor.
Los ejemplos de código hacen referencia a la siguiente clase y sus variantes:
Espacios de nombres
El espacio de nombres System.Text.Json contiene todos los puntos de entrada y los tipos principales. El espacio de
nombres System.Text.Json.Serialization contiene atributos e interfaces API para escenarios avanzados y
personalización específicos de la serialización y deserialización. Los ejemplos de código que se muestran en este
artículo requieren directivas using para uno o ambos espacios de nombres:
using System.Text.Json;
using System.Text.Json.Serialization;
string jsonString;
jsonString = JsonSerializer.Serialize(weatherForecast);
En los ejemplos anteriores se usa la inferencia de tipos para el tipo que se está serializando. Una sobrecarga de
Serialize() toma un parámetro de tipo genérico:
jsonString = JsonSerializer.Serialize<WeatherForecastWithPOCOs>(weatherForecast);
Ejemplo de serialización
Aquí se muestra una clase de ejemplo que contiene colecciones y una clase anidada:
La salida JSON de la serialización de una instancia del tipo anterior es similar al ejemplo siguiente. La salida JSON
se minimiza de manera predeterminada:
{"Date":"2019-08-01T00:00:00-07:00","TemperatureCelsius":25,"Summary":"Hot","DatesAvailable":["2019-08-
01T00:00:00-07:00","2019-08-02T00:00:00-07:00"],"TemperatureRanges":{"Cold":{"High":20,"Low":-10},"Hot":
{"High":60,"Low":20}},"SummaryWords":["Cool","Windy","Humid"]}
En el ejemplo siguiente se muestra el mismo JSON, con formato (es decir, impreso correctamente con espacio en
blanco y sangría):
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot",
"DatesAvailable": [
"2019-08-01T00:00:00-07:00",
"2019-08-02T00:00:00-07:00"
],
"TemperatureRanges": {
"Cold": {
"High": 20,
"Low": -10
},
"Hot": {
"High": 60,
"Low": 20
}
},
"SummaryWords": [
"Cool",
"Windy",
"Humid"
]
}
Serialización a UTF -8
Para serializar a UTF-8, llame al método JsonSerializer.SerializeToUtf8Bytes:
byte[] jsonUtf8Bytes;
var options = new JsonSerializerOptions
{
WriteIndented = true
};
jsonUtf8Bytes = JsonSerializer.SerializeToUtf8Bytes(weatherForecast, options);
También está disponible una sobrecarga Serialize que toma un valor Utf8JsonWriter.
La serialización a UTF-8 es aproximadamente un 5-10 % más rápida que el uso de métodos basados en cadenas.
La diferencia se debe a que no hay que convertir los bytes (como UTF-8) en cadenas (UTF-16).
Comportamiento de serialización
De manera predeterminada, se serializan todas las propiedades públicas. Puede especificar propiedades para
excluir.
El codificador predeterminado escapa a caracteres que no son ASCII, caracteres que distinguen HTML en el
intervalo ASCII y caracteres que deben escaparse según la especificación de JSON RFC 8259.
De forma predeterminada, JSON se minimiza. Puede imprimir correctamente el JSON.
De forma predeterminada, el uso de mayúsculas y minúsculas en los nombres JSON coincide con el de los
nombres de .NET. Puede personalizar el uso de mayúsculas y minúsculas e nombres JSON.
Se detectan las referencias circulares y se inician las excepciones.
Actualmente, se excluyen los campos.
Los tipos no admitidos incluyen:
Elementos primitivos de .NET que se asignan a elementos primitivos de JavaScript, tales como tipos numéricos,
cadenas y valores booleanos.
Objetos CLR antiguos sin formato (POCO) definidos por el usuario.
Matrices unidimensionales y escalonadas ( ArrayName[][] ).
Dictionary<string,TValue> donde TValue es object , JsonElement o un POCO.
Colecciones de los espacios de nombres siguientes.
System.Collections
System.Collections.Generic
System.Collections.Immutable
Puede implementar convertidores personalizados para controlar tipos adicionales o proporcionar funcionalidad
que no admiten los convertidores integrados.
weatherForecast = JsonSerializer.Deserialize<WeatherForecastWithPOCOs>(jsonString);
Para deserializar a partir de un archivo mediante código sincrónico, lea el archivo en una cadena, tal y como se
muestra en el ejemplo siguiente:
jsonString = File.ReadAllText(fileName);
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString);
Para deserializar a partir de un archivo mediante código asincrónico, llame al método DeserializeAsync:
Comportamiento de la deserialización
De forma predeterminada, la coincidencia de nombres de la propiedad distingue mayúsculas de minúsculas.
Puede especificar la no distinción de mayúsculas y minúsculas.
Si el archivo JSON contiene un valor para una propiedad de solo lectura, el valor se omite y no se inicia
ninguna excepción.
No se admite la deserialización a tipos de referencia sin un constructor sin parámetros.
No se admite la deserialización a objetos inmutables o propiedades de solo lectura.
De forma predeterminada, las enumeraciones se admiten como números. Puede serializar nombres de
enumeración como cadenas.
No se admiten los campos.
De forma predeterminada, los comentarios o las comas finales en el JSON inician excepciones. Puede permitir
comentarios y comas finales.
La profundidad máxima predeterminada es 64.
Puede implementar convertidores personalizados para proporcionar funcionalidad que no admiten los
convertidores integrados.
Aquí se muestra un tipo de ejemplo que se serializa y la salida de JSON impresa correctamente:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot",
"Wind": 35
}
{
"date": "2019-08-01T00:00:00-07:00",
"temperatureCelsius": 25,
"summary": "Hot",
"Wind": 35
}
using System.Text.Json;
namespace SystemTextJsonSamples
{
public class UpperCaseNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name) =>
name.ToUpper();
}
}
{
"DATE": "2019-08-01T00:00:00-07:00",
"TEMPERATURECELSIUS": 25,
"SUMMARY": "Hot",
"Wind": 35
}
La serialización de un objeto con un diccionario denominado TemperatureRanges que tenga pares clave-valor
"ColdMinTemp", 20 y "HotMinTemp", 40 se traduciría en una salida JSON similar a la del ejemplo siguiente:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot",
"TemperatureRanges": {
"coldMinTemp": 20,
"hotMinTemp": 40
}
}
La directiva de nomenclatura en mayúsculas y minúsculas combinadas Camel para las claves de diccionario se
aplica solo a la serialización. Si se deserializa un diccionario, las claves coincidirán con el archivo JSON aunque se
especifique JsonNamingPolicy.CamelCase para DictionaryKeyPolicy .
Enumeraciones como cadenas
De forma predeterminada, las enumeraciones se serializan como números. Para serializar nombres de
enumeración como cadenas, use JsonStringEnumConverter.
Por ejemplo, supongamos que hay que serializar la siguiente clase que tiene una enumeración:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": 3
}
En el código de ejemplo siguiente se serializan los nombres de enumeración en lugar de los valores numéricos y
los nombres se convierten en mayúsculas y minúsculas combinadas Camel:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "hot"
}
Los nombres de cadena de enumeración también se pueden deserializar, tal y como se muestra en el ejemplo
siguiente:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
}
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot",
}
Esta opción solo se aplica a la serialización. Durante la deserialización, las propiedades de solo lectura se omiten
de forma predeterminada.
Exclusión de todas las propiedades de valores NULL
Para excluir todas las propiedades de valores NULL, establezca la propiedad IgnoreNullValues en true , tal y como
se muestra en el ejemplo siguiente:
P RO P IEDA D. VA LO R
TemperatureCelsius 25
Resumen null
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25
}
Este valor se aplica a la serialización y la deserialización. Para obtener información sobre su efecto en la
deserialización, consulte el artículo sobre cómo omitir valores NULL al deserializar.
using System;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
Este código no escapa a los caracteres cirílicos o griegos. Si la propiedad Summary está establecida en cirílico
жарко, el objeto WeatherForecast se serializa tal y como se muestra en este ejemplo:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "жарко"
}
Para serializar todos los conjuntos de lenguaje sin escape, use UnicodeRanges.All.
Serialización de caracteres específicos
Una alternativa consiste en especificar caracteres individuales que se quieren dejar pasar sin secuencia de escape.
En el ejemplo siguiente se serializan solo los dos primeros caracteres de жарко:
using System;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
using System;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
Cau t i on
Supongamos también que el argumento de tipo del método Serialize en tiempo de compilación es
WeatherForecast :
En este escenario, la propiedad WindSpeed no se serializa aunque el objeto weatherForecast sea en realidad un
objeto WeatherForecastDerived . Solo se serializan las propiedades de la clase base:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
Este comportamiento está diseñado para ayudar a evitar la exposición accidental de datos en un tipo derivado
creado en tiempo de ejecución.
Para serializar las propiedades del tipo derivado del ejemplo anterior, use uno de los enfoques siguientes:
Llame a una sobrecarga de Serialize que le permita especificar el tipo en tiempo de ejecución:
En el escenario de ejemplo anterior, ambos enfoques hacen que la propiedad WindSpeed se incluya en la salida
JSON:
{
"WindSpeed": 35,
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
IMPORTANT
Estos enfoques proporcionan serialización polimórfica solo para el objeto raíz que se va a serializar, no para las propiedades
de dicho objeto raíz.
Puede obtener una serialización polimórfica para objetos de nivel inferior si se definen como tipo object . Por
ejemplo, supongamos que la clase WeatherForecast tiene una propiedad denominada PreviousForecast que se
puede definir como tipo WeatherForecast o object :
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot",
"PreviousForecast": {
"WindSpeed": 35,
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
}
El mismo enfoque para definir propiedades como object funciona con interfaces. Supongamos que tiene la
interfaz y la implementación siguientes, y quiere serializar una clase con propiedades que contienen instancias de
implementación:
using System;
namespace SystemTextJsonSamples
{
public interface IForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string Summary { get; set; }
}
Cuando se serializa una instancia de Forecasts , solo Tuesday muestra la propiedad WindSpeed , porque Tuesday
se define como object :
Para obtener más información sobre la serialización polimórfica e información sobre la deserialización ,
consulte Migración desde Newtonsoft.Json a System.Text.Json.
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25, // Fahrenheit 77
"Summary": "Hot", /* Zharko */
}
Aquí se muestra un ejemplo de JSON con nombres de propiedades en mayúsculas y minúsculas combinadas
Camel. Se puede deserializar en el tipo siguiente que tenga nombres de propiedades en mayúsculas y minúsculas
Pascal.
{
"date": "2019-08-01T00:00:00-07:00",
"temperatureCelsius": 25,
"summary": "Hot",
}
{
"Date": "2019-08-01T00:00:00-07:00",
"temperatureCelsius": 25,
"Summary": "Hot",
"DatesAvailable": [
"2019-08-01T00:00:00-07:00",
"2019-08-02T00:00:00-07:00"
],
"SummaryWords": [
"Cool",
"Windy",
"Humid"
]
}
Si deserializa el JSON que se muestra en el tipo mostrado, las propiedades DatesAvailable y SummaryWords no
tienen a donde ir y se pierden. Para capturar datos adicionales tales como estas propiedades, aplique el atributo
JsonExtensionData a una propiedad de tipo Dictionary<string,object> o Dictionary<string,JsonElement> :
P RO P IEDA D. VA LO R N OTA S
Cuando se serializa el objeto de destino, los pares clave-valor de los datos de la extensión se convierten en
propiedades JSON tal y como estaban en el JSON entrante:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 0,
"Summary": "Hot",
"temperatureCelsius": 25,
"DatesAvailable": [
"2019-08-01T00:00:00-07:00",
"2019-08-02T00:00:00-07:00"
],
"SummaryWords": [
"Cool",
"Windy",
"Humid"
]
}
Observe que el nombre de la propiedad ExtensionData no aparece en el archivo JSON. Este comportamiento
permite que JSON realice un viaje de ida y vuelta sin perder ningún dato adicional que de otro modo no se
deserializaría.
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": null
}
Con esta opción, la propiedad Summary del objeto WeatherForecastWithDefault es el valor predeterminado "No
summary" después de la deserialización.
Los valores NULL en JSON solo se omiten si son válidos. Los valores NULL para los tipos de valor que no aceptan
valores NULL provocan excepciones.
System.Text.Json.JsonDocument ofrece la posibilidad de crear una especificación Document Object Model de solo
lectura (DOM) mediante Utf8JsonReader . La especificación DOM proporciona acceso aleatorio a los datos en una
carga JSON. A los elementos JSON que componen la carga se puede acceder mediante el tipo JsonElement. El tipo
JsonElement contiene los enumeradores de matriz y objeto junto con las API para convertir texto JSON en tipos
de .NET comunes. JsonDocument expone una propiedad RootElement.
En las secciones siguientes se muestra cómo usar estas herramientas para leer y escribir JSON.
Uso de JsonDocument para acceder a datos
En el ejemplo siguiente se muestra cómo usar la clase JsonDocument para el acceso aleatorio a los datos de una
cadena JSON:
double sum = 0;
int count = 0;
El código anterior:
Supone que el JSON que se va a analizar está en una cadena denominada jsonString .
Calcula la calificación media de los objetos de una matriz de Students que tienen una propiedad Grade .
Asigna una calificación predeterminada de 70 a los alumnos que no tienen ninguna calificación.
Cuenta los alumnos incrementando una variable count con cada iteración. Una alternativa es llamar a
GetArrayLength, tal y como se muestra en el ejemplo siguiente:
double sum = 0;
int count = 0;
count = studentsElement.GetArrayLength();
Aquí se muestra un ejemplo del código JSON que este código procesa:
{
"Class Name": "Science",
"Teacher\u0027s Name": "Jane",
"Semester": "2019-01-01",
"Students": [
{
"Name": "John",
"Grade": 94.3
},
{
"Name": "James",
"Grade": 81.0
},
{
"Name": "Julia",
"Grade": 91.9
},
{
"Name": "Jessica",
"Grade": 72.4
},
{
"Name": "Johnathan"
}
],
"Final": true
}
if (root.ValueKind == JsonValueKind.Object)
{
writer.WriteStartObject();
}
else
{
return;
}
writer.WriteEndObject();
writer.Flush();
El código anterior:
Lee un archivo JSON, carga los datos en un JsonDocument y escribe JSON con formato (impreso
correctamente) en un archivo.
Utiliza JsonDocumentOptions para especificar que se permiten los comentarios en el JSON de entrada, pero se
omiten.
Cuando termina, llama a Flush en el escritor. Una alternativa consiste en permitir el vaciado automático del
escritor cuando se elimina.
Aquí se muestra un ejemplo de entrada JSON que el código de ejemplo va a procesar:
Uso de Utf8JsonWriter
En el siguiente ejemplo, se muestra cómo utilizar la clase Utf8JsonWriter:
Uso de Utf8JsonReader
En el siguiente ejemplo, se muestra cómo utilizar la clase Utf8JsonReader:
var options = new JsonReaderOptions
{
AllowTrailingCommas = true,
CommentHandling = JsonCommentHandling.Skip
};
Utf8JsonReader reader = new Utf8JsonReader(jsonUtf8Bytes, options);
while (reader.Read())
{
Console.Write(reader.TokenType);
switch (reader.TokenType)
{
case JsonTokenType.PropertyName:
case JsonTokenType.String:
{
string text = reader.GetString();
Console.Write(" ");
Console.Write(text);
break;
}
case JsonTokenType.Number:
{
int intValue = reader.GetInt32();
Console.Write(" ");
Console.Write(intValue);
break;
}
En el código anterior se supone que la variable jsonUtf8 es una matriz de bytes que contiene JSON válido, con
codificación UTF-8.
Filtrado de datos con Utf8JsonReader
En el ejemplo siguiente se muestra cómo leer un archivo de forma sincrónica y buscar un valor:
using System;
using System.IO;
using System.Text;
using System.Text.Json;
namespace SystemTextJsonSamples
{
public class Utf8ReaderFromFile
{
private static readonly byte[] s_nameUtf8 = Encoding.UTF8.GetBytes("name");
private static ReadOnlySpan<byte> Utf8Bom => new byte[] { 0xEF, 0xBB, 0xBF };
public static void Run()
{
// ReadAllBytes if the file encoding is UTF-8:
string fileName = "UniversitiesUtf8.json";
ReadOnlySpan<byte> jsonReadOnlySpan = File.ReadAllBytes(fileName);
int count = 0;
int total = 0;
while (reader.Read())
{
JsonTokenType tokenType = reader.TokenType;
switch (tokenType)
{
case JsonTokenType.StartObject:
total++;
break;
case JsonTokenType.PropertyName:
if (reader.ValueTextEquals(s_nameUtf8))
{
// Assume valid JSON, known schema
reader.Read();
if (reader.GetString().EndsWith("University"))
{
count++;
}
}
break;
}
}
Console.WriteLine($"{count} out of {total} have names that end with 'University'");
}
}
}
El código anterior:
Supone que el JSON contiene una matriz de objetos y cada objeto puede contener una propiedad "name"
de tipo cadena.
Cuenta los objetos y los valores de propiedad "name" que terminan en "University".
Supone que el archivo tiene codificación UTF-16 y lo transcodifica a UTF-8. Un archivo con codificación
UTF-8 puede leerse directamente en ReadOnlySpan<byte> mediante el código siguiente:
Si el archivo contiene una marca BOM UTF-8, quítela antes de pasar los bytes a Utf8JsonReader , ya que el
lector espera texto. De lo contrario, la marca BOM se considera JSON no válido y el lector inicia una
excepción.
Aquí se muestra un ejemplo de JSON que el código anterior puede leer. El mensaje de resumen resultante es "2 de
4 tienen nombres que terminan en 'University'":
[
{
"web_pages": [ "https://contoso.edu/" ],
"alpha_two_code": "US",
"state-province": null,
"country": "United States",
"domains": [ "contoso.edu" ],
"name": "Contoso Community College"
},
{
"web_pages": [ "http://fabrikam.edu/" ],
"alpha_two_code": "US",
"state-province": null,
"country": "United States",
"domains": [ "fabrikam.edu" ],
"name": "Fabrikam Community College"
},
{
"web_pages": [ "http://www.contosouniversity.edu/" ],
"alpha_two_code": "US",
"state-province": null,
"country": "United States",
"domains": [ "contosouniversity.edu" ],
"name": "Contoso University"
},
{
"web_pages": [ "http://www.fabrikamuniversity.edu/" ],
"alpha_two_code": "US",
"state-province": null,
"country": "United States",
"domains": [ "fabrikamuniversity.edu" ],
"name": "Fabrikam University"
}
]
using System;
using System.IO;
using System.Text;
using System.Text.Json;
namespace SystemTextJsonSamples
{
public class Utf8ReaderPartialRead
{
public static void Run()
{
var jsonString = @"{
""Date"": ""2019-08-01T00:00:00-07:00"",
""Temperature"": 25,
""TemperatureRanges"": {
""Cold"": { ""High"": 20, ""Low"": -10 },
""Hot"": { ""High"": 60, ""Low"": 20 }
},
""Summary"": ""Hot"",
}";
private static void GetMoreBytesFromStream(MemoryStream stream, ref byte[] buffer, ref Utf8JsonReader
reader)
{
int bytesRead;
if (reader.BytesConsumed < buffer.Length)
{
ReadOnlySpan<byte> leftover = buffer.AsSpan((int)reader.BytesConsumed);
if (leftover.Length == buffer.Length)
{
Array.Resize(ref buffer, buffer.Length * 2);
Console.WriteLine($"Increased buffer size to {buffer.Length}");
}
leftover.CopyTo(buffer);
bytesRead = stream.Read(buffer.AsSpan(leftover.Length));
}
else
{
bytesRead = stream.Read(buffer);
}
Console.WriteLine($"String in buffer is: {Encoding.UTF8.GetString(buffer)}");
reader = new Utf8JsonReader(buffer, isFinalBlock: bytesRead == 0, reader.CurrentState);
}
}
}
En el ejemplo anterior no se establece ningún límite para el tamaño del búfer. Si el tamaño del token es
demasiado grande, se podría producir un error en el código con una excepción OutOfMemoryException. Esto
puede ocurrir si el archivo JSON contiene un token de aproximadamente 1 GB o más de tamaño, ya que la
duplicación del tamaño de 1 GB da como resultado un tamaño demasiado grande para caber en un búfer de
int32 .
Recursos adicionales
Información general de System.Text.Json
Procedimientos para escribir convertidores personalizados
Procedimiento para migrar desde Newtonsoft.Json
Compatibilidad con DateTime y DateTimeOffset en System.Text.Json
Referencia de la API System.Text.Json
Procedimiento para escribir convertidores
personalizados para la serialización de JSON
(cálculo de referencias) en .NET
16/09/2020 • 28 minutes to read • Edit Online
En este artículo se muestra cómo crear convertidores personalizados para las clases de serialización de JSON
que se proporcionan en el espacio de nombres System.Text.Json. Para disponer de una introducción a
System.Text.Json , vea Cómo serializar y deserializar JSON en .NET.
Un convertidor es una clase que convierte un objeto o un valor en JSON; también admite conversiones a partir
de este formato. El espacio de nombres System.Text.Json tiene convertidores integrados para la mayoría de los
tipos primitivos que se asignan a primitivos de JavaScript. Puede escribir convertidores personalizados:
Para reemplazar el comportamiento predeterminado de un convertidor integrado. Por ejemplo, puede que
quiera que los valores DateTime se representen con el formato mm/dd/aaaa, en lugar del formato ISO 8601-
1:2019 predeterminado.
Para admitir un tipo de valor personalizado. Por ejemplo, una estructura PhoneNumber .
También puede escribir convertidores personalizados para personalizar o extender System.Text.Json con
funcionalidad no incluida en la versión actual. Los siguientes escenarios se describen más adelante en este
artículo:
Deserialización de tipos inferidos en las propiedades de objeto.
Compatibilidad para diccionarios con una clave que no sea de cadena.
Compatibilidad con la deserialización polimórfica.
Compatibilidad con el recorrido de ida y vuelta para Stack<T>.
Entre los ejemplos de tipos que puede controlar el patrón básico se incluyen los siguientes:
Dictionary<int, string>
WeekdaysEnum
List<DateTimeOffset>
DateTime
Int32
El patrón básico crea una clase que puede controlar un tipo. El patrón de fábrica crea una clase que determina,
en tiempo de ejecución, qué tipo específico es necesario y crea dinámicamente el convertidor adecuado.
Convertidor básico de ejemplo
El ejemplo siguiente es un convertidor que reemplaza la serialización predeterminada para un tipo de datos
existente. El convertidor usa el formato mm/dd/aaaa para las propiedades DateTimeOffset .
using System;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class DateTimeOffsetConverter : JsonConverter<DateTimeOffset>
{
public override DateTimeOffset Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
DateTimeOffset.ParseExact(reader.GetString(),
"MM/dd/yyyy", CultureInfo.InvariantCulture);
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class DictionaryTKeyEnumTValueConverter : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
if (!typeToConvert.IsGenericType)
{
return false;
}
if (typeToConvert.GetGenericTypeDefinition() != typeof(Dictionary<,>))
{
return false;
}
return typeToConvert.GetGenericArguments()[0].IsEnum;
}
public override JsonConverter CreateConverter(
Type type,
JsonSerializerOptions options)
{
Type keyType = type.GetGenericArguments()[0];
Type valueType = type.GetGenericArguments()[1];
return converter;
}
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
return dictionary;
}
// Add to dictionary.
dictionary.Add(key, v);
}
if (_valueConverter != null)
{
_valueConverter.Write(writer, kvp.Value, options);
}
else
{
JsonSerializer.Serialize(writer, kvp.Value, options);
}
}
writer.WriteEndObject();
}
}
}
}
El código anterior es el mismo que el que se muestra en Compatibilidad para diccionarios con una clave que no
sea de cadena, más adelante en este artículo.
Control de errores
Si necesita producir una excepción en el código de control de errores, considere la posibilidad de iniciar una
excepción JsonException sin un mensaje. Este tipo de excepción crea automáticamente un mensaje que incluye la
ruta de acceso a la parte del JSON que causó el error. Por ejemplo, la instrucción throw new JsonException();
genera un mensaje de error como el ejemplo siguiente:
Si proporciona un mensaje (por ejemplo, throw new JsonException("Error occurred") ), la excepción sigue
proporcionando la ruta de acceso en la propiedad Path.
Este es un ejemplo de salida JSON que muestra que se ha usado el convertidor personalizado:
{
"Date": "08/01/2019",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
En el código siguiente se usa el mismo enfoque para realizar una deserialización mediante el convertidor
DateTimeOffset personalizado:
weatherForecast = JsonSerializer.Deserialize<WeatherForecastWithConverterAttribute>(jsonString);
Ejemplo de registro: [JsonConverter] en un tipo
Este es el código que crea una estructura y le aplica el atributo [JsonConverter] :
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
[JsonConverter(typeof(TemperatureConverter))]
public struct Temperature
{
public Temperature(int degrees, bool celsius)
{
_degrees = degrees;
_isCelsius = celsius;
}
private bool _isCelsius;
private int _degrees;
public int Degrees => _degrees;
public bool IsCelsius => _isCelsius;
public bool IsFahrenheit => !_isCelsius;
public override string ToString() =>
$"{_degrees.ToString()}{(_isCelsius ? "C" : "F")}";
public static Temperature Parse(string input)
{
int degrees = int.Parse(input.Substring(0, input.Length - 1));
bool celsius = (input.Substring(input.Length - 1) == "C");
return new Temperature(degrees, celsius);
}
}
}
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class TemperatureConverter : JsonConverter<Temperature>
{
public override Temperature Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
Temperature.Parse(reader.GetString());
Si se registran varios convertidores personalizados para un tipo en la colección Converters , se usa el primer
convertidor que devuelve "true" para CanConvert .
Solo se elige un convertidor integrado si no se registra ningún convertidor personalizado aplicable.
En escenarios que requieren inferencia de tipos, el código siguiente muestra un convertidor personalizado para
las propiedades object . El código convierte:
true y false , en Boolean
Números sin decimales, en long
Números con un decimal, en double
Fechas, en DateTime
Cadenas, en string
Todo lo demás, en JsonElement
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class ObjectToInferredTypesConverter
: JsonConverter<object>
{
public override object Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.True)
{
return true;
}
if (reader.TokenType == JsonTokenType.False)
{
return false;
}
if (reader.TokenType == JsonTokenType.Number)
{
if (reader.TryGetInt64(out long l))
{
return l;
}
return reader.GetDouble();
}
if (reader.TokenType == JsonTokenType.String)
{
if (reader.TryGetDateTime(out DateTime datetime))
{
return datetime;
}
return reader.GetString();
}
El siguiente ejemplo de JSON para deserializar contiene valores que se deserializarán como DateTime , long y
string :
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot",
}
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class DictionaryTKeyEnumTValueConverter : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
if (!typeToConvert.IsGenericType)
{
return false;
}
if (typeToConvert.GetGenericTypeDefinition() != typeof(Dictionary<,>))
{
return false;
}
return typeToConvert.GetGenericArguments()[0].IsEnum;
}
return converter;
}
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
return dictionary;
}
// Add to dictionary.
dictionary.Add(key, v);
}
if (_valueConverter != null)
{
_valueConverter.Write(writer, kvp.Value, options);
}
else
{
JsonSerializer.Serialize(writer, kvp.Value, options);
}
}
writer.WriteEndObject();
}
}
}
}
El convertidor puede serializar y deserializar la propiedad TemperatureRanges de la siguiente clase que usa el
elemento Enum siguiente:
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class PersonConverterWithTypeDiscriminator : JsonConverter<Person>
{
enum TypeDiscriminator
{
Customer = 1,
Employee = 2
}
public override bool CanConvert(Type typeToConvert) =>
typeof(Person).IsAssignableFrom(typeToConvert);
reader.Read();
if (reader.TokenType != JsonTokenType.PropertyName)
{
throw new JsonException();
}
reader.Read();
if (reader.TokenType != JsonTokenType.Number)
{
throw new JsonException();
}
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
return person;
}
if (reader.TokenType == JsonTokenType.PropertyName)
{
propertyName = reader.GetString();
reader.Read();
switch (propertyName)
{
case "CreditLimit":
decimal creditLimit = reader.GetDecimal();
((Customer)person).CreditLimit = creditLimit;
break;
case "OfficeNumber":
string officeNumber = reader.GetString();
((Employee)person).OfficeNumber = officeNumber;
break;
case "Name":
string name = reader.GetString();
person.Name = name;
break;
}
}
}
writer.WriteString("Name", person.Name);
writer.WriteEndObject();
}
}
}
El convertidor puede deserializar el JSON que se creó con el mismo convertidor para serializar, por ejemplo:
[
{
"TypeDiscriminator": 1,
"CreditLimit": 10000,
"Name": "John"
},
{
"TypeDiscriminator": 2,
"OfficeNumber": "555-1234",
"Name": "Nancy"
}
]
El código del convertidor en el ejemplo anterior lee y escribe cada propiedad manualmente. Una alternativa es
llamar a Deserialize o Serialize para realizar parte del trabajo. Para obtener un ejemplo, vea esta publicación
de StackOverflow.
Compatibilidad con el recorrido de ida y vuelta para Stack<T>
Si deserializa una cadena JSON en un objeto Stack<T> y después serializa ese objeto, el contenido de la pila está
en orden inverso. Este comportamiento se aplica a los siguientes tipos e interfaz, así como a los tipos definidos
por el usuario que derivan de ellos:
Stack
Stack<T>
ConcurrentStack<T>
ImmutableStack<T>
IImmutableStack<T>
Para admitir la serialización y deserialización que conserva el orden original en la pila, se requiere un
convertidor personalizado.
En el código siguiente se muestra un convertidor personalizado que permite el recorrido de ida y vuelta hacia y
desde objetos Stack<T> :
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class JsonConverterFactoryForStackOfT : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
return typeToConvert.IsGenericType &&
typeToConvert.GetGenericTypeDefinition() == typeof(Stack<>);
}
return converter;
}
}
if (!reader.Read())
{
throw new JsonException();
}
}
return elements;
}
writer.WriteEndArray();
}
}
}
Recursos adicionales
Código fuente para convertidores integrados
Compatibilidad con DateTime y DateTimeOffset en System.Text.Json
Información general sobre System.Text.Json
Procedimiento para usar System.Text.Json
Procedimiento para migrar de Newtonsoft.Json
Referencia de API de System.Text.Json
Referencia de API de System.Text.Json.Serialization
Procedimiento para realizar la migración de
Newtonsoft.Json a System.Text.Json
16/09/2020 • 65 minutes to read • Edit Online
De forma predeterminada, no hay ninguna profundidad ️ Profundidad máxima predeterminada de 64, configurable
✔
máxima
Compatibilidad con una gran variedad de tipos ️ Algunos tipos requieren convertidores personalizados
⚠
Deserializar Dictionary con clave que no sea de cadena ️ No compatible, solución alternativa, ejemplo
⚠
Deserializar los tipos inferidos en propiedades de object ️ No compatible, solución alternativa, ejemplo
⚠
Deserializar el literal null de JSON a tipos de valor que no ️ No compatible, solución alternativa, ejemplo
⚠
aceptan valores NULL
Esta no es una lista exhaustiva de características de Newtonsoft.Json . La lista incluye muchos de los escenarios
que se han solicitado en publicaciones de problemas de GitHub o StackOverflow. Si implementa una solución
alternativa para uno de los escenarios que aquí se enumeran que no tenga actualmente un código de ejemplo, y
si quiere compartir la solución, haga clic en Esta página en la sección Comentarios de la parte inferior de esta
página. De esta forma se abre una incidencia en el repositorio de GitHub de esta documentación y también se
muestra en la sección Comentarios de esta página.
{
"name1": "value",
'name2': "value",
name3: 'value'
}
System.Text.Json solo acepta nombres de propiedad y valores de cadena entre comillas dobles, ya que ese es el
formato requerido por la especificación RFC 8259 y es el único formato que se considera JSON válido.
Un valor entre comillas simples da como resultado una JsonException con el siguiente mensaje:
{
"String1": 1,
"String2": true,
"String3": false
}
System.Text.Json no deserializa valores que no son de cadena en propiedades de cadena. Un valor que no sea
de cadena recibido para un campo de cadena da como resultado una JsonException con el siguiente mensaje:
using System;
using System.Buffers;
using System.Buffers.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class LongToStringConverter : JsonConverter<long>
{
public override long Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
{
ReadOnlySpan<byte> span = reader.HasValueSequence ? reader.ValueSequence.ToArray() :
reader.ValueSpan;
if (Utf8Parser.TryParse(span, out long number, out int bytesConsumed) && span.Length ==
bytesConsumed)
return number;
return reader.GetInt64();
}
Registre este convertidor personalizado usando un atributo en propiedades individuales de long o agregando
el convertidor a la colección Converters.
Diccionario con clave que no es de cadena
Newtonsoft.Json admite colecciones de tipo Dictionary<TKey, TValue> . La compatibilidad integrada con
colecciones de diccionarios en System.Text.Json se limita a Dictionary<string, TValue> . Por lo tanto, la clave
debe ser una cadena.
Para admitir un diccionario con un entero o algún otro tipo como clave, cree un convertidor como el ejemplo de
Cómo escribir convertidores personalizados.
Serialización polimórfica
Newtonsoft.Json realiza automáticamente la serialización polimórfica. Para obtener información sobre las
capacidades limitadas de serialización polimórfica de System.Text.Json, vea Serialización de propiedades de
clases derivadas.
La solución alternativa que se describe aquí es para definir propiedades que pueden contener clases derivadas
como el tipo object . Si eso no es posible, otra opción es crear un convertidor con un método Write para toda
la jerarquía de tipo de herencia, como en el ejemplo de Cómo escribir convertidores personalizados.
Deserialización polimórfica
Newtonsoft.Json tiene un valor TypeNameHandling que agrega metadatos de nombre de tipo al JSON durante la
serialización. Usa los metadatos durante la deserialización para realizar la deserialización polimórfica.
System.Text.Json puede realizar un intervalo limitado de serialización polimórfica, pero no deserialización
polimórfica.
Para admitir la deserialización polimórfica, cree un convertidor como el ejemplo de Cómo escribir convertidores
personalizados.
Deserialización de propiedades de objeto
Cuando Newtonsoft.Json deserializa en Object:
infiere el tipo de valores primitivos en la carga JSON (excepto null ) y devuelve los valores string , long ,
double , boolean o DateTime almacenados como un objeto al que se ha aplicado la conversión boxing. Los
valores primitivos son valores JSON únicos, como un número JSON, una cadena, un valor true , false o
null .
Devuelve JObject o JArray para valores complejos en la carga de JSON. Los valores complejos son
colecciones de pares clave-valor JSON entre llaves ( {} ) o listas de valores entre corchetes ( [] ). Las
propiedades y los valores entre llaves o corchetes pueden tener propiedades o valores adicionales.
Devuelve una referencia nula cuando la carga útil tiene el literal JSON null .
System.Text.Json almacena un objeto JsonElement al que se ha aplicado la conversión boxing para valores
primitivos y los complejos, siempre que se deserialice en Object; por ejemplo:
Propiedad object .
Un valor de diccionario object .
Un valor de matriz object .
Una raíz object .
Pero System.Text.Json trata null igual que Newtonsoft.Json , y devuelve una referencia nula cuando la carga
útil tiene el literal JSON null en ella.
Para implementar la inferencia de tipos para las propiedades object , cree un convertidor como el ejemplo de
Cómo escribir convertidores personalizados.
Deserialización de null en un tipo que no acepta valores NULL
Newtonsoft.Json no provoca una excepción en el escenario siguiente:
using System;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class DateTimeOffsetNullHandlingConverter : JsonConverter<DateTimeOffset>
{
public override DateTimeOffset Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
{
return default;
}
return reader.GetDateTimeOffset();
}
{
"Date": null,
"TemperatureCelsius": 25,
"Summary": null
}
using System;
using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class ImmutablePointConverter : JsonConverter<ImmutablePoint>
{
private readonly JsonEncodedText XName = JsonEncodedText.Encode("X");
private readonly JsonEncodedText YName = JsonEncodedText.Encode("Y");
int x = default;
bool xSet = false;
int y = default;
bool ySet = false;
if (reader.ValueTextEquals(XName.EncodedUtf8Bytes))
{
x = ReadProperty(ref reader, options);
xSet = true;
}
else if (reader.ValueTextEquals(YName.EncodedUtf8Bytes))
{
y = ReadProperty(ref reader, options);
ySet = true;
}
else
{
throw new JsonException();
}
reader.Read();
if (reader.TokenType != JsonTokenType.EndObject)
{
throw new JsonException();
}
{
"TemperatureCelsius": 25,
"Summary": "Hot"
}
Para que se produzca un error en la deserialización si no hay una propiedad Date en el objeto JSON,
implemente un convertidor personalizado. El siguiente código de convertidor de ejemplo produce una
excepción si no se establece la propiedad Date cuando se completa la deserialización:
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class WeatherForecastRequiredPropertyConverter : JsonConverter<WeatherForecast>
{
public override WeatherForecast Read(
ref Utf8JsonReader reader,
Type type,
JsonSerializerOptions options)
{
// Don't pass in options when recursively calling Deserialize.
WeatherForecast forecast = JsonSerializer.Deserialize<WeatherForecast>(ref reader);
[JsonConverter(typeof(WeatherForecastRequiredPropertyConverterForAttributeRegistration))]
public class WeatherForecastWithRequiredPropertyConverterAttribute
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string Summary { get; set; }
}
Y este es el convertidor:
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class WeatherForecastRequiredPropertyConverterForAttributeRegistration :
JsonConverter<WeatherForecastWithRequiredPropertyConverterAttribute>
{
public override WeatherForecastWithRequiredPropertyConverterAttribute Read(
ref Utf8JsonReader reader,
Type type,
JsonSerializerOptions options)
{
// OK to pass in options when recursively calling Deserialize.
WeatherForecastWithRequiredPropertyConverterAttribute forecast =
JsonSerializer.Deserialize<WeatherForecastWithoutRequiredPropertyConverterAttribute>(
ref reader,
options);
return forecast;
}
El convertidor de propiedades necesario requeriría lógica adicional en el caso de necesitar administrar atributos
como [JsonIgnore] u otras opciones, como codificadores personalizados. Además, el código de ejemplo no
controla las propiedades para las que se establece un valor predeterminado en el constructor, y este enfoque no
distingue entre los siguientes escenarios:
Falta una propiedad en el objeto JSON.
Hay una propiedad para un tipo que no acepta valores NULL en el objeto JSON, pero el valor es el
predeterminado para el tipo, como cero para int .
Hay una propiedad para un tipo de valor que acepta valores NULL en el objeto JSON, pero el valor es NULL.
Omitir condicionalmente una propiedad
Newtonsoft.Json ofrece varias formas de omitir condicionalmente una propiedad en la serialización o
deserialización:
DefaultContractResolver le permite seleccionar las propiedades que se van a incluir o excluir, en función de
criterios arbitrarios.
Los valores NullValueHandling y DefaultValueHandling de JsonSerializerSettings le permiten especificar
que se deben omitir todas las propiedades de valores NULL o valores predeterminados.
Los valores NullValueHandling y DefaultValueHandling del atributo [JsonProperty] le permiten especificar
propiedades individuales que se deben omitir cuando se establecen en null o en el valor predeterminado.
System.Text.Json proporciona las siguientes formas de omitir las propiedades durante la serialización:
El atributo [JsonIgnore] de una propiedad hace que la propiedad se omita en el objeto JSON durante la
serialización.
La opción global IgnoreNullValues le permite excluir todas las propiedades de valores NULL.
La opción global IgnoreReadOnlyProperties le permite excluir todas las propiedades de solo lectura.
Estas opciones no le permiten:
Omitir todas las propiedades que tienen el valor predeterminado para el tipo.
Omitir todas las propiedades seleccionadas que tienen el valor predeterminado para el tipo.
Omitir las propiedades seleccionadas si su valor es NULL.
Omitir las propiedades seleccionadas en función de criterios arbitrarios evaluados en tiempo de ejecución.
Para esa funcionalidad, puede escribir un convertidor personalizado. Este es un POCO de ejemplo y un
convertidor personalizado para él que ilustra este enfoque:
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class WeatherForecastRuntimeIgnoreConverter : JsonConverter<WeatherForecast>
{
public override WeatherForecast Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
return wf;
}
if (reader.TokenType == JsonTokenType.PropertyName)
if (reader.TokenType == JsonTokenType.PropertyName)
{
string propertyName = reader.GetString();
reader.Read();
switch (propertyName)
{
case "Date":
DateTimeOffset date = reader.GetDateTimeOffset();
wf.Date = date;
break;
case "TemperatureCelsius":
int temperatureCelsius = reader.GetInt32();
wf.TemperatureCelsius = temperatureCelsius;
break;
case "Summary":
string summary = reader.GetString();
wf.Summary = string.IsNullOrWhiteSpace(summary) ? "N/A" : summary;
break;
}
}
}
writer.WriteString("Date", wf.Date);
writer.WriteNumber("TemperatureCelsius", wf.TemperatureCelsius);
if (!string.IsNullOrWhiteSpace(wf.Summary) && wf.Summary != "N/A")
{
writer.WriteString("Summary", wf.Summary);
}
writer.WriteEndObject();
}
}
}
El convertidor hace que se omita la propiedad Summary de la serialización si su valor es NULL, una cadena vacía
o "N/A".
Registre este convertidor personalizado usando un atributo en la clase o agregando el convertidor a la colección
Converters.
Este enfoque requiere lógica adicional en los siguientes casos:
El objeto POCO incluye propiedades complejas.
Debe controlar los atributos como [JsonIgnore] , o las opciones como los codificadores personalizados.
Especificación del formato de fecha
Newtonsoft.Json proporciona varias maneras de controlar cómo se serializan y deserializan las propiedades de
los tipos DateTime y DateTimeOffset :
El valor DateTimeZoneHandling se puede usar para serializar todos los valores DateTime como fechas UTC.
El valor DateFormatString y los convertidores de DateTime se pueden usar para personalizar el formato de
las cadenas de fecha.
En System.Text.Json, el único formato que tiene compatibilidad integrada es ISO 8601-1:2019, ya que se ha
adoptado ampliamente, no es ambiguo, y realiza de forma precisa los recorridos de ida y vuelta. Para usar
cualquier otro formato, cree un convertidor personalizado. Para obtener más información, consulte
Compatibilidad con DateTime y DateTimeOffset en System.Text.Json.
Devoluciones de llamada
Newtonsoft.Json le permite ejecutar código personalizado en varios puntos en el proceso de serialización o
deserialización:
OnDeserializing: al empezar a deserializar un objeto
OnDeserialized: al finalizar la deserialización de un objeto
OnSerializing: al empezar a serializar un objeto
OnSerialized: al finalizar la serialización de un objeto
En System.Text.Json, puede simular devoluciones de llamada escribiendo un convertidor personalizado. En el
ejemplo siguiente se muestra un convertidor personalizado para un objeto POCO. El convertidor incluye código
que muestra un mensaje en cada punto que corresponde a una devolución de llamada de Newtonsoft.Json .
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SystemTextJsonSamples
{
public class WeatherForecastCallbacksConverter : JsonConverter<WeatherForecast>
{
public override WeatherForecast Read(
ref Utf8JsonReader reader,
Type type,
JsonSerializerOptions options)
{
// Place "before" code here (OnDeserializing),
// but note that there is no access here to the POCO instance.
Console.WriteLine("OnDeserializing");
return forecast;
}
Para más información sobre los convertidores personalizados que llaman a Serialize o Deserialize de forma
recursiva, consulte la sección Propiedades obligatorias anteriormente en este artículo.
Campos públicos y no públicos
Newtonsoft.Json puede serializar y deserializar los campos, así como las propiedades. System.Text.Json solo
funciona con propiedades públicas. Los convertidores personalizados no proporcionan esta funcionalidad.
Captadores y establecedores de propiedades internos y privados
Newtonsoft.Json puede usar captadores y establecedores de propiedades internos y privados a través del
atributo JsonProperty . System.Text.Json solo admite establecedores públicos. Los convertidores personalizados
no proporcionan esta funcionalidad.
Rellenar objetos existentes
El método JsonConvert.PopulateObject de Newtonsoft.Json deserializa un documento JSON en una instancia
existente de una clase, en lugar de crear una nueva instancia. System.Text.Json siempre crea una nueva instancia
del tipo de destino mediante el constructor sin parámetros público predeterminado. Los convertidores
personalizados se pueden deserializar en una instancia existente.
Reutilización en lugar de reemplazo de propiedades
El valor ObjectCreationHandling de Newtonsoft.Json le permite especificar que los objetos de las propiedades
deben reutilizarse en lugar de reemplazarse durante la deserialización. System.Text.Json siempre reemplaza los
objetos en las propiedades. Los convertidores personalizados no proporcionan esta funcionalidad.
Agregar a colecciones sin establecedores
Durante la deserialización, Newtonsoft.Json agrega objetos a una colección, incluso si la propiedad no tiene
ningún establecedor. System.Text.Json omite las propiedades que no tienen establecedores. Los convertidores
personalizados no proporcionan esta funcionalidad.
JsonDocument es IDisposable
JsonDocument compila una vista en memoria de los datos en un búfer agrupado. Por lo tanto, a diferencia de
JObject o JArray de Newtonsoft.Json , el tipo JsonDocument implementa IDisposable y debe usarse dentro de
un bloque Using.
Devuelva un JsonDocument desde la API solo si quiere transferir la propiedad de la duración y derivar la
responsabilidad al autor de la llamada. En la mayoría de los escenarios, eso no es necesario. Si el autor de la
llamada necesita trabajar con todo el documento JSON, devuelva el Clone del RootElement, que es un
JsonElement. Si el autor de la llamada necesita trabajar con un elemento determinado dentro del documento
JSON, devuelva el Clone de dicho JsonElement. Si devuelve el RootElement o un subelemento directamente sin
realizar un Clone , el autor de la llamada no podrá acceder al JsonElement devuelto después de que se elimine
el JsonDocument que lo posee.
Este es un ejemplo en el que se le requiere que realice un Clone :
El código anterior espera un JsonElement que contiene una propiedad fileName . Abre el archivo JSON y crea
un JsonDocument . El método supone que el autor de la llamada quiere trabajar con todo el documento, por lo
que devuelve el Clone del RootElement .
Si recibe un JsonElement y está devolviendo un subelemento, no es necesario devolver un Clone del
subelemento. El autor de la llamada es responsable de mantener activo el JsonDocument al que pertenece el
JsonElement pasado. Por ejemplo:
Dado que Utf8JsonReader considera que la entrada es texto JSON, se considera que una marca de orden de
bytes (BOM) de UTF-8 no es válida. El autor de la llamada debe filtrarla antes de pasar los datos al lector.
Para obtener códigos de ejemplo, vea Uso de Utf8JsonReader.
Lectura con ReadOnlySequence de varios segmentos
Si la entrada JSON es ReadOnlySpan<byte>, se puede acceder a cada elemento JSON desde la propiedad
ValueSpan en el lector a medida que avance por el bucle de lectura. Pero si la entrada es
ReadOnlySequence<byte> (que es el resultado de la lectura de PipeReader), algunos elementos JSON podrían
ocupar varios segmentos del objeto ReadOnlySequence<byte> . No se puede acceder a estos elementos desde
ValueSpan en un bloque de memoria contiguo. En su lugar, siempre que tenga un ReadOnlySequence<byte> de
varios segmentos como entrada, sondee la propiedad HasValueSequence en el lector para averiguar cómo
acceder al elemento JSON actual. Este es un patrón recomendado:
while (reader.Read())
{
switch (reader.TokenType)
{
// ...
ReadOnlySpan<byte> jsonElement = reader.HasValueSequence ?
reader.ValueSequence.ToArray() :
reader.ValueSpan;
// ...
}
}
while (reader.Read())
{
JsonTokenType tokenType = reader.TokenType;
switch (tokenType)
{
case JsonTokenType.StartObject:
total++;
break;
case JsonTokenType.PropertyName:
if (reader.ValueTextEquals(s_nameUtf8))
{
count++;
}
break;
}
Recursos adicionales
Información general de System.Text.Json
Modo de uso de System.Text.Json
Procedimientos para escribir convertidores personalizados
Compatibilidad con DateTime y DateTimeOffset en System.Text.Json
Referencia de API de System.Text.Json
Referencia de API de System.Text.Json.Serialization
Serialización binaria
16/09/2020 • 15 minutes to read • Edit Online
La serialización se puede definir como el proceso de almacenar el estado de un objeto a los medios de
almacenamiento. Durante este proceso, los campos públicos y privados del objeto y el nombre de la clase,
incluso el ensamblado que contiene la clase, se convierten en una secuencia de bytes, que se escribe a
continuación en un flujo de datos. Cuando se deserializa el objeto como consecuencia, se crea un clon exacto del
objeto original.
Al implementar un mecanismo de la serialización en un entorno orientado a objetos, tiene que realizar varios
intercambios entre la facilidad de uso y la flexibilidad. El proceso se puede automatizar en gran medida, con tal
de que sea proporcionado el control suficiente sobre el proceso. Por ejemplo, las situaciones se pueden presentar
donde la serialización binaria simple no es suficiente, o podría haber una razón concreta para decidir qué campos
en una clase necesitan ser serializados. Las secciones siguientes examinan el sólido mecanismo de serialización
proporcionado con .NET y resaltan varias características importantes que permiten personalizar el proceso para
satisfacer las necesidades.
NOTE
El estado de un objeto UTF-8 o UTF-7 codificado no se conserva si el objeto se serializa y se deserializa utilizando distintas
versiones de .NET Framework.
WARNING
La serialización binaria puede ser peligrosa. Para obtener más información, consulte la Guía de seguridad BinaryFormatter.
La serialización binaria permite modificar miembros privados dentro de un objeto y, por tanto, cambiar su
estado. Por este motivo, se recomiendan otros marcos de serialización, como System.Text.Json, que operan en la
superficie de la API pública.
.NET Core
.NET Core admite la serialización binaria para un subconjunto de tipos. Puede ver la lista de tipos compatibles en
la sección Tipos serializables siguiente. Se garantiza que los tipos indicados son serializables entre
.NET Framework 4.5.1 y versiones posteriores, y entre .NET Core 2.0 y versiones posteriores. Otras
implementaciones de .NET, como Mono, no son oficialmente compatibles, pero también deberían funcionar.
Tipos serializables
T IP O N OTA S
System.Array
System.ArraySegment<T>
System.Attribute
System.Boolean
System.Byte
System.Char
System.Collections.ArrayList
System.Collections.BitArray
System.Collections.Comparer
System.Collections.DictionaryEntry
System.Collections.Generic.Comparer<T>
System.Collections.Generic.Dictionary<TKey,TValue>
System.Collections.Generic.EqualityComparer<T>
System.Collections.Generic.HashSet<T>
System.Collections.Generic.KeyValuePair<TKey,TValue>
T IP O N OTA S
System.Collections.Generic.LinkedList<T>
System.Collections.Generic.List<T>
System.Collections.Generic.Queue<T>
System.Collections.Generic.SortedDictionary<TKey,TValue>
System.Collections.Generic.SortedList<TKey,TValue>
System.Collections.Generic.SortedSet<T>
System.Collections.Generic.Stack<T>
System.Collections.Hashtable
System.Collections.ObjectModel.Collection<T>
System.Collections.ObjectModel.KeyedCollection<TKey,TItem
>
System.Collections.ObjectModel.ObservableCollection<T>
System.Collections.ObjectModel.ReadOnlyCollection<T>
System.Collections.ObjectModel.ReadOnlyDictionary<TKey,T
Value>
System.Collections.ObjectModel.ReadOnlyObservableCollecti
on<T>
System.Collections.Queue
System.Collections.SortedList
System.Collections.Specialized.HybridDictionary
System.Collections.Specialized.ListDictionary
System.Collections.Specialized.OrderedDictionary
System.Collections.Specialized.StringCollection
System.Collections.Specialized.StringDictionary
System.Collections.Stack
System.ComponentModel.BindingList<T>
T IP O N OTA S
System.Data.DataSet
System.Data.PropertyCollection
System.Data.SqlTypes.SqlBoolean
System.Data.SqlTypes.SqlByte
System.Data.SqlTypes.SqlDateTime
System.Data.SqlTypes.SqlDouble
System.Data.SqlTypes.SqlGuid
System.Data.SqlTypes.SqlInt16
System.Data.SqlTypes.SqlInt32
System.Data.SqlTypes.SqlInt64
System.Data.SqlTypes.SqlString
T IP O N OTA S
System.DateTime
System.DateTimeOffset
System.Decimal
System.Double
System.Drawing.Color
System.Drawing.Point
System.Drawing.PointF
System.Drawing.Rectangle
System.Drawing.RectangleF
System.Drawing.Size
System.Drawing.SizeF
System.Enum
T IP O N OTA S
System.Exception
System.Globalization.CompareInfo
System.Globalization.SortVersion
System.Guid
System.Int16
System.Int32
T IP O N OTA S
System.Int64
System.IntPtr
System.Net.Cookie
System.Net.CookieCollection
System.Net.CookieContainer
System.Nullable<T>
System.Numerics.BigInteger
System.Numerics.Complex
System.Object
System.SByte
System.Single
System.String
System.StringComparer
System.Text.StringBuilder
System.TimeSpan
System.TimeZoneInfo.AdjustmentRule
T IP O N OTA S
System.TimeZoneInfo
System.Tuple
System.UInt16
System.UInt32
System.UInt64
System.UIntPtr
System.Uri
System.ValueType
System.Version
System.WeakReference<T>
T IP O N OTA S
System.WeakReference
Vea también
System.Runtime.Serialization
Contiene clases que se pueden usar para serializar y deserializar objetos.
Serialización SOAP y XML
Describe el mecanismo de la serialización XML que está incluido con Common Language Runtime.
Seguridad y serialización
Describe las instrucciones de la codificación seguras que hay que seguir al escribir el código que realiza la
serialización.
Comunicación remota de .NET
Describe los diversos métodos a partir de .NET Framework para las comunicaciones remotas.
Servicios web XML creados con ASP.NET y clientes de servicio web XML
Artículos en los que se describe y explica cómo programar servicios web XML creados con ASP.NET.
Guía de seguridad de BinaryFormatter
16/09/2020 • 11 minutes to read • Edit Online
Fondo
WARNING
El tipo BinaryFormatter es peligroso y no se recomienda para el procesamiento de datos. Las aplicaciones deben dejar de
usar BinaryFormatter lo antes posible, aunque crean que los datos que procesan son de confianza. BinaryFormatter
no es seguro y no se puede convertir en seguro.
BinaryFormatter se implementó antes de que las vulnerabilidades de deserialización fueran una categoría de
amenaza bien entendida. Como resultado, el código no sigue los procedimientos recomendados modernos. El
método Deserialize se puede usar como un vector para que los atacantes realicen ataques DoS contra
aplicaciones de consumo. Es posible que estos ataques hagan que la aplicación deje de responder o que se
produzca una terminación inesperada del proceso. Esta categoría de ataque no se puede mitigar con
SerializationBinder o con cualquier otro modificador de configuración BinaryFormatter . .NET considera que
este comportamiento es por definición y no emitirá una actualización de código para modificarlo.
BinaryFormatter.Deserialize puede ser vulnerable a otras categorías de ataque, como la divulgación de
información o la ejecución remota de código. El uso de características como un elemento SerializationBinder
personalizado puede ser insuficiente para mitigar estos riesgos de forma correcta. Existe la posibilidad de que se
detecte una vulnerabilidad nueva para la que .NET no pueda publicar de forma práctica una actualización de
seguridad. Los consumidores deben evaluar sus escenarios individuales y considerar su posible exposición a
estos riesgos.
Se recomienda que los consumidores de BinaryFormatter realicen valoraciones de riesgo individuales en sus
aplicaciones. Es responsabilidad exclusiva del consumidor determinar si se usa BinaryFormatter . Los
consumidores deben evaluar el riesgo de los requisitos de seguridad, técnicos, de reputación, legales y
normativos del uso de BinaryFormatter .
Alternativas preferidas
.NET ofrece varios serializadores integrados que pueden administrar de forma segura los datos que no son de
confianza:
XmlSerializer y DataContractSerializer para serializar gráficos de objetos en y desde XML. No se debe
confundir DataContractSerializer con NetDataContractSerializer.
BinaryReader y BinaryWriter para XML y JSON.
Las API de System.Text.Json para serializar gráficos de objetos en JSON.
Alternativas peligrosas
Evite los siguientes serializadores:
SoapFormatter
LosFormatter
NetDataContractSerializer
ObjectStateFormatter
Los serializadores anteriores realizan una deserialización polimórfica sin restricciones y son peligrosos, como
BinaryFormatter .
NOTE
En términos generales, la intención de la serialización es transmitir un objeto dentro o fuera de una aplicación. Un ejercicio
de modelado de amenazas casi siempre marca este tipo de transferencia de datos como el cruce de un límite de confianza.
Recursos adicionales
YSoSerial.Net para investigar cómo los adversarios atacan las aplicaciones que usan BinaryFormatter .
Información general sobre vulnerabilidades de deserialización:
OWASP Top 10: A8:2017 - Deserialización no segura
CWE-502: Deserialización de datos que no son de confianza
Conceptos de serialización
16/09/2020 • 6 minutes to read • Edit Online
¿Por qué desearía utilizar la serialización? Las dos razones más importantes son conservar así el estado de un
objeto a los medios de almacenamiento, de manera que una copia exacta se puede recrear en una copia intermedia
posterior y para enviar por valor el objeto de un dominio de aplicación a otro. Por ejemplo, la serialización se utiliza
para guardar el estado de sesión en ASP.NET y copiar los objetos en el Portapapeles en Windows Forms. La
comunicación remota se utiliza también para pasar por valor los objetos de un dominio de aplicación a otro.
WARNING
La serialización binaria puede ser peligrosa. Para obtener más información, consulte la Guía de seguridad BinaryFormatter.
Almacenamiento persistente
Es a menudo necesario almacenar el valor de los campos de un objeto en el disco y a continuación, más tarde,
recuperar estos datos. Aunque esto es fácil de lograr sin confiar en la serialización, este enfoque es a menudo
embarazoso y propenso a errores y se vuelve progresivamente más complejo al necesitar realizar el seguimiento
de una jerarquía de objetos. Imagine escribir una aplicación empresarial grande, que contiene miles de objetos, y
tener que escribir el código para guardar y restaurar los campos y propiedades a y desde el disco para cada objeto.
La serialización proporciona un mecanismo conveniente para lograr este objetivo.
Common Language Runtime administra cómo se almacenan los objetos en memoria y proporciona un mecanismo
de serialización automatizado mediante reflexión. Cuando se serializa un objeto, el nombre de la clase, el
ensamblado y todos los miembros de datos de la instancia de clase se escribe en el almacenamiento. Los objetos
almacenan a menudo referencias a otras instancias en variables miembro. Cuando se serializa la clase, las pistas de
motor de serialización hacen referencia a los objetos, ya serializados, para asegurarse de que no se serializa el
mismo objeto más de una vez. La arquitectura de serialización proporcionada correctamente con .NET Framework
controla de forma automática gráficos de objetos y referencias circulares. El único requisito de los gráficos de
objetos es que todos los objetos, a los que hace referencia el objeto serializado, también se deben marcar como
Serializable (para más información, vea Serialización básica). Si no se hace esto, se producirá una excepción
cuando el serializador intente serializar el objeto sin marca.
Cuando se deserializa la clase serializada, se vuelve a crear la clase y se restauran automáticamente los valores de
todos los miembros de datos.
Secciones relacionadas
Serialización binaria
Describe el mecanismo de la serialización binaria que está incluido con Common Language Runtime.
Comunicación remota de .NET
Describe los diversos métodos de comunicaciones disponibles en .NET Framework para las comunicaciones
remotas.
Serialización SOAP y XML
Describe el mecanismo de la serialización XML y SOAP que está incluido con Common Language Runtime.
Serialización básica
16/09/2020 • 4 minutes to read • Edit Online
WARNING
La serialización binaria puede ser peligrosa. Para obtener más información, consulte la Guía de seguridad BinaryFormatter.
La manera más sencilla de convertir una clase en serializable es marcarla con SerializableAttribute como se
muestra a continuación.
[Serializable]
public class MyObject {
public int n1 = 0;
public int n2 = 0;
public String str = null;
}
El ejemplo de código siguiente muestra cómo serializar una instancia de esta clase en un archivo.
Este ejemplo utiliza un formateador binario para hacer la serialización. Todo lo que necesita hacer es crear una
instancia del flujo y el formateador que piensa usar y luego llamar al método Serialize en el formateador. La
secuencia y el objeto para serializar se proporcionan como parámetros a esta llamada. Aunque no se muestra
explícitamente en este ejemplo, todas las variables miembro de una clase serán serializadas, incluso las variables
marcadas como privadas. En este aspecto, la serialización binaria difiere de la clase XmlSerializer, que solo serializa
campos públicos. Para más información sobre cómo excluir variables miembro de la serialización binaria, vea
Serialización selectiva.
Restaurar el objeto a su estado anterior es así de fácil. Primero, cree un flujo para leer y un Formatter y luego
indique al formateador que deserialice el objeto. El ejemplo de código siguiente muestra cómo se realiza la acción.
El BinaryFormatter usado arriba es muy eficaz y genera un flujo de bytes compacto. Todos los objetos serializados
con este formateador también se pueden deserializar con él, que lo convierte en una herramienta ideal para
serializar objetos que se deserializarán en .NET Framework. Es importante tener en cuenta que no se llama a los
constructores cuando se deserializa un objeto. Esta restricción se coloca en deserialización por las razones de
rendimiento. Sin embargo, esto infringe algunos de los contratos usuales hechos en tiempo de ejecución con la
escritura de objeto y los programadores debería asegurarse que entienden las ramificaciones al marcar un objeto
como serializable.
Si la portabilidad es un requisito, use SoapFormatter en su lugar. Simplemente reemplace Binar yFormatter en el
código anterior por SoapFormatter y llame a Serialize y Deserialize como antes. Este formateador genera el
resultado siguiente para obtener el ejemplo utilizado anteriormente.
<SOAP-ENV:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:a1="http://schemas.microsoft.com/clr/assem/ToFile">
<SOAP-ENV:Body>
<a1:MyObject id="ref-1">
<n1>1</n1>
<n2>24</n2>
<str id="ref-3">Some String</str>
</a1:MyObject>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Es importante tener en cuenta que el atributo Serializable no se puede heredar. Si deriva una nueva clase de
MyObject , la nueva clase también se debe marcar con el atributo o no se puede serializar. Por ejemplo, al intentar
serializar una instancia de la clase siguiente, se obtiene una SerializationException que informa de que el tipo
MyStuff no está marcado como serializable.
Es conveniente usar el atributo Serializable, aunque tiene limitaciones, como se ha mostrado anteriormente. Vea
Directrices de serialización para más información sobre cuándo se debe marcar una clase para la serialización. La
serialización no se puede agregar a una clase después de que se haya compilado.
Vea también
Serialización binaria
Serialización SOAP y XML
Serialización selectiva
16/09/2020 • 2 minutes to read • Edit Online
Una clase a menudo contiene campos que no se deberían serializar. Por ejemplo, suponga que una clase almacena
un Identificador de subproceso en una variable miembro. Al deserializar la clase, es posible que el subproceso que
ha almacenado el identificador al serializar la clase ya no se esté ejecutando; así, serializar este valor no tiene
sentido. Puede evitar que las variables miembro se serialicen si las marca con el atributo NonSerialized como se
indica a continuación.
[Serializable]
public class MyObject
{
public int n1;
[NonSerialized] public int n2;
public String str;
}
Si es posible, haz que un objeto pueda contener datos seguros no serializables. Si se debe serializar el objeto,
aplique el atributo NonSerialized a campos concretos que almacenen datos confidenciales. Si no excluye estos
campos de la serialización, sea consciente de que los datos que almacenan se exponen a cualquier código que
tenga permiso para serializar. Para más información sobre cómo escribir código de serialización seguro, vea
Seguridad y serialización.
WARNING
La serialización binaria puede ser peligrosa. Para obtener más información, consulte la Guía de seguridad BinaryFormatter.
Vea también
Serialización binaria
Serialización SOAP y XML
Seguridad y serialización
Serialización personalizada
16/09/2020 • 14 minutes to read • Edit Online
WARNING
La serialización binaria puede ser peligrosa. Para obtener más información, consulte la Guía de seguridad BinaryFormatter.
IMPORTANT
En versiones previas a .NET Framework 4.0, la serialización de datos de usuario personalizados de un ensamblado de
confianza parcial se realizaba mediante el método GetObjectData. A partir de la versión 4.0, ese método se marca con el
atributo de la clase SecurityCriticalAttribute que impide la ejecución en ensamblados de confianza parcial. Para solucionarlo,
implemente la interfaz ISafeSerializationData.
[Serializable]
public class MyObject : ISerializable
{
public int n1;
public int n2;
public String str;
public MyObject()
{
}
[Serializable]
public class ObjectTwo : MyObject
{
public int num;
public ObjectTwo()
: base()
{
}
End Sub
No se olvide de llamar a la clase base en el constructor de deserialización. Si no se hace esto, nunca se llama al
constructor en la clase base y el objeto no se construye totalmente después de la deserialización.
Los objetos se reconstruyen al revés; y llamar a los métodos durante la deserialización puede tener efectos
secundarios indeseables, porque los métodos llamados podrían hacer referencia a las referencias que no se han
deserializado cuando se realiza la llamada. Si la clase deserializada implementa IDeserializationCallback, se llama al
método OnDeserialization automáticamente cuando se ha deserializado el gráfico de objetos completo. Se han
restaurado todos los objetos secundarios hechos referencia totalmente en este punto. Una tabla hash es un
ejemplo típico de una clase que es difícil de deserializar sin utilizar el agente de escucha de evento. Es fácil de
recuperar los pares de valor y clave durante la deserialización, pero volver a agregar estos objetos a la tabla hash
puede producir los problemas, porque no hay ninguna garantía de que se hayan deserializado las clases que
derivaron de la tabla hash. Llamar a los métodos en una tabla hash en esta copia intermedia no es, por
consiguiente, aconsejable.
Vea también
Serialización binaria
Serialización SOAP y XML
Seguridad y serialización
Pasos del proceso de serialización
16/09/2020 • 2 minutes to read • Edit Online
Cuando se llama al método Serialize en un formateador, la serialización de objetos procede según la secuencia
siguiente de reglas:
Una comprobación se realiza para determinar si el formateador tiene un selector de suplente. Si el
formateador lo tiene, compruebe si el selector de suplente administra objetos del tipo determinado. Si el
seleccionador administra el tipo de objeto, se llama a ISerializable.GetObjectData en el selector de suplente.
Si no hay ningún selector de suplente o si no administra el tipo de objeto, se realiza una comprobación para
determinar si el objeto está marcado con el atributo Serializable. Si el objeto no está marcado, se genera
SerializationException.
Si el objeto está marcado correctamente, compruebe si el objeto implementa la interfaz ISerializable. Si es
así, se llama a GetObjectData en el objeto.
Si el objeto no implementa ISerializable, se usa la directiva de la serialización predeterminada, serializando
todos los campos no marcados como NonSerialized.
WARNING
La serialización binaria puede ser peligrosa. Para obtener más información, consulte la Guía de seguridad BinaryFormatter.
Vea también
Serialización binaria
Serialización SOAP y XML
Serialización tolerante a versiones
16/09/2020 • 12 minutes to read • Edit Online
En la versión 1.0 y 1.1 de .NET Framework, la creación de tipos serializables que serían reutilizables a partir de una
versión de una aplicación a lo siguiente, ha resultado problemática. Si un tipo se modificara agregando los campos
adicionales, se producirían los problemas siguientes:
Las versiones anteriores de una aplicación produciría excepciones en caso de solicitar la deserialización de las
nuevas versiones del tipo anterior.
Las versiones más recientes de una aplicación producirían las excepciones al deserializar versiones anteriores de
un tipo con datos que faltan.
La Serialización Tolerante a versiones (VTS) es un conjunto de características introducido en .NET Framework 2.0
que facilita, con el tiempo, modificar los tipos serializables. Específicamente, las características VTS están habilitadas
para las clases a las que se ha aplicado el atributo SerializableAttribute, incluidos los tipos genéricos. VTS posibilita
el agregar los nuevos campos a esas clases sin interrumpir la compatibilidad con otras versiones del tipo. Para
obtener una aplicación de ejemplo en funcionamiento, vea Ejemplo de tecnología de serialización tolerante a
versiones.
Las características VTS están habilitadas al utilizar BinaryFormatter. Además, todas las características, excepto la
tolerancia de datos extraños, están habilitadas también al utilizar SoapFormatter. Para más información sobre el
uso de estas clases para la serialización, vea Serialización binaria.
WARNING
La serialización binaria puede ser peligrosa. Para obtener más información, consulte la Guía de seguridad BinaryFormatter.
Lista de características
En la lista de características se incluyen las siguientes:
Tolerancia de datos extraños o inesperados. Esto permite a las versiones más recientes del tipo enviar los datos a
las versiones anteriores.
Tolerancia de datos opcionales que faltan. Esto permite a las versiones anteriores enviar los datos a las versiones
más recientes.
Devoluciones de llamada de la serialización. Esto habilita el valor predeterminado inteligente en casos donde
faltan datos.
Hay además, una característica para declarar cuando se ha agregado un nuevo campo opcional. Ésta es la
propiedad VersionAdded del atributo OptionalFieldAttribute.
Estas opciones se tratan con más detalle en las secciones siguientes.
Tolerancia de datos extraños o inesperados
En el pasado, durante la deserialización, cualquier dato extraño o inesperado produjeron excepciones. Con VTS, en
la misma situación, cualquier dato extraño o inesperado se omite en lugar de producir excepciones. Esto habilita
aplicaciones que utilizan versiones más recientes de un tipo (es decir, una versión que incluye más campos) para
enviar información a las aplicaciones que esperan versiones anteriores del mismo tipo.
En el ejemplo siguiente, los datos adicionales contenidos en CountryField de la versión 2.0 de la clase Address se
omiten cuando una aplicación anterior deserializa la versión más reciente.
// Version 1 of the Address class.
[Serializable]
public class Address
{
public string Street;
public string City;
}
// Version 2.0 of the Address class.
[Serializable]
public class Address
{
public string Street;
public string City;
// The older application ignores this data.
public string CountryField;
}
[Serializable]
public class Address
{
public string Street;
public string City;
[OptionalField]
public string CountryField;
}
<Serializable> _
Public Class Address
Public Street As String
Public City As String
<OptionalField> _
Public CountryField As String
End Class
C UA N DO SE L L A M A A L M ÉTO DO
AT RIB UTO A SO C IA DO. USO T ÍP IC O
* Esta devolución de llamada se invoca antes del constructor de deserialización, si hay alguno.
Empleo de devoluciones de llamada
Para utilizar las devoluciones de llamada, aplique el atributo adecuado a un método que acepta un parámetro
StreamingContext. Solo se puede marcar un método por clase con cada uno de estos atributos. Por ejemplo:
[OnDeserializing]
private void SetCountryRegionDefault(StreamingContext sc)
{
CountryField = "Japan";
}
<OnDeserializing>
Private Sub SetCountryRegionDefault(sc As StreamingContext)
CountryField = "Japan"
End Sub
El uso previsto de estos métodos es para las versiones. Durante la deserialización, un campo opcional no se puede
inicializar correctamente si faltan los datos para el campo. Esto se puede corregir si se crea el método que asigna el
valor correcto y luego se aplica el atributo OnDeserializingAttribute u OnDeserializedAttribute al método.
El ejemplo siguiente muestra el método en el contexto de un tipo. Si una versión anterior de una aplicación envía
una instancia de la clase Address a una versión posterior de la aplicación, faltarán los datos de campo
CountryField . Pero después de la deserialización, el campo se establecerá en el valor predeterminado "Japan".
[Serializable]
public class Address
{
public string Street;
public string City;
[OptionalField]
public string CountryField;
[OnDeserializing]
private void SetCountryRegionDefault(StreamingContext sc)
{
CountryField = "Japan";
}
}
<Serializable> _
Public Class Address
Public Street As String
Public City As String
<OptionalField> _
Public CountryField As String
<OnDeserializing> _
Private Sub SetCountryRegionDefault(sc As StreamingContext)
CountryField = "Japan"
End Sub
End Class
Propiedad VersionAdded
OptionalFieldAttribute tiene la propiedad VersionAdded . En la versión 2.0 de .NET Framework no se usa. Pero
es importante establecer correctamente esta propiedad para asegurarse de que el tipo sea compatible con los
motores de serialización futuros.
La propiedad indica qué versión de un tipo de un campo determinado se ha agregado. Se debería incrementar en
uno exactamente (comenzando en 2) cada vez que se modifica el tipo, como se muestra en el ejemplo siguiente:
// Version 1.0
[Serializable]
public class Person
{
public string FullName;
}
// Version 2.0
[Serializable]
public class Person
{
public string FullName;
[OptionalField(VersionAdded = 2)]
public string NickName;
[OptionalField(VersionAdded = 2)]
public DateTime BirthDate;
}
// Version 3.0
[Serializable]
public class Person
{
public string FullName;
[OptionalField(VersionAdded=2)]
public string NickName;
[OptionalField(VersionAdded=2)]
public DateTime BirthDate;
[OptionalField(VersionAdded=3)]
public int Weight;
}
<OptionalField(VersionAdded := 2)> _
Public NickName As String
<OptionalField(VersionAdded := 2)> _
Public BirthDate As DateTime
End Class
<OptionalField(VersionAdded := 2)> _
Public NickName As String
<OptionalField(VersionAdded := 2)> _
Public BirthDate As DateTime
<OptionalField(VersionAdded := 3)> _
Public Weight As Integer
End Class
SerializationBinder
Algunos usuarios pueden necesitar controlar qué clase serializar y deserializar porque se necesita una versión
diferente de la clase en el servidor y el cliente. SerializationBinder es una clase abstracta usada para controlar los
tipos reales empleados durante la serialización y la deserialización. Para usar esta clase, obtenga una clase de
SerializationBinder y omita los métodos BindToName y BindToType. Para obtener más información, vea Control de
la serialización y la deserialización con SerializationBinder.
Procedimientos recomendados
Para asegurar el comportamiento apropiado de la versión, siga estas reglas al modificar un tipo de la versión para
la versión:
Nunca quite un campo serializado.
Nunca aplique el atributo NonSerializedAttribute a un campo si el atributo no se aplicó al campo en la versión
anterior.
Nunca cambie el nombre o el tipo de un campo serializado.
Al agregar un nuevo campo serializado, aplique el atributo OptionalFieldAttribute .
Al quitar un atributo NonSerializedAttribute de un campo (que no era serializable en una versión anterior),
aplique el atributo OptionalFieldAttribute .
Para todos los campos opcionales, establezca valores predeterminados significativos mediante las devoluciones
de llamada de serialización, a menos que se acepten 0 o null como valores predeterminados.
Para asegurarse de que un tipo será compatible con motores de serialización futuros, siga estas instrucciones:
Establezca siempre correctamente la propiedad VersionAdded en el atributo OptionalFieldAttribute .
Evite la versión bifurcada.
Vea también
SerializableAttribute
BinaryFormatter
SoapFormatter
VersionAdded
OptionalFieldAttribute
OnDeserializingAttribute
OnDeserializedAttribute
OnSerializingAttribute
OnSerializedAttribute
StreamingContext
NonSerializedAttribute
Serialización binaria
Directrices de serialización
16/09/2020 • 19 minutes to read • Edit Online
Este documento enumera las instrucciones que se deben tener en cuenta al diseñar una API para su serialización.
WARNING
La serialización binaria puede ser peligrosa. Para obtener más información, consulte la Guía de seguridad BinaryFormatter.
.NET proporciona tres tecnologías de serialización principales que están optimizadas para varios escenarios de
serialización. En la siguiente tabla se enumeran estas tecnologías y los principales tipos de .NET relacionados con
ellas.
DataContractSerializer JSON
NetDataContractSerializer
DataContractJsonSerializer
ISerializable
BinaryFormatter
SoapFormatter
Al diseñar tipos nuevos, debería decidir cuales de estas tecnologías deben admitir esos tipos (en caso de que haya
alguna). Las siguientes instrucciones describen cómo tomar una decisión y cómo proporcionar dicha
compatibilidad. Estas instrucciones no están pensadas para ayudarle a elegir qué tecnología de serialización
debería utilizar en la implementación de su aplicación o biblioteca. Tales instrucciones no están relacionadas
directamente con el diseño de la API y, por lo tanto, no forman parte de este tema.
Instrucciones
Piense en la serialización al diseñar los nuevos tipos.
La serialización es un factor importante en el diseño de cualquier tipo, es posible que los programas
necesiten conservar o transmitir instancias del tipo.
Elegir la tecnología de serialización correcta que se va a admitir
Un tipo dado puede admitir una o varias tecnologías de serialización, o ninguna.
CONSIDERE admitir la serialización de contrato de datos si puede que las instancias de su tipo deban
conservarse o usarse en Servicios Web.
CONSIDERE la posibilidad de admitir la serialización XML en lugar de —o además de— la serialización de
contrato de datos si necesita más control sobre el formato XML que se genera cuando se serializa el tipo.
Esto puede ser necesario en algunos escenarios de interoperabilidad donde es necesario utilizar una
construcción XML no admitida por la serialización de contrato de datos; por ejemplo, para generar atributos
XML.
CONSIDERE la posibilidad de admitir la serialización en tiempo de ejecución si las instancias de su tipo
necesitan traspasar los límites de .NET Remoting.
EVITE admitir la serialización en tiempo de ejecución o la serialización XML solo por motivos generales de
persistencia. Preferencia por la serialización de contrato de datos
Admitir la serialización de contrato de datos
Los tipos pueden admitir la serialización de contrato de datos mediante la aplicación de DataContractAttribute al
tipo y DataMemberAttribute a los miembros (campos y propiedades) del tipo.
[DataContract]
class Person
{
[DataMember]
string LastName { get; set; }
[DataMember]
string FirstName { get; set; }
End Class
1. CONSIDERE la posibilidad de marcar como públicos los miembros de datos del tipo si el tipo se puede
utilizar con confianza parcial. Con plena confianza, los serializadores de contrato de datos pueden serializar
y deserializar tipos y miembros no públicos, pero solo los miembros públicos se pueden serializar y
deserializar con confianza parcial.
2. Implemente getter y setter en todas las propiedades que tienen Data-MemberAttribute. Los serializadores
de contrato de datos requieren getter y setter para que el tipo se considere serializable. Si el tipo no se va a
usar con confianza parcial, uno o los dos descriptores de acceso de propiedad pueden ser no públicos.
[DataContract]
class Person2
{
string lastName;
string firstName;
[DataMember]
public string LastName
{
// Implement get and set.
get { return lastName; }
private set { lastName = value; }
}
[DataMember]
public string FirstName
{
// Implement get and set.
get { return firstName; }
private set { firstName = value; }
}
}
End Property
End Get
Set(ByVal value As String)
firstNameValue = value
End Set
End Property
End Class
[DataContract]
class Person3
{
[DataMember]
string lastName;
[DataMember]
string firstName;
string fullName;
<DataContract()> _
Class Person3
<DataMember()> Private lastNameValue As String
<DataMember()> Private firstNameValue As String
Dim fullNameValue As String
[DataMember]
string fullNameValue;
[DataMember]
Address address; // Address is abstract
[DataContract]
public abstract class Address
{
public abstract string FullAddress { get; }
}
[DataContract]
public class USAddress : Address
{
[DataMember]
public string Street { get; set; }
[DataMember]
public string City { get; set; }
[DataMember]
public string State { get; set; }
[DataMember]
public string ZipCode { get; set; }
End Property
End Class
Cuando no se conoce la lista de tipos conocidos estáticamente (cuando se compila la clase Person ),
KnownTypeAttribute también puede apuntar a un método que devuelve una lista de tipos conocidos en
tiempo de ejecución.
5. Tenga en cuenta la compatibilidad con versiones anteriores y posteriores al crear o cambiar tipos
serializables.
Tenga presente que las secuencias serializadas de futuras versiones del tipo se puedan deserializar en la
versión actual del tipo y viceversa. Asegúrese de que entiende que los miembros de datos, incluso los
privados e internos, no pueden sufrir cambios en sus nombres, tipos, o incluso su orden, en versiones
futuras del tipo a menos que se tomen precauciones para conservar el contrato mediante el uso de
parámetros explícitos para los atributos del contrato de datos. Pruebe la compatibilidad de la serialización
cuando realice cambios en los tipos serializables. Pruebe a deserializar la nueva versión en una versión
anterior, y viceversa.
6. CONSIDERE la posibilidad de implementar la interfaz IExtensibleDataObject para permitir los viajes de ida y
vuelta entre distintas versiones del tipo.
La interfaz permite al serializador asegurarse de que no se pierden datos durante los viajes de ida y vuelta.
La propiedad ExtensionData almacena cualquier dato de la versión futura del tipo que es desconocido para
la versión actual. Cuando la versión actual se serialice y deserialice en una versión futura, los datos
adicionales estarán disponibles en la secuencia serializada a través del valor de propiedad ExtensionData .
// Implement the IExtensibleDataObject interface.
[DataContract]
class Person5 : IExtensibleDataObject
{
ExtensionDataObject serializationData;
[DataMember]
string fullNameValue;
ExtensionDataObject IExtensibleDataObject.ExtensionData
{
get
{
return serializationData;
}
set { serializationData = value; }
}
}
Para obtener más información, vea Forward-Compatible Data Contracts (Contratos de datos compatibles
con el reenvío).
Admitir la serialización XML
La serialización de contrato de datos es la tecnología de serialización principal (predeterminada) en .NET
Framework, pero hay escenarios de serialización que la serialización de contrato de datos no admite. Por ejemplo,
no proporciona control total sobre la forma del XML generado o utilizado por el serializador. Si se requiere un
control tan detallado, debe usarse la serialización XML y los tipos se deben diseñar para admitir esta tecnología de
serialización.
1. EVITE diseñar sus tipos concretamente para la serialización XML, a menos que tenga una razón de peso para
controlar la forma del XML generado. Esta tecnología de serialización ha sido reemplazada por la
serialización de contrato de datos que se describió en la sección anterior.
En otras palabras, no aplique atributos del espacio de nombres System.Xml.Serialization a los nuevos tipos,
a menos que sepa que el tipo se utilizará con la serialización XML. El siguiente ejemplo muestra cómo se
puede usar System.Xml.Serialization para controlar la forma del XML generado.
2. CONSIDERE la implementación del patrón serializable en tiempo de ejecución si desea un control completo
sobre el proceso de serialización. Por ejemplo, si desea transformar los datos cuando se serializan o
deserializan.
El patrón es muy simple. Todo lo que necesita hacer es implementar la interfaz ISerializable y proporcionar
un constructor especial que se utiliza cuando se deserializa el objeto.
public Person_Runtime_Serializable() { }
protected Person_Runtime_Serializable(SerializationInfo info, StreamingContext context){
if (info == null) throw new System.ArgumentNullException("info");
fullName = (string)info.GetValue("name", typeof(string));
}
[SecurityPermission(SecurityAction.LinkDemand,
Flags = SecurityPermissionFlag.SerializationFormatter)]
void ISerializable.GetObjectData(SerializationInfo info,
StreamingContext context) {
if (info == null) throw new System.ArgumentNullException("info");
info.AddValue("name", fullName);
}
Get
Return fullNameValue
End Get
Set(ByVal value As String)
fullNameValue = value
End Set
End Property
End Class
3. Proteja el constructor de serialización y proporcione dos parámetros con el tipo y el nombre que se indican
en el ejemplo que se muestra aquí.
[SecurityPermission(SecurityAction.LinkDemand,
Flags = SecurityPermissionFlag.SerializationFormatter)]
Vea también
Utilización de contratos de datos
Serializador de contratos de datos
Tipos admitidos por el serializador de contratos de datos
Serialización binaria
.NET Remoting
Serialización SOAP y XML
Seguridad y serialización
Cómo: Fragmentar datos serializados
16/09/2020 • 6 minutes to read • Edit Online
WARNING
La serialización binaria puede ser peligrosa. Para obtener más información, consulte la Guía de seguridad BinaryFormatter.
Dos problemas que se producen al enviar los conjuntos de datos grandes en mensajes del Servicio Web son:
1. Un espacio de trabajo grande (memoria) debido al almacenamiento en búfer por el motor de la serialización.
2. Consumo de ancho de banda inmoderado debido a 33 por ciento inflación después de la codificación de
Base64.
Para resolver estos problemas, implemente la interfaz IXmlSerializable para controlar la serialización y
deserialización. Específicamente, implemente WriteXml y los métodos ReadXml al fragmento los datos.
Para implementar la fragmentación del lado del servidor
1. En el equipo del servidor, el método web se debe apagar del almacenado en búfer de ASP.NET y devolver un
tipo que implementa IXmlSerializable.
2. El tipo que implementa IXmlSerializable fragmenta los datos en el método WriteXml.
Para implementar el procesamiento del lado cliente
1. Modifique el método Web en el proxy de cliente para devolver el tipo que implementa IXmlSerializable.
Puede usar una SchemaImporterExtension para realizar esta acción automáticamente, pero no se muestra
aquí.
2. Implemente el método ReadXml para leer el flujo de datos fragmentado y escribir los bytes en el disco. Esta
implementación también genera eventos de progreso que pueden ser utilizados por un control gráfico,
como una barra de progreso.
Ejemplo
El ejemplo de código siguiente muestra el método Web en el cliente que desactiva el almacenado en búfer de
ASP.NET. También muestra la implementación del lado cliente de la interfaz IXmlSerializable que fragmenta los
datos en el método WriteXml.
[WebMethod]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute
(ParameterStyle= SoapParameterStyle.Bare)]
public SongStream DownloadSong(DownloadAuthorization Authorization, string filePath)
{
End Function
End Class
[XmlSchemaProvider("MySchema")]
public class SongStream : IXmlSerializable
{
public SongStream(){ }
writer.Flush();
readLen = sr.Read(songBytes, 0, bufferSize);
}
writer.WriteEndElement();
inFile.Close();
}
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
{
throw new System.NotImplementedException();
}
<XmlSchemaProvider("MySchema")> _
Public Class SongStream
Implements IXmlSerializable
End Sub
' This is the method named by the XmlSchemaProviderAttribute applied to the type.
Public Shared Function MySchema(ByVal xs As XmlSchemaSet) As XmlQualifiedName
' This method is called by the framework to get the schema for this type.
' We return an existing schema from disk.
Dim schemaSerializer As New XmlSerializer(GetType(XmlSchema))
Dim xsdPath As String = Nothing
' NOTE: replace SongStream.xsd with your own schema file.
xsdPath = System.Web.HttpContext.Current.Server.MapPath("SongStream.xsd")
Dim s As XmlSchema = CType(schemaSerializer.Deserialize(New XmlTextReader(xsdPath)), XmlSchema)
xs.XmlResolver = New XmlUrlResolver()
xs.Add(s)
End Function
writer.Flush()
readLen = sr.Read(songBytes, 0, bufferSize)
End While
writer.WriteEndElement()
inFile.Close()
End Sub
public SongFile()
{ }
string songBase64;
byte[] songBytes;
reader.ReadStartElement("song", ns);
double totalRead=0;
while(true)
{
if (reader.IsStartElement("chunk", ns))
{
songBase64 = reader.ReadElementString();
totalRead += songBase64.Length;
songBytes = Convert.FromBase64String(songBase64);
outFile.Write(songBytes, 0, songBytes.Length);
outFile.Flush();
if (OnProgress != null)
{
OnProgress(100 * (totalRead / size));
}
}
else
{
break;
}
}
outFile.Close();
reader.ReadEndElement();
}
[PermissionSet(SecurityAction.Demand, Name="FullTrust")]
public void Play()
{
System.Diagnostics.Process.Start(this.filePath);
}
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
{
throw new System.NotImplementedException();
}
End Sub
End Sub
End Sub
outFile.Close()
reader.ReadEndElement()
End Sub
<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _
Public Sub Play()
System.Diagnostics.Process.Start(Me.filePath)
End Sub
Vea también
Serialización personalizada
Procedimiento para determinar si un objeto de
.NET Standard es serializable
16/09/2020 • 3 minutes to read • Edit Online
.NET Standard es una especificación que define los tipos y miembros que debe haber en las implementaciones de
.NET específicas que se ajusten a esa versión del estándar. Pero .NET Standard no define si un tipo es serializable.
Los tipos definidos en la biblioteca de .NET Standard no están marcados con el atributo SerializableAttribute. En su
lugar, son las implementaciones de .NET específicas, como .NET Framework y .NET Core, las que deciden libremente
si un tipo determinado es serializable.
Si ha desarrollado una biblioteca que tiene como destino .NET Standard, cualquier implementación de .NET que
admita .NET Standard podrá usar esa biblioteca. Esto significa que no se puede saber con antelación si un tipo
determinado es serializable; solo se sabrá en tiempo de ejecución.
Para determinar si un objeto es serializable en tiempo de ejecución, hay que recuperar el valor de la propiedad
IsSerializable de un objeto Type que representa el tipo de ese objeto. En el siguiente ejemplo se proporciona una
implementación. En él se define un método de extensión IsSerializable(Object) que indica si cualquier instancia
de Object se puede serializar.
namespace Libraries
{
using System;
Type t = obj.GetType();
return t.IsSerializable;
}
}
}
Imports System.Runtime.CompilerServices
Namespace Global.Libraries
Después, se puede pasar cualquier objeto al método para determinar si se puede serializar y deserializar en la
implementación actual de .NET, como se muestra en el ejemplo siguiente:
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using Libraries;
namespace test_serialization
{
class Program
{
static void Main()
{
var value = ValueTuple.Create("03244562", DateTime.Now, 13452.50m);
if (value.IsSerializable())
{
// Serialize the value tuple.
var formatter = new BinaryFormatter();
using (var stream = new FileStream("data.bin", FileMode.Create,
FileAccess.Write, FileShare.None))
{
formatter.Serialize(stream, value);
}
// Deserialize the value tuple.
using (var readStream = new FileStream("data.bin", FileMode.Open))
{
object restoredValue = formatter.Deserialize(readStream);
Console.WriteLine($"{restoredValue.GetType().Name}: {restoredValue}");
}
}
else
{
Console.WriteLine($"{nameof(value)} is not serializable");
}
}
}
}
// The example displays output like the following:
// ValueTuple`3: (03244562, 10/18/2017 5:25:22 PM, 13452.50)
Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary
Imports Libraries
Module Program
Sub Main()
Dim value = ValueTuple.Create("03244562", DateTime.Now, 13452.50d)
If value.IsSerializable() Then
Dim formatter As New BinaryFormatter()
' Serialize the value tuple.
Using stream As New FileStream("data.bin", FileMode.Create,
FileAccess.Write, FileShare.None)
formatter.Serialize(stream, value)
End Using
' Deserialize the value tuple.
Using readStream As New FileStream("data.bin", FileMode.Open)
Dim restoredValue = formatter.Deserialize(readStream)
Console.WriteLine($"{restoredValue.GetType().Name}: {restoredValue}")
End Using
Else
Console.WriteLine($"{nameof(value)} is not serializable")
End If
End Sub
End Module
' The example displays output like the following:
' ValueTuple`3: (03244562, 10/18/2017 5:25:22 PM, 13452.50)
Vea también
Serialización binaria
System.SerializableAttribute
Type.IsSerializable
Ejemplo de tecnología de serialización básica
16/09/2020 • 5 minutes to read • Edit Online
Descargar ejemplo
En este ejemplo se demuestra la capacidad de Common Language Runtime para serializar un gráfico de objetos de
la memoria en una secuencia. En este ejemplo se puede utilizar SoapFormatter o BinaryFormatter para la
serialización. Se serializa o deserializa una lista vinculada, llena de datos, desde o hacia una secuencia del archivo.
En cualquier caso, se muestra la lista para que pueda ver los resultados. La lista vinculada es de tipo LinkedList ,
un tipo definido por este ejemplo.
Para obtener más información sobre la serialización, revise los comentarios de los archivos de código fuente y de
build.proj.
Para generar el ejemplo desde el símbolo del sistema
1. Navegue hasta uno de los subdirectorios específicos de lenguaje bajo el directorio
Technologies\Serialization\Runtime Serialization\Basic, utilizando el símbolo del sistema.
2. Escriba msbuild SerializationCS.sln , msbuild SerializationJSL.sln o msbuild SerializationVB.sln ,
según el lenguaje de programación que prefiera, en la línea de comandos.
Para compilar el ejemplo con Visual Studio
1. Abra el Explorador de archivos y navegue hasta uno de los subdirectorios específicos del lenguaje del
ejemplo.
2. Haga doble clic en el icono del archivo SerializationCS.sln, SerializationJSL.sln o SerializationVB.sln,
dependiendo del lenguaje de programación elegido, para abrir el archivo en Visual Studio.
3. En el menú Compilar , seleccione Compilar solución .
La aplicación de ejemplo se generará en el subdirectorio predeterminado \bin o \bin\Debug.
Para ejecutar el ejemplo
1. Navegue hasta el directorio que contiene el ejecutable generado.
2. Escriba Serialization.exe , junto con los valores de parámetro que quiera, en la línea de comandos.
NOTE
En este ejemplo se genera una aplicación de consola. Para poder ver el resultado, debe iniciarla desde la línea de comandos.
Comentarios
La aplicación de ejemplo acepta parámetros de línea de comandos que indican qué comprobación desea ejecutar.
Para serializar una lista de 10 nodos en un archivo denominado Test.xml mediante el formateador SOAP, use los
parámetros sx Test.xml 10 .
Por ejemplo:
Para deserializar el archivo Test.xml del ejemplo anterior, use los parámetros dx Test.xml .
Por ejemplo:
En los dos ejemplos anteriores, la "x" en el modificador de la línea de comandos indica que desea realizar una
serialización de XML SOAP. Puede utilizar "b" en su lugar para utilizar la serialización binaria. Si desea realizar la
serialización con un gran número de nodos, puede que prefiera redirigir el resultado de la consola a un archivo.
Por ejemplo:
Las viñetas siguientes describen brevemente las clases y las tecnologías que se utilizan en este ejemplo.
Serialización en tiempo de ejecución
IFormatter Se usa para hacer referencia a un objeto BinaryFormatter o SoapFormatter.
BinaryFormatter Se usa para serializar una lista vinculada a una secuencia en formato binario. El
formateador binario utiliza un formato que solo entiende el tipo BinaryFormatter. Sin embargo, los
datos son concisos.
SoapFormatter Se usa para serializar una lista vinculada a una secuencia en formato SOAP. SOAP es
un formato estándar.
E/S de secuencia
Stream Se utiliza para serializar y deserializar. El tipo de secuencia concreto utilizado en este ejemplo
es el tipo FileStream. Sin embargo, la serialización se puede utilizar con cualquier tipo derivado de
Stream.
File Se utiliza para crear objetos FileStream para leer y crear archivos en disco.
FileStream Se utiliza para serializar y deserializar las listas vinculadas.
Vea también
System.IO
File
FileStream
Stream
Random
System.Runtime.Serialization
BinaryFormatter
SoapFormatter
IFormatter
SerializableAttribute
System.Xml.Serialization
Serialización básica
Serialización binaria
Controlar la serialización XML mediante atributos
Introducción a la serialización XML
Serialización
Serialización SOAP y XML
Serialización de SOAP y XML
16/09/2020 • 2 minutes to read • Edit Online
La serialización XML convierte (serializa) las propiedades y campos públicos de un objeto y los parámetros y
valores devueltos de los métodos en una secuencia XML que se ajusta a un documento específico del lenguaje
de definición de esquemas XML (XSD). La serialización XML produce clases fuertemente tipadas cuyas
propiedades y campos se convierten en un formato en serie (en este caso, a XML) para almacenaje y
transporte.
Dado que XML es un estándar abierto, cualquier aplicación, según sea necesario, puede procesar la secuencia
XML sin tener en cuenta la plataforma. Por ejemplo, los servicios Web XML creados utilizando el ASP.NET
utilizan la clase XmlSerializer para crear secuencias XML que pasan los datos entre las aplicaciones de servicio
Web XML a lo largo de Internet o en intranets. A la inversa, la deserialización toma este tipo de secuencia XML y
reconstruye el objeto.
La serialización XML también se puede usar para serializar objetos en secuencias XML que se ajustan a la
especificación SOAP. SOAP es un protocolo basado en XML, diseñado específicamente para transportar
llamadas a procedimiento utilizando XML.
Para serializar o deserializar objetos utilice la clase XmlSerializer. Para crear las clases que se van a serializar,
utilice la herramienta XML Schema Definition.
Vea también
Serialización binaria
Servicios Web XML creados con ASP.NET y clientes de servicio Web XML
serialización XML
16/09/2020 • 21 minutes to read • Edit Online
La serialización es el proceso de convertir un objeto a un formulario que pueda transportarse fácilmente. Por
ejemplo, puede serializar un objeto y transportarlo a través de Internet utilizando http entre un cliente y un
servidor. El otro fin para el cual sirve es que la deserialización reconstruye el objeto desde la secuencia.
La serialización XML serializa solo los campos públicos y los valores de propiedad de un objeto en una secuencia
XML. La serialización XML no incluye información de tipo. Por ejemplo, si tiene un objeto Book que existe en el
espacio de nombres Librar y , no hay garantía de que se deserialice en un objeto del mismo tipo.
NOTE
La serialización XML no convierte los métodos, indizadores, campos privados ni propiedades (excepto las colecciones de
solo lectura). Para serializar todos los campos y propiedades, tanto públicos como privados, de un objeto, utilice el
DataContractSerializer en vez de la serialización XML.
La clase central en serialización XML es la clase XmlSerializer, y los métodos más importantes de esta clase son
Serialize y Deserialize . XmlSerializer crea los archivos C# y los compila en archivos .dll para realizar esta
serialización. En .NET Framework 2.0, la herramienta Generador de serializador XML (Sgen.exe) está diseñada
para generar de antemano estos ensamblados de serialización para implementarlos con la aplicación y mejorar el
rendimiento de inicio. La secuencia XML generada por XmlSerializer es conforme con la recomendación del
lenguaje de definición de esquema XML (XSD) 1.0 de World Wide Web Consortium (W3C). Además, los tipos de
datos generados son conformes al documento titulado "Esquema XML parte 2: Tipos de datos."
Los datos de los objetos se describen mediante construcciones de lenguaje de programación, como clases,
campos, propiedades, tipos primitivos, matrices e incluso XML insertado en forma de objetos XmlElement o
XmlAttribute . Existe la opción de crear clases propias, anotadas con atributos, o de utilizar la herramienta de
definición de esquema XML para generar las clases de acuerdo con un documento de esquema XML existente.
Si se dispone de un esquema XML, es posible ejecutar la herramienta de definición de esquema XML con el fin de
generar un conjunto de clases fuertemente tipadas para el esquema y que se anoten con atributos. Cuando se
serializa una instancia de este tipo de clase, el XML generado se adhiere al Esquema XML. Contando con este tipo
de clase, se puede programar con un modelo de objetos manipulados con facilidad estando asegurado que el
XML generado cumple el esquema XML. Esta es una alternativa al uso de otras clases en .NET Framework, como
las clases XmlReader y XmlWriter , para analizar y escribir una secuencia XML. Para obtener más información,
vea XML Documents and Data (Documentos y datos XML). Estas clases le permiten analizar cualquier secuencia
XML. En contraste, use XmlSerializer cuando se espera que la secuencia XML cumpla con un esquema XML
conocido.
Los atributos controlan la secuencia XML generada por la clase XmlSerializer , lo que le permite establecer el
espacio de nombres XML, el nombre de elemento, el nombre de atributo, etc., de la secuencia XML. Para obtener
más información sobre estos atributos y la forma en que controlan la serialización XML, vea Controlling XML
Serialization Using Attributes (Controlar la serialización XML mediante atributos). Para consultar una tabla de los
atributos que se usan para controlar el XML generado, vea Attributes That Control XML Serialization (Atributos
que controlan la serialización XML).
La clase XmlSerializer puede serializar adicionalmente un objeto y generar una secuencia XML SOAP codificada.
El XML generado cumple las normas de la sección 5 del documento de World Wide Web Consortium con título
"Protocolo simple de acceso a objetos (SOAP) 1.1" (SOAP). Para más información sobre este proceso, vea
Procedimiento para serializar un objeto como secuencia XML con codificación SOAP. Para consultar una tabla de
los atributos que controlan el XML generado, vea Attributes That Control Encoded SOAP Serialization (Atributos
que controlan la serialización SOAP codificada).
La clase XmlSerializer genera los mensajes SOAP creados por servicios Web XML y pasados a estos. Para
controlar los mensajes SOAP, puede aplicar los atributos a las clases, los valores devueltos, parámetros y campos
situados en un archivo de servicio Web XML (.asmx). Puede utilizar ambos, los atributos mostrados en "atributos
que controlan la serialización XML" y los “atributos que controlan la serialización de SOAP codificada” porque un
servicio Web XML puede utilizar o el estilo SOAP literal o codificado. Para obtener más información sobre cómo
usar atributos para controlar el XML generado por un servicio Web XML, vea XML Serialization with XML Web
Services (Serialización XML con servicios Web XML). Para más información sobre SOAP y servicios Web XML, vea
Personalizar el formato de mensajes SOAP.
NOTE
Estos ensamblados de serialización se pueden generar de antemano y firmar utilizando la herramienta SGen.exe.
Esta no es la causa del funcionamiento de ningún servidor de servicio Web. En otras palabras, solo está para el uso
del cliente y para la serialización manual.
<OrderForm>
<OrderDate>12/12/01</OrderDate>
</OrderForm>
Para obtener más ejemplos de serialización, vea Examples of XML Serialization (Ejemplos de serialización XML).
NOTE
Solo se serializan las colecciones, no las propiedades públicas.
<XmlElement(DataType:="NMTOKEN")> _
Public MyToken As String
[XmlElement(DataType = "NMTOKEN")]
public string MyToken;
De igual forma, si está creando una clase que debe cumplir un esquema XML (XSD) concreto, debe aplicar el
atributo adecuado y establecer su propiedad DataType en el nombre del tipo de datos XML deseado.
Para obtener una lista completa de asignaciones de tipo, vea la propiedad DataType para cualquiera de las clases
de atributos siguientes:
SoapAttributeAttribute
SoapElementAttribute
XmlArrayItemAttribute
XmlAttributeAttribute
XmlElementAttribute
XmlRootAttribute
Vea también
XmlSerializer
DataContractSerializer
FileStream
Serialización SOAP y XML
Serialización binaria
Serialización
XmlSerializer
Ejemplos de serialización XML
Cómo: Serialización de un objeto
Cómo: Deserialización de un objeto
Ejemplos de serialización XML
16/09/2020 • 18 minutes to read • Edit Online
La serialización XML puede tomar más de un formulario, del simple al complejo. Por ejemplo, puede serializar una
clase que simplemente está compuesta de campos públicos y propiedades, como se muestra en Introducción a la
serialización XML. Los ejemplos de código siguientes resuelven varios escenarios avanzados, incluso cómo utilizar
la serialización XML para generar una secuencia XML que cumple con un documento de esquema XML concreto
(XSD).
<PurchaseOrder>
<MyAddress>
<FirstName>George</FirstName>
</MyAddress>
</PurchaseOrder>
<PurchaseOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ItemsOrders>
<Item>
<ItemID>aaa111</ItemID>
<ItemPrice>34.22</ItemPrice>
</Item>
<Item>
<ItemID>bbb222</ItemID>
<ItemPrice>2.89</ItemPrice>
</Item>
</ItemsOrders>
</PurchaseOrder>
Serializar una clase que implementa la interfaz ICollection
Puede crear sus propias clases de colección implementando la interfaz ICollection y utilizar XmlSerializer para
serializar instancias de estas clases. Tenga en cuenta que cuando una clase implementa la interfaz ICollection, se
serializa solo la colección contenida por la clase. No se serializará cualquier propiedad pública o campo
agregados a la clase. La clase debe incluir un método Add y una propiedad Item (indexador de C#) que se va a
serializar.
Imports System.Collections
Imports System.IO
Imports System.Xml.Serialization
End Property
return empArray.GetEnumerator()
End Function
Imports System.IO
Imports System.Xml
Imports System.Xml.Serialization
Imports Microsoft.VisualBasic
' Calculate is a custom method that calculates the price per item
' and stores the value in a field.
Public Sub Calculate()
LineTotal = UnitPrice * Quantity
End Sub
End Class
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
// Creates an OrderedItem.
OrderedItem i1 = new OrderedItem();
i1.ItemName = "Widget S";
i1.Description = "Small widget";
i1.UnitPrice = (decimal) 5.23;
i1.Quantity = 3;
i1.Calculate();
Vea también
Introducción a la serialización XML
Controlar la serialización XML mediante atributos
Atributos que controlan la serialización XML
XmlSerializer (clase)
Cómo: Serialización de un objeto
Cómo: Deserialización de un objeto
Herramienta de definición de esquema XML y
serialización XML
16/09/2020 • 2 minutes to read • Edit Online
La herramienta de definición de esquema XML (Herramienta de definición de esquema XML (Xsd.exe)) se instala
junto con las herramientas de .NET Framework como parte del kit de desarrollo de software (SDK) de Windows®.
La herramienta está diseñada para cumplir principalmente dos propósitos:
Para crear archivos de clase C# o para crearlos de clase Visual Basic y que así cumplan con un lenguaje de
definición de esquema XML (XSD) concreto. La herramienta toma un Esquema XML como un argumento y
genera un archivo que contiene varias clases que cuando se serializa con XmlSerializer, cumpla al esquema.
Para más información sobre cómo usar la herramienta para generar clases que cumplan con un esquema
en concreto, vea Procedimiento para usar la herramienta de definición de esquema XML para generar clases
y documentos de esquema XML.
Generar un documento de esquema XML de un archivo .dll o .exe. Para ver el esquema de un conjunto de
archivos que ha creado, o uno que se ha modificado con atributos, pase la DLL o el EXE como un argumento
de la herramienta para generar el esquema XML. Para más información sobre cómo usar la herramienta
para generar un documento de esquema XML a partir de un conjunto de clases, vea Procedimiento para
usar la herramienta de definición de esquema XML para generar clases y documentos de esquema XML.
Para más información sobre cómo usar la herramienta, vea Herramienta de definición de esquema XML (Xsd.exe).
Vea también
DataSet
Introducción a la serialización XML
Herramienta de definición de esquema XML (Xsd.exe)
XmlSerializer
Cómo: Serialización de un objeto
Cómo: Deserializar un objeto
Cómo: Usar la herramienta de definición de esquema XML para generar clases y documentos de esquema XML
Compatibilidad con enlaces del esquema XML
Controlar la serialización XML mediante atributos
16/09/2020 • 11 minutes to read • Edit Online
Los atributos se pueden utilizar para controlar la serialización XML de un objeto o crear una secuencia XML
alternativa a partir del mismo conjunto de clases. Para obtener más información sobre la creación de una
secuencia XML alternativa vea Procedimiento para especificar un nombre de elemento alternativo para una
secuencia XML.
NOTE
Si el código XML generado se ajusta a la sección 5 del documento Protocolo simple de acceso a objetos (SOAP) 1.1, del
Consorcio WWC (W3C), use los atributos enumerados en Atributos que controlan la serialización SOAP codificada.
La clase o nombre de miembro determina, de forma predeterminada, un nombre del elemento XML. En una clase
simple denominada Book , un campo denominado ISBN generará una etiqueta de elemento XML <ISBN> como
se muestra en el ejemplo siguiente.
Se puede cambiar este comportamiento predeterminado si desea dar un nuevo nombre al elemento. El código
siguiente muestra cómo un atributo habilita esto estableciendo la propiedad ElementName de
XmlElementAttribute.
Para obtener más información sobre atributos, vea Atributos. Para obtener una lista de atributos que controlan la
serialización XML, vea Atributos que controlan la serialización XML.
Controlar la serialización de la matriz
Los atributos XmlArrayAttribute y XmlArrayItemAttribute están diseñados para controlar la serialización de las
matrices. Con estos atributos, puede controlar el nombre de elemento, el espacio de nombres y el tipo de datos
del esquema XML (XSD) (como se define en el documento de World Wide Web Consortium [www.w3.org]
titulado "Esquema XML parte 2: tipos de datos"). También puede especificar los tipos que pueden estar incluidos
en una matriz.
XmlArrayAttribute determinará las propiedades del elemento envolvente XML que resulta cuando se serializa una
matriz. Por ejemplo, de forma predeterminada, serializar la matriz siguiente producirá un elemento XML
denominado Employees . El elemento Employees contendrá una serie de elementos denominados según el tipo de
matriz Employee .
<Group>
<Employees>
<Employee>
<Name>Haley</Name>
</Employee>
</Employees>
</Group>
Aplicando XmlArrayAttribute, puede cambiar el nombre del elemento XML, como sigue.
XmlArrayItemAttribute, por otro lado, controla cómo se serializan los elementos contenidos en la matriz. Observe
que el atributo se aplica al campo que devuelve la matriz.
<Group>
<Employees>
<MemberName>Haley</MemberName>
</Employees>
</Group>
<Group>
<Employees>
<Employee>
<Name>Haley</Name>
</Employee>
<Employee xsi:type = "Manager">
<Name>Ann</Name>
<Level>3</Level>
</Employee>
</Employees>
</Group>
<Group>
<Employees>
<Name>Haley</Name>
</Employees>
<Employees>
<Name>Noriko</Name>
</Employees>
<Employees>
<Name>Marco</Name>
</Employees>
</Group>
Otra manera de diferenciar las dos secuencias XML es utilizar la herramienta de definición de esquemas XML para
generar los archivos de documento de esquema XML (XSD) a partir del código compilado. (Para obtener más
información sobre el uso de la herramienta, vea La herramienta de definición de esquema XML y serialización
XML). Cuando no se aplica ningún atributo al campo, el esquema describe el elemento de la manera siguiente.
Cuando XmlElementAttribute se aplica al campo, el esquema resultante describe el elemento como sigue.
Serializar un ArrayList
La clase ArrayList puede contener una colección de objetos diversos. Además puede utilizar ArrayList tantas veces
como utiliza la matriz. En lugar de crear un campo que devuelve una matriz de objetos escritos, sin embargo,
puede crear un campo que devuelve un ArrayListúnico. Sin embargo, al igual que con las matrices, debe informar
aXmlSerializer de los tipos de objetos ArrayList que contiene. Para lograr esto, asigne varias instancias de
XmlElementAttribute al campo, como se muestra en el ejemplo siguiente.
<XmlRoot("NewGroupName"), _
XmlType("NewTypeName")> _
Public Class Group
Public Employees() As Employee
End Class
[XmlRoot("NewGroupName")]
[XmlType("NewTypeName")]
public class Group {
public Employee[] Employees;
}
Si esta clase está compilada, y la herramienta de definición de esquemas XML se utiliza para generar su esquema,
encontraría el XML siguiente que describe Group .
En contraste, si fuera serializar una instancia de la clase, solo NewGroupName se buscarían en el documento XML.
<NewGroupName>
. . .
</NewGroupName>
Vea también
Atributos que controlan la serialización XML
Atributos que controlan la serialización SOAP codificada
Introducción a la serialización XML
Ejemplos de serialización XML
Cómo: Especificar un nombre de elemento alternativo para una secuencia XML
Cómo: Serialización de un objeto
Cómo: Deserialización de un objeto
Atributos que controlan la serialización XML
16/09/2020 • 4 minutes to read • Edit Online
Se pueden aplicar atributos a clases y a miembros de clase en la siguiente tabla para controlar la manera en que
XmlSerializer serializa o deserializa una instancia de la clase. Para entender cómo controlan estos atributos la
serialización XML, vea Controlar la serialización XML mediante atributos.
Estos atributos también se pueden utilizar para controlar los mensajes SOAP de estilo literales generados por un
servicio Web XML. Para más información sobre la aplicación de estos atributos a un método de servicios web
XML, vea Serialización XML con servicios web XML.
Para obtener más información sobre atributos, vea Atributos.
Además de estos atributos, que se encuentran todos en el espacio de nombres System.Xml.Serialization también
se puede aplicar el atributo DefaultValueAttribute a un campo. DefaultValueAttribute establece el valor que se
asignará automáticamente al miembro si no se especifica ningún valor.
Para controlar la serialización SOAP y XML codificada, vea Atributos que controlan la serialización SOAP
codificada.
Vea también
Serialización SOAP y XML
XmlSerializer
Controlar la serialización XML mediante atributos
Cómo: Especificar un nombre de elemento alternativo para una secuencia XML
Cómo: Serialización de un objeto
Cómo: Deserialización de un objeto
Serialización XML con servicios Web XML
16/09/2020 • 8 minutes to read • Edit Online
La serialización XML es el mecanismo de transporte subyacente utilizado en la arquitectura de los servicios Web
XML, realizada por la clase XmlSerializer. Para controlar el XML generado por un servicio web XML, puede aplicar
los atributos de Atributos que controlan la serialización XML y Atributos que controlan la serialización SOAP
codificada a las clases, los valores devueltos, los parámetros y los campos de un archivo usados para crear un
servicio web XML (.asmx). Para más información sobre cómo crear un servicio web XML, vea Servicios web XML
con ASP.NET.
<WebMethod, SoapDocumentMethod> _
public Function MyLiteralMethod() As _
<XmlElement(Namespace:="http://www.cohowinery.com", _
ElementName:= "BookOrder")> _
Order
Dim myOrder As Order = New Order()
return myOrder
End Function
<WebMethod, SoapDocumentMethod> _
public Function MyLiteralMethod(<XmlElement _
("MyOrderID", Namespace:="http://www.microsoft.com")>ID As String) As _
<XmlElement(Namespace:="http://www.cohowinery.com", _
ElementName:= "BookOrder")> _
Order
Dim myOrder As Order = New Order()
myOrder.OrderID = ID
return myOrder
End Function
Se pueden ver los resultados de aplicar XmlTypeAttribute y SoapTypeAttribute al examinar la descripción del
servicio, como se muestra en el ejemplo de código siguiente.
<s:schema targetNamespace="http://tempuri.org/encodedTypes">
<s:complexType name="SoapBookService">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="EncodedOrderID" type="s:string" />
</s:sequence>
</s:complexType>
</s:schema>
</s:complexType>
El efecto de XmlRootAttribute también se puede ver en el Http GET y resultados de Http POST, como sigue.
Vea también
Serialización SOAP y XML
Atributos que controlan la serialización SOAP codificada
Cómo: Serializar un objeto como secuencia XML con codificación SOAP
Cómo: invalidar la serialización XML SOAP codificada
Introducción a la serialización XML
Cómo: Serialización de un objeto
Cómo: Deserialización de un objeto
Atributos que controlan la serialización SOAP
codificada
16/09/2020 • 2 minutes to read • Edit Online
El documento de World Wide Web Consortium (W3C) denominado Simple Object Access Protocol (SOAP) 1.1
contiene una sección opcional (sección 5) que describe cómo se pueden codificar los parámetros SOAP. Para
ajustarse a la sección 5 de la especificación, debe usar un conjunto especial de atributos que se encuentra en el
espacio de nombres System.Xml.Serialization. Aplique según corresponda esos atributos a las clases y
miembros de clases y, a continuación, utilice XmlSerializer para serializar instancias de la clase o clases.
La tabla siguiente muestra los atributos, donde se pueden aplicar, y lo que hacen. Para más información sobre
el uso de estos atributos para controlar la serialización de XML, consulte Cómo: Serializar un objeto como
secuencia XML con codificación SOAP y Cómo: Invalidar la serialización XML SOAP codificada.
Para obtener más información sobre atributos, vea Atributos.
Vea también
Serialización SOAP y XML
Cómo: Serializar un objeto como secuencia XML con codificación SOAP
Cómo: Invalidar la serialización XML SOAP codificada
Atributos
XmlSerializer
Cómo: Serialización de un objeto
Cómo: Deserialización de un objeto
Procedimiento para serializar un objeto
16/09/2020 • 2 minutes to read • Edit Online
Para serializar un objeto, primero cree el objeto que será serializado y establezca Debe determinar el formato de
transporte en el que la secuencia XML estará almacenada, o como una secuencia o como un archivo, para ello.
Por ejemplo, si la secuencia XML debe estar guardada en un formulario permanente, cree un objeto FileStream.
NOTE
Para obtener más ejemplos de serialización XML, vea Ejemplos de serialización XML.
Serializar un objeto
1. Cree el objeto y establezca sus campos públicos y propiedades.
2. Construya un XmlSerializer utilizando el tipo de objeto. Para obtener más información, vea los
constructores de clase XmlSerializer .
3. Llame al método Serialize para generar o una secuencia XML o una representación del archivo de las
propiedades públicas del objeto y campos. En el ejemplo siguiente se crea un archivo.
Vea también
Introducción a la serialización XML
Cómo: Deserialización de un objeto
Deserialización de un objeto con XmlSerializer
16/09/2020 • 2 minutes to read • Edit Online
Al deserializar un objeto, el formato de transporte determina si creará una secuencia u objeto de archivo. Una
vez determinado el formato de transporte, puede llamar Serialize o los métodos Deserialize, como se requiera.
Vea también
Introducción a la serialización XML
Cómo: Serialización de un objeto
Procedimiento para usar la herramienta de definición
de esquema XML para generar clases y documentos
de esquema XML
16/09/2020 • 3 minutes to read • Edit Online
La herramienta XML Schema Definition (Xsd.exe) le permite generar un esquema XML que describe una clase o
generar la clase definida por un esquema XML. Los procedimientos siguientes muestran cómo realizar estas
operaciones.
La herramienta de definición de esquema XML (Xsd.exe) suele estar en la siguiente ruta de acceso:
C:\Archivos de programa (x86)\Microsoft SDKs\Windows\{versión}\bin\NETFX {versión} Tools\
Para generar clases que cumplen con un esquema concreto
1. Abra un símbolo del sistema.
2. Pasar el esquema XML como un argumento a la herramienta XML Schema Definition, que crea un conjunto
de clases con las que precisamente coinciden el Esquema XML, por ejemplo:
xsd mySchema.xsd
La herramienta solo puede procesar esquemas que hagan referencia a la especificación de World Wide
Web Consortium XML del 16 de marzo de 2001. En otras palabras, el espacio de nombres del esquema
XML debe ser "http://www.w3.org/2001/XMLSchema" como se muestra en el ejemplo siguiente.
3. Modifique las clases con métodos, propiedades o campos, como sea necesario. Para más información sobre
cómo modificar una clase con atributos, vea Controlar la serialización XML mediante atributos y Atributos
que controlan la serialización SOAP codificada.
Es a menudo útil para examinar el esquema de la secuencia XML que se genera cuando se serializan las instancias
de una clase (o clases). Por ejemplo, podría publicar su esquema para que otros lo utilicen o podría compararlo
con un esquema con el que está intentando lograr la conformidad.
Para generar un documento de esquema XML de un conjunto de clases
1. Compile la clase o clases en un archivo DLL.
2. Abra un símbolo del sistema.
3. Pasar el archivo DLL como un argumento a Xsd.exe, por ejemplo:
xsd MyFile.dll
Vea también
DataSet
Herramienta de definición de esquema XML y serialización XML
Introducción a la serialización XML
Herramienta de definición de esquema XML (Xsd.exe)
XmlSerializer
Cómo: Serialización de un objeto
Cómo: Deserialización de un objeto
Procedimiento para controlar la serialización de
clases derivadas
16/09/2020 • 5 minutes to read • Edit Online
El uso del atributo XmlElementAttribute para cambiar el nombre de un elemento XML no es la única manera de
personalizar la serialización de objeto. También puede personalizar la secuencia XML derivando de una clase
existente e indicando a la instancia XmlSerializer cómo serializar la nueva clase.
Por ejemplo, dada una clase Book , puede derivar de él y crear una clase ExpandedBook que tiene más propiedades.
Pero debe indicarle a XmlSerializer que acepte el tipo derivado al serializar o deserializar. Para ello, cree una
instancia XmlElementAttribute y establezca su propiedad Type en el tipo de clase derivada. Agregue
XmlElementAttribute a una instancia XmlAttributes. Después, agregue XmlAttributes a una instancia
XmlAttributeOverrides. Para ello, especifique el tipo que se invalida y el nombre del miembro que acepta la clase
derivada. Esta implementación se muestra en el ejemplo siguiente.
Ejemplo
Public Class Orders
public Books() As Book
End Class
' Adds the type of the class that contains the overridden
' Adds the type of the class that contains the overridden
' member, as well as the XmlAttributes instance to override it
' with, to the XmlAttributeOverrides instance.
attrOverrides.Add(GetType(Orders), "Books", attrs)
Vea también
XmlSerializer
XmlElementAttribute
XmlAttributes
XmlAttributeOverrides
Serialización SOAP y XML
Cómo: Serialización de un objeto
Cómo: para especificar un nombre de elemento alternativo para una secuencia XML
Procedimiento para especificar un nombre de
elemento alternativo para una secuencia XML
16/09/2020 • 3 minutes to read • Edit Online
UtilizandoXmlSerializer, se puede generar más de una secuencia XML con el mismo conjunto de clases. Puede
que desee proceder de esta forma ya que dos servicios Web XML diferentes requieren la misma información
básica, con solo ligeras diferencias. Por ejemplo, imagine dos servicios Web XML que procesan órdenes para los
libros y así ambos requieren los números de ISBN. Un servicio usa la etiqueta <ISBN> mientras el segundo usa la
etiqueta <BookID>. Tiene una clase denominada Book que contiene un campo denominado ISBN . Cuando se
serializa una instancia de la clase Book , utilizará, de forma predeterminada, el nombre de miembro (ISBN) como
el nombre de elemento de etiqueta. Para el primer servicio Web XML, esto es como esperado. Pero para enviar la
secuencia XML al segundo servicio Web XML, debe invalidar la serialización para que el nombre de elemento de
la etiqueta sea BookID .
Ejemplo
Public Function SerializeOverride()
' Creates an XmlElementAttribute with the alternate name.
Dim myElementAttribute As XmlElementAttribute = _
New XmlElementAttribute()
myElementAttribute.ElementName = "BookID"
Dim myAttributes As XmlAttributes = New XmlAttributes()
myAttributes.XmlElements.Add(myElementAttribute)
Dim myOverrides As XmlAttributeOverrides = New XmlAttributeOverrides()
myOverrides.Add(typeof(Book), "ISBN", myAttributes)
Dim mySerializer As XmlSerializer = _
New XmlSerializer(GetType(Book), myOverrides)
Dim b As Book = New Book()
b.ISBN = "123456789"
' Creates a StreamWriter to write the XML stream to.
Dim writer As StreamWriter = New StreamWriter("Book.xml")
mySerializer.Serialize(writer, b);
End Class
public void SerializeOverride()
{
// Creates an XmlElementAttribute with the alternate name.
XmlElementAttribute myElementAttribute = new XmlElementAttribute();
myElementAttribute.ElementName = "BookID";
XmlAttributes myAttributes = new XmlAttributes();
myAttributes.XmlElements.Add(myElementAttribute);
XmlAttributeOverrides myOverrides = new XmlAttributeOverrides();
myOverrides.Add(typeof(Book), "ISBN", myAttributes);
XmlSerializer mySerializer =
new XmlSerializer(typeof(Book), myOverrides)
Book b = new Book();
b.ISBN = "123456789"
// Creates a StreamWriter to write the XML stream to.
StreamWriter writer = new StreamWriter("Book.xml");
mySerializer.Serialize(writer, b);
}
<Book>
<BookID>123456789</BookID>
</Book>
Vea también
XmlElementAttribute
XmlAttributes
XmlAttributeOverrides
Serialización SOAP y XML
XmlSerializer
Cómo: Serialización de un objeto
Cómo: Deserialización de un objeto
Procedimiento para calificar elementos XML y
nombres de atributo de XML
16/09/2020 • 4 minutes to read • Edit Online
Los espacios de nombres XML contenidos por instancias de la clase XmlSerializerNamespaces deben cumplir con la
especificación de World Wide Web Consortium (W3C) llamada Espacios de nombres en XML.
Los espacios de nombres XML proporcionan una método para calificar los nombres de elementos y atributos XML
en documentos XML. Un nombre calificado se compone de un prefijo y un nombre local, separados por dos puntos.
El prefijo funciona únicamente como marcador de posición y está asignado a un identificador URI que especifica un
espacio de nombres. La combinación del espacio de nombres del URI, universalmente administrado, y el nombre
local genera un nombre del que se garantiza que es universalmente único.
Creando una instancia de XmlSerializerNamespaces y agregando los pares de espacio de nombres al objeto, puede
especificar los prefijos utilizados en un documento XML.
Ejemplo
En el siguiente ejemplo, se crea un XmlSerializerNamespaces al que se agregan dos prefijos y pares de espacio de
nombres al objeto. El código crea XmlSerializer que se utiliza para serializar una instancia de la clase Books . El
código llama al método Serialize con XmlSerializerNamespaces , permitiéndole al XML contener los espacios de
nombres prefijados.
Imports System.IO
Imports System.Xml
Imports System.Xml.Serialization
<XmlType([Namespace] := "http://www.cpandl.com")> _
Public Class Book
<XmlElement([Namespace] := "http://www.cpandl.com")> _
Public TITLE As String
<XmlElement([Namespace] := "http://www.cohowinery.com")> _
Public PRICE As Price
End Class
// Creates a Book.
var myBook = new Book();
myBook.TITLE = "A Book Title";
var myPrice = new Price();
myPrice.price = (decimal) 9.95;
myPrice.currency = "US Dollar";
myBook.PRICE = myPrice;
var myBooks = new Books();
myBooks.Book = myBook;
mySerializer.Serialize(myWriter, myBooks, myNamespaces);
myWriter.Close();
}
}
[XmlType(Namespace ="http://www.cpandl.com")]
public class Book
{
[XmlElement(Namespace = "http://www.cpandl.com")]
public string TITLE;
[XmlElement(Namespace ="http://www.cohowinery.com")]
public Price PRICE;
}
Vea también
XmlSerializer
Herramienta de definición de esquema XML y serialización XML
Introducción a la serialización XML
XmlSerializer (clase)
Atributos que controlan la serialización XML
Cómo: Especificar un nombre de elemento alternativo para una secuencia XML
Cómo: Serialización de un objeto
Cómo: Deserialización de un objeto
Procedimiento para serializar un objeto como
secuencia XML con codificación SOAP
16/09/2020 • 2 minutes to read • Edit Online
Dado que un mensaje SOAP se genera mediante XML, se puede usar la clase XmlSerializer para serializar las
clases y generar mensajes SOAP codificados. El XML resultante se ajusta a la sección 5 del documento de World
Wide Web Consortium, "Protocolo simple de acceso a objetos (SOAP) 1.1". Si está creando un servicio Web XML
que se comunica a través de mensajes SOAP, puede personalizar la secuencia XML aplicando un conjunto de
atributos SOAP especiales a las clases y miembros de clases. Para obtener más información, vea Atributos que
controlan la serialización SOAP codificada.
Para serializar un objeto como secuencia XML con codificación SOAP
1. Cree la clase mediante la herramienta de definición de esquema XML (Xsd.exe).
2. Aplique uno o más de los atributos especiales situados en System.Xml.Serialization . Vea la lista en
"Atributos que controlan la serialización SOAP codificada".
3. Cree XmlTypeMapping creando un nuevo SoapReflectionImporter e invocando el método ImportTypeMapping
con el tipo de la clase serializada.
En el ejemplo de código siguiente se llama al método ImportTypeMapping de la clase
SoapReflectionImporter para crear XmlTypeMapping .
Ejemplo
' Serializes a class named Group as a SOAP message.
Dim myTypeMapping As XmlTypeMapping =
New SoapReflectionImporter().ImportTypeMapping(GetType(Group))
Dim mySerializer As XmlSerializer = New XmlSerializer(myTypeMapping)
// Serializes a class named Group as a SOAP message.
XmlTypeMapping myTypeMapping =
new SoapReflectionImporter().ImportTypeMapping(typeof(Group));
XmlSerializer mySerializer = new XmlSerializer(myTypeMapping);
Vea también
Serialización SOAP y XML
Atributos que controlan la serialización SOAP codificada
Serialización XML con servicios web XML
Cómo: Serialización de un objeto
Cómo: Deserializar un objeto
Cómo: Invalidar la serialización XML SOAP codificada
Procedimiento para invalidar la serialización XML
SOAP codificada
16/09/2020 • 5 minutes to read • Edit Online
El proceso para invalidar serialización XML de objetos como los mensajes SOAP es similar al proceso para
invalidar la serialización XML estándar. Para obtener más información sobre la invalidación de la serialización XML
estándar, vea Cómo: Especificar un nombre de elemento alternativo para una secuencia XML.
Ejemplo
El ejemplo de código siguiente serializa un archivo de dos maneras: primero, sin invalidar el comportamiento de
clase XmlSerializer y segundo, invalidando el comportamiento. El ejemplo contiene una clase denominada
Group con varios miembros. Varios atributos, como SoapElementAttribute , se han aplicado a los miembros de
clase. Cuando la clase se serializa con el método SerializeOriginal , los atributos controlan el contenido del
mensaje SOAP. Cuando se llama al método SerializeOverride , el comportamiento de XmlSerializer se invalida
creando varios atributos y estableciendo las propiedades de SoapAttributes en esos atributos (según
corresponda).
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Schema;
[SoapAttribute(DataType = "base64Binary")]
public Byte [] GroupNumber;
[SoapInclude(typeof(Car))]
public Vehicle myCar(string licNumber)
{
Vehicle v;
if(licNumber == "")
{
v = new Car();
v.licenseNumber = "!!!!!!";
}
else
{
v = new Car();
v.licenseNumber = licNumber;
}
return v;
}
}
}
public void SerializeOriginal(string filename)
{
// Creates an instance of the XmlSerializer class.
XmlTypeMapping myMapping =
(new SoapReflectionImporter().ImportTypeMapping(
typeof(Group)));
XmlSerializer mySerializer =
new XmlSerializer(myMapping);
myGroup.PositiveInt= "10000";
myGroup.IgnoreThis=true;
myGroup.Grouptype= GroupType.small;
Car thisCar =(Car) myGroup.myCar("1234566");
// Prints the license number just to prove the car was created.
Console.WriteLine("License#: " + thisCar.licenseNumber + "\n");
myGroup.PositiveInt= "10000";
myGroup.IgnoreThis=true;
myGroup.Grouptype= GroupType.small;
Car thisCar =(Car) myGroup.myCar("1234566");
Console.WriteLine(myGroup.GroupName);
Console.WriteLine(myGroup.GroupNumber[0]);
Console.WriteLine(myGroup.GroupNumber[1]);
Console.WriteLine(myGroup.Today);
Console.WriteLine(myGroup.PositiveInt);
Console.WriteLine(myGroup.IgnoreThis);
Console.WriteLine();
}
Console.WriteLine(myGroup.GroupName);
Console.WriteLine(myGroup.GroupNumber[0]);
Console.WriteLine(myGroup.GroupNumber[1]);
Console.WriteLine(myGroup.Today);
Console.WriteLine(myGroup.PositiveInt);
Console.WriteLine(myGroup.IgnoreThis);
}
Vea también
Serialización SOAP y XML
Atributos que controlan la serialización SOAP codificada
Serialización XML con servicios web XML
Cómo: Serialización de un objeto
Cómo: Deserializar un objeto
Cómo: Serializar un objeto como secuencia XML con codificación SOAP
<system.xml.serialization> (Elemento)
16/09/2020 • 2 minutes to read • Edit Online
El elemento de nivel superior para controlar la serialización XML. Para más información sobre los archivos de
configuración, vea Configuration File Schema (Esquema de archivos de configuración).
<configuration>
<system.xml.serialization>
Sintaxis
<system.xml.serialization>
</system.xml.serialization>
Atributos y elementos
En las siguientes secciones se describen los atributos, los elementos secundarios y los elementos primarios.
Atributos
Ninguno.
Elementos secundarios
EL EM EN TO DESC RIP C IÓ N
Elementos primarios
EL EM EN TO DESC RIP C IÓ N
Ejemplo
El ejemplo de código siguiente muestra cómo especificar el modo de la serialización de un objeto DateTime y la
suma de tipos utilizada por XmlSchemaImporter al asignar los tipos XSD a los tipos de .NET Framework.
<system.xml.serialization>
<xmlSerializer checkDeserializeAdvances="false" />
<dateTimeSerialization mode = "Local" />
<schemaImporterExtensions>
<add
name = "MobileCapabilities"
type = "System.Web.Mobile.MobileCapabilities,
System.Web.Mobile, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f6f11d40a3a" />
</schemaImporterExtensions>
</system.xml.serialization>
Vea también
XmlSchemaImporter
DateTimeSerializationSection.DateTimeSerializationMode
Esquema de los archivos de configuración
Elemento <dateTimeSerialization>
Elemento <schemaImporterExtensions>
<add> Elemento para <schemaImporterExtensions>
<dateTimeSerialization> (Elemento)
16/09/2020 • 2 minutes to read • Edit Online
Sintaxis
<dateTimeSerialization
mode = "Roundtrip|Local"
/>
Atributos y elementos
En las siguientes secciones se describen los atributos, los elementos secundarios y los elementos primarios.
Atributos
AT RIB UTO S DESC RIP C IÓ N
Elementos secundarios
Ninguno.
Elementos primarios
EL EM EN TO DESC RIP C IÓ N
Comentarios
En las versiones 1.0, 1.1, 2.0 y posteriores de .NET Framework, cuando esta propiedad se establece en Local , los
objetos DateTime siempre reciben formato según la hora local. Es decir, la información de zona horaria local
siempre se incluye con los datos serializados. Establezca esta propiedad en Local para garantizar la compatibilidad
con las versiones anteriores de .NET Framework.
En la versión 2.0 y posteriores de .NET Framework que tienen esta propiedad establecida en Roundtrip , los
objetos DateTime se examinan para determinar si están en la zona horaria local, UTC o una no especificada. Los
objetos DateTime se serializan a continuación de este tipo de manera que esta información se conserva. Éste es el
comportamiento predeterminado y el que se recomienda para todas las aplicaciones nuevas que no funcionan con
versiones anteriores de .Net Framework.
Vea también
DateTime
XmlSchemaImporter
DateTimeSerializationSection.DateTimeSerializationMode
Esquema de los archivos de configuración
Elemento <schemaImporterExtensions>
<add> Elemento para <schemaImporterExtensions>
Elemento <system.xml.serialization>
<schemaImporterExtensions> (Elemento)
16/09/2020 • 2 minutes to read • Edit Online
Contiene tipos que son utilizados por XmlSchemaImporter para asignar de los tipos XSD a los tipos de .NET
Framework. Para más información sobre los archivos de configuración, vea Configuration File Schema (Esquema
de archivos de configuración).
Sintaxis
<schemaImporterExtensions>
<!-- Add types -->
</schemaImporterExtensions>
Elementos secundarios
EL EM EN TO DESC RIP C IÓ N
<add> Elemento para <schemaImporterExtensions> Agrega tipos que son usados por XmlSchemaImporter para
crear las asignaciones.
Elementos primarios
EL EM EN TO DESC RIP C IÓ N
Ejemplo
El ejemplo de código siguiente muestra cómo agregar tipos que son utilizados por XmlSchemaImporter al asignar
los tipos XSD a los tipos de .NET Framework.
<system.xml.serialization>
<schemaImporterExtensions>
<add name = "MobileCapabilities" type =
"System.Web.Mobile.MobileCapabilities,
System.Web.Mobile, Version - 2.0.0.0, Culture = neutral,
PublicKeyToken = b03f5f6f11d40a3a" />
</schemaImporterExtensions>
</system.xml.serialization>
Vea también
XmlSchemaImporter
DateTimeSerializationSection.DateTimeSerializationMode
Esquema de los archivos de configuración
Elemento <dateTimeSerialization>
<add> Elemento para <schemaImporterExtensions>
Elemento <system.xml.serialization>
Elemento <add> para <schemaImporterExtensions>
16/09/2020 • 2 minutes to read • Edit Online
Agrega tipos utilizados por XmlSchemaImporter para asignar los tipos XSD a los tipos de .NET Framework. Para
más información sobre los archivos de configuración, vea Configuration File Schema (Esquema de archivos de
configuración).
<configuration>
<system.xml.serialization>
<schemaImporterExtensions>
<add>
Sintaxis
<add name = "typeName" type="fully qualified type [,Version=version number] [,Culture=culture]
[,PublicKeyToken= token]"/>
Atributos y elementos
En las siguientes secciones se describen los atributos, los elementos secundarios y los elementos primarios.
Atributos
AT RIB UTO DESC RIP C IÓ N
Elementos secundarios
Ninguno.
Elementos primarios
EL EM EN TO DESC RIP C IÓ N
Ejemplo
El ejemplo de código siguiente agrega un tipo de extensión que XmlSchemaImporter puede utilizar al asignar los
tipos.
<configuration>
<system.xml.serialization>
<schemaImporterExtensions>
<add name="contoso" type="System.Web.Mobile.MobileCapabilities,
System.Web.Mobile, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" />
</schemaImporterExtensions>
</system.xml.serialization>
</configuration>
Vea también
XmlSchemaImporter
Elemento <system.xml.serialization>
Elemento <schemaImporterExtensions>
<xmlSerializer> (Elemento)
16/09/2020 • 2 minutes to read • Edit Online
Sintaxis
<xmlSerializer checkDeserializerAdvance = "true|false" />
Atributos y elementos
En las siguientes secciones se describen los atributos, los elementos secundarios y los elementos primarios.
Atributos
AT RIB UTO DESC RIP C IÓ N
Elementos secundarios
Ninguno.
Elementos primarios
EL EM EN TO DESC RIP C IÓ N
Comentarios
De forma predeterminada, XmlSerializer proporciona una capa adicional de seguridad contra los ataques por
denegación de servicio potenciales al deserializar datos que no son de confianza. Actúa de esta modo intentando
detectar los bucles sin fin durante la deserialización. Si se detecta este tipo de condición, se inicia una excepción con
el siguiente mensaje: "Error interno: la deserialización no ha podido avanzar sobre la secuencia subyacente".
Recibir este mensaje necesariamente no indica que un ataque por denegación de servicio está en curso. En algunas
circunstancias raras, el mecanismo de detección de bucle sin fin genera un positivo falso y la excepción se
producirá para un mensaje entrante legítimo. Si detecta que en la aplicación concreta esta capa adicional de
protección está rechazando los mensajes legítimos, establezca el atributo checkDeserializeAdvances en "false".
Ejemplo
En el ejemplo de código siguiente se establece el atributo checkDeserializeAdvances en "false".
<configuration>
<system.xml.serialization>
<xmlSerializer checkDeserializeAdvances="false" />
</system.xml.serialization>
</configuration>
Vea también
XmlSerializer
Elemento <system.xml.serialization>
Serialización SOAP y XML
Herramienta Generador de serializador XML
(Sgen.exe)
16/09/2020 • 5 minutes to read • Edit Online
El Generador de serializador XML crea un ensamblado de serialización XML para los tipos de un ensamblado
especificado. El ensamblado de serialización mejora el rendimiento de inicio de un elemento XmlSerializer al
serializar o deserializar objetos de los tipos especificados.
Sintaxis
Ejecute la herramienta desde la línea de comandos.
sgen [options]
TIP
Para que las herramientas de .NET Framework funcionen como deben, hay que configurar correctamente las variables de
entorno Path , Include y Lib . Establezca estas variables de entorno ejecutando SDKVars.bat, que se encuentra en el
directorio <SDK>\v2.0\Bin. SDKVars.bat debe ejecutarse en cada shell de comando.
Parámetros
O P C IÓ N DESC RIP C IÓ N
/a[ssembly]: nombre_de_archivo Genera código de serialización para todos los tipos incluidos
en el ensamblado o la aplicación ejecutable especificados por
filename. Solo se puede proporcionar un nombre de archivo.
Si se repite este argumento, se utilizará el último nombre.
/r[eference]: archivos_de_ensamblado Especifica los ensamblados a los que hacen referencia los
tipos que requieren serialización XML. Acepta varios archivos
de ensamblado separados por comas.
Comentarios
Cuando no se utiliza el Generador de serializador XML, un objeto XmlSerializer genera código de serialización y
un ensamblado de serialización para cada tipo siempre que se ejecuta una aplicación. Para mejorar el rendimiento
del inicio de la serialización XML, use la herramienta Sgen.exe a fin de generar esos ensamblados con antelación.
Estos ensamblados se podrán implementar después con la aplicación.
El Generador de serializador XML también puede mejorar el rendimiento de los clientes que utilizan proxy de
servicio Web XML para comunicarse con los servidores, dado que no se verá afectado el rendimiento del proceso
de serialización la primera vez que se carga el tipo.
Estos ensamblados generados no se pueden utilizar en el lado del servidor de un servicio Web. Esta herramienta
solo es para los clientes de servicios Web y escenarios de serialización manual.
Si el ensamblado que contiene el tipo que se debe serializar se denomina MyType.dll, el ensamblado de
serialización asociado se denominará MyType.XmlSerializers.dll.
Ejemplos
Mediante el siguiente comando se crea un ensamblado denominado Data.XmlSerializers.dll para serializar todos
los tipos que contiene el ensamblado denominado Data.dll.
sgen Data.dll
Se puede hacer referencia al ensamblado Data.XmlSerializers.dll desde código cuando se necesite serializar y
deserializar los tipos en Data.dll.
Vea también
Herramientas
Símbolos del sistema
XML Schema Definition Tool (Xsd.exe)
16/09/2020 • 18 minutes to read • Edit Online
La herramienta Definición de esquemas XML (Xsd.exe) genera clases de esquemas XML o de Common Language
Runtime a partir de archivos XDR, XML y XSD, o a partir de clases de un ensamblado de motor en tiempo de
ejecución.
La herramienta de definición de esquema XML (Xsd.exe) suele estar en la siguiente ruta de acceso:
C:\Archivos de programa (x86)\Microsoft SDKs\Windows\{versión}\bin\NETFX {versión} Tools\
Sintaxis
Ejecute la herramienta desde la línea de comandos.
TIP
Para que las herramientas de .NET Framework funcionen como deben, hay que configurar correctamente las variables de
entorno Path , Include y Lib . Establezca estas variables de entorno ejecutando SDKVars.bat, que se encuentra en el
directorio <SDK>\v2.0\Bin. SDKVars.bat debe ejecutarse en cada shell de comando.
Argumento
A RGUM EN TO DESC RIP C IÓ N
A RGUM EN TO DESC RIP C IÓ N
Opciones generales
O P C IÓ N DESC RIP C IÓ N
/p[arameters]: file.xml Lee las opciones de los distintos modos de operación desde
el archivo .xml especificado. La forma abreviada es /p: . Para
más información, vea la sección Comentarios.
O P C IÓ N DESC RIP C IÓ N
O P C IÓ N DESC RIP C IÓ N
O P C IÓ N DESC RIP C IÓ N
/t[ype]: typename Especifica el nombre del tipo para el que se crea un esquema.
Se pueden especificar varios argumentos de tipo. Si
typename no especifica un espacio de nombres, Xsd.exe
busca todos los tipos del ensamblado con el tipo
especificado. Si typename especifica un espacio de nombres,
solo se busca ese tipo. Si typename termina con carácter de
asterisco (*), la herramienta busca todos los tipos que
empiezan con la cadena anterior a *. Si se omite la opción
/type , Xsd.exe genera esquemas para todos los tipos del
ensamblado.
Comentarios
En la siguiente tabla se muestran las operaciones que realiza Xsd.exe.
Xsd.exe solo permite manipular esquemas XML que siguen al lenguaje de definición de esquemas XML (XSD)
propuesto por el consorcio World Wide Web (W3C). Para más información sobre la propuesta XSD (lenguaje de
definición de esquemas XML) o la norma XML, vea https://w3.org.
xsd /p:GenerateSchemas.xml
Por otro lado, si se estuviese generando un esquema para un tipo único situado en el ensamblado, se podría
utilizar el siguiente código XML:
Pero para poder utilizar el código anterior, habría que especificar también el nombre del ensamblado en el
símbolo del sistema. Escriba lo siguiente en el símbolo del sistema (se da por hecho que el nombre del
archivo XML es GenerateSchemaFromType.xml):
Solo se debe especificar una de las siguientes opciones para el elemento <generateSchemas> .
EL EM EN TO DESC RIP C IÓ N
Para generar un archivo de código, utilice el elemento <generateClasses> . En el siguiente ejemplo se genera un
archivo de código. Observe que se muestran también dos atributos que permiten establecer el lenguaje de
programación y el espacio de nombres del archivo generado.
<xsd xmlns='http://microsoft.com/dotnet/tools/xsd/'>
<generateClasses language='VB' namespace='Microsoft.Serialization.Examples'/>
</xsd>
<!-- You must supply an .xsd file when typing in the command line.-->
<!-- For example: xsd /p:genClasses mySchema.xsd -->
Entre las opciones que se pueden establecer para el elemento <generateClasses> se incluyen las siguientes.
EL EM EN TO DESC RIP C IÓ N
En la siguiente tabla se muestran los atributos que también pueden utilizarse con el elemento <generateClasses>
.
También se puede controlar la forma en que se genera el código DataSet mediante el uso del elemento
<generateDataSet> . El siguiente XML especifica que el código generado usa estructuras DataSet (como la clase
DataTable) para crear código de Visual Basic para un elemento especificado. Las estructuras de DataSet
generadas admitirán consultas LINQ.
<xsd xmlns='http://microsoft.com/dotnet/tools/xsd/'>
<generateDataSet language='VB' namespace='Microsoft.Serialization.Examples' enableLinqDataSet='true'>
</generateDataSet>
</xsd>
Entre las opciones que se pueden establecer para el elemento <generateDataSet> se incluyen las siguientes.
EL EM EN TO DESC RIP C IÓ N
En la siguiente tabla se muestran los atributos que también pueden utilizarse con el elemento <generateDataSet>
.
AT RIB UTO DESC RIP C IÓ N
Hay atributos que pueden establecerse en el elemento <xsd> de nivel superior. Estas opciones pueden utilizarse
con cualquiera de los elementos secundarios ( <generateSchemas> , <generateClasses> o <generateDataSet> ). El
siguiente código XML genera código para un elemento denominado "IDItems" del directorio de resultados
denominado "MyOutputDirectory".
En la siguiente tabla se muestran los atributos que también pueden utilizarse con el elemento <xsd> .
Ejemplos
El comando siguiente genera un esquema XML a partir de myFile.xdr y lo guarda en el directorio actual.
xsd myFile.xdr
El comando siguiente genera un esquema XML a partir de myFile.xml y lo guarda en el directorio especificado.
El comando siguiente genera esquemas XML para todos los tipos del ensamblado myAssembly.dll , y los guarda
como schema0.xsd en el directorio actual.
xsd myAssembly.dll
Vea también
DataSet
System.Xml.Serialization.XmlSerializer
Herramientas
Símbolos del sistema
Información general de LINQ to DataSet
Consultar objetos DataSet con tipo
LINQ (Language-Integrated Query) (C#)
LINQ (Language-Integrated Query) (Visual Basic)
E/S de archivos y secuencias
16/09/2020 • 17 minutes to read • Edit Online
La E/S (entrada/salida) de archivos y secuencias hace referencia a la transferencia de datos con destino u origen
en un medio de almacenamiento. En .NET Framework, los espacios de nombres System.IO contienen tipos que
permiten la lectura y escritura, tanto sincrónica como asincrónica, en archivos y flujos de datos. Estos espacios de
nombres también contienen tipos que realizan la compresión y la descompresión de archivos, así como tipos que
permiten la comunicación a través de canalizaciones y puertos de serie.
Un archivo es una colección de bytes ordenada y con nombre que tiene un almacenamiento persistente. Cuando
se trabaja con archivos, se opera con las rutas de acceso de directorios, almacenamiento en disco y nombres de
archivo y de directorio. En cambio, una secuencia es una sucesión de bytes que se puede utilizar para leer y
escribir en una memoria auxiliar, que puede ser uno de los distintos tipos de medios de almacenamiento (por
ejemplo, discos o memoria). Al igual que hay varios tipos de memorias auxiliares distintas de los discos, existen
varios tipos de secuencias distintas de las secuencias de archivo, como las secuencias de red, de memoria y de
canalización.
Archivos y directorios
Se pueden utilizar los tipos del espacio de nombres System.IO para interactuar con archivos y directorios. Por
ejemplo, se pueden obtener y establecer las propiedades de los archivos y directorios, y recuperar colecciones de
archivos y de directorios basándose en criterios de búsqueda.
Para obtener las convenciones de nomenclatura de las rutas de acceso y las maneras de expresar una ruta de
acceso de archivo para los sistemas Windows, incluidos con la sintaxis de dispositivo DOS compatible en .NET
Core 1.1 y versiones posteriores, y .NET Framework 4.6.2 y versiones posteriores, vea Formatos de ruta de acceso
de archivo en los sistemas Windows.
Estas son algunas clases de archivo y directorio de uso general:
File: proporciona métodos estáticos para crear, copiar, eliminar, mover y abrir archivos, y ayuda a crear un
objeto FileStream.
FileInfo: proporciona métodos de instancia para crear, copiar, eliminar, mover y abrir archivos, y ayuda a
crear un objeto FileStream.
Directory: proporciona métodos estáticos para crear, mover y enumerar directorios y subdirectorios.
DirectoryInfo: proporciona métodos de instancia para crear, mover y enumerar directorios y
subdirectorios.
Path: proporciona métodos y propiedades para procesar cadenas de directorio entre plataformas.
Siempre debe proporcionar un control de excepciones sólido al llamar a métodos del sistema de archivos. Para
más información, vea Control de errores de E/S.
Además de usar estas clases, los usuarios de Visual Basic pueden usar los métodos y propiedades
proporcionados por la clase Microsoft.VisualBasic.FileIO.FileSystem para la E/S de archivos.
Vea Cómo: Copiar directorios, Cómo: Crear una lista de directorios y Cómo: Enumerar directorios y archivos.
Secuencias
La clase base abstracta Stream es compatible con bytes de lectura y escritura. Todas las clases que representan
secuencias heredan de la clase Stream. La clase Stream y sus clases derivadas proporcionan una visión genérica
de los repositorios y los orígenes de datos, y evitan que el programador tenga que ocuparse de los detalles
específicos del sistema operativo y los dispositivos subyacentes.
Las secuencias comprenden tres operaciones fundamentales:
Lectura: transferencia de datos desde una secuencia a una estructura de datos como, por ejemplo, una
matriz de bytes.
Escritura: transferencia de datos a una secuencia desde un origen de datos.
Búsqueda: consulta y modificación de la posición actual en una secuencia.
Dependiendo del repositorio o el origen de datos subyacente, una secuencia puede admitir solo algunas de estas
características. Por ejemplo, la clase PipeStream no admite operaciones de búsqueda. Las propiedades CanRead,
CanWrite y CanSeek de una secuencia especifican las operaciones que admite.
Estas son algunas de las clases de secuencias de uso general:
FileStream: para leer y escribir en un archivo.
IsolatedStorageFileStream: para leer y escribir en un archivo en almacenamiento aislado.
MemoryStream: para leer y escribir en la memoria como una memoria auxiliar.
BufferedStream: para mejorar el rendimiento de las operaciones de lectura y escritura.
NetworkStream: para leer y escribir sobre los sockets de red.
PipeStream: para leer y escribir sobre canalizaciones anónimas y con nombre.
CryptoStream: para vincular secuencias de datos con transformaciones criptográficas.
Para obtener un ejemplo de cómo trabajar con flujos de forma asincrónica, vea E/S de archivos asincrónica.
Lectores y escritores
El espacio de nombres System.IO también proporciona tipos para leer los caracteres codificados de las
secuencias y escribirlos en ellas. Normalmente, las secuencias están diseñadas para la entrada y salida de bytes.
Los tipos de lectura y escritura controlan la conversión de caracteres codificados en bytes y a la inversa, para que
la secuencia pueda completar la operación. Cada clase de lectura y escritura se asocia a una secuencia, que se
puede recuperar mediante la propiedad BaseStream de la clase.
Estas son algunas clases de lectura y escritura de uso general:
BinaryReader y BinaryWriter: para leer y escribir tipos de datos primitivos como valores binarios.
StreamReader y StreamWriter: para leer y escribir caracteres utilizando un valor de codificación para
convertir los caracteres en bytes y a la inversa.
StringReader y StringWriter: para leer y escribir caracteres en cadenas.
TextReader y TextWriter: sirven como clases base abstractas para otros lectores y escritores que leen y
escriben caracteres y cadenas, pero no datos binarios.
Vea Cómo: Leer texto de un archivo, Cómo: Escribir texto en un archivo, Cómo: Leer caracteres de una cadena y
Cómo: Escribir caracteres en una cadena.
Compresión
La compresión se refiere al proceso de reducir el tamaño de un archivo para su almacenamiento. La
descompresión es el proceso de extraer el contenido de un archivo comprimido para que esté en un formato
utilizable. El espacio de nombres System.IO.Compression contiene tipos para comprimir y descomprimir archivos
y secuencias.
Las clases siguientes se utilizan con frecuencia al comprimir y descomprimir archivos y secuencias:
ZipArchive: para crear y recuperar entradas en el archivo zip.
ZipArchiveEntry: para representar un archivo comprimido.
ZipFile: para crear, extraer y abrir un paquete comprimido.
ZipFileExtensions: para crear y extraer entradas en un paquete comprimido.
DeflateStream: para comprimir y descomprimir secuencias utilizando el algoritmo de deflación (Deflate).
GZipStream: para comprimir y descomprimir secuencias con formato de datos gzip.
Vea Cómo: Comprimir y extraer archivos.
Almacenamiento aislado
El almacenamiento aislado es un mecanismo de almacenamiento de datos que proporciona aislamiento y
seguridad mediante la definición de modos estándar de asociar código a los datos guardados. El
almacenamiento proporciona un sistema de archivos virtual que está aislado para cada usuario, ensamblado y
(opcionalmente) dominio. El almacenamiento aislado es especialmente útil cuando la aplicación no tiene permiso
para obtener acceso a los archivos del usuario. Se pueden guardar los valores o los archivos de la aplicación de
una forma controlada por la directiva de seguridad del equipo.
El almacenamiento aislado no está disponible para aplicaciones de la Tienda Windows 8.x, sino que deben usarse
las clases de datos de aplicaciones del espacio de nombres Windows.Storage. Para más información, vea Datos
de la aplicación.
Las clases siguientes se utilizan con frecuencia al implementar el almacenamiento aislado:
IsolatedStorage: proporciona la clase base para las implementaciones de almacenamiento aislado.
IsolatedStorageFile: proporciona un área de almacenamiento aislado que contiene archivos y directorios.
IsolatedStorageFileStream: expone un archivo dentro del almacenamiento aislado.
Vea Almacenamiento aislado.
E/S y seguridad
Cuando se utilizan las clases del espacio de nombres System.IO, se deben seguir los requisitos de seguridad del
sistema operativo como las listas de control de acceso (ACL) para controlar el acceso a los archivos y directorios.
Este requisito es adicional a cualquier requisito FileIOPermission. Se pueden administrar las listas de control de
acceso mediante programación. Para obtener más información, vea Cómo: Agregar o quitar entradas de la lista
de control de acceso.
Las políticas de seguridad predeterminadas impiden que las aplicaciones de intranet o Internet obtengan acceso
a los archivos del equipo del usuario. Por lo tanto, no use las clases de E/S que requieren una ruta a un archivo
físico al escribir el código que se descarga a través de Internet o una intranet. En su lugar, use el almacenamiento
aislado para las aplicaciones tradicionales de .NET Framework, o bien datos de aplicaciones para las aplicaciones
de la Tienda Windows 8.x.
Solo se realiza una comprobación de seguridad cuando se construye la secuencia. Por consiguiente, no abra una
secuencia y se la pase al código o a los dominios de aplicación de menos confianza.
Temas relacionados
Tareas de E/S comunes
Proporciona una lista de tareas de E/S asociadas a los archivos, directorios y secuencias, y vínculos al
contenido y los ejemplos pertinentes para cada tarea.
E/S de archivos asincrónica
Describe las ventajas de rendimiento y el funcionamiento básico de la E/S asincrónica.
Almacenamiento aislado
Describe un mecanismo de almacenamiento de datos que proporciona aislamiento y seguridad mediante
la definición de las formas estándar de asociar código a los datos guardados.
Canalizaciones
Describe las operaciones anónimas y de canalización con nombre de .NET Framework.
Archivos asignados a memoria
Describe los archivos asignados a memoria, que incluyen el contenido de archivos en disco en memoria
virtual. Puede usar archivos asignados a memoria para editar archivos muy grandes y crear memoria
compartida para la comunicación entre procesos.
Formatos de ruta de acceso de archivo en los
sistemas Windows
16/09/2020 • 29 minutes to read • Edit Online
Los miembros de muchos de los tipos del espacio de nombres System.IO incluyen un parámetro path que
permite especificar una ruta de acceso absoluta o relativa a un recurso de sistema de archivos. Después, esta ruta
de acceso se pasa a las API del sistema de archivos de Windows. En este tema se describen los formatos de las
rutas de acceso de archivo que se pueden usar en los sistemas Windows.
\Program Files\Custom Utilities\StringFinder.exe Ruta de acceso absoluta desde la raíz de la unidad actual.
Puede determinar si una ruta de acceso de archivo es un nombre completo (es decir, si la ruta de acceso es
independiente del directorio actual y no cambia cuando cambia el directorio actual) mediante una llamada al
método IsPathFullyQualified. Tenga en cuenta que una ruta de acceso de este tipo puede incluir segmentos de
directorio relativos ( . y .. ) y seguir siendo completa si la ruta de acceso resuelta siempre apunta a la misma
ubicación.
En el ejemplo siguiente se muestra la diferencia entre las rutas de acceso absolutas y relativas. Se supone que
existe el directorio D:\FY2018\ y que aún no ha establecido ningún directorio actual para D:\ desde el símbolo
del sistema antes de ejecutar el ejemplo.
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
Directory.SetCurrentDirectory(@"C:\");
string path = Path.GetFullPath(@"D:\FY2018");
Console.WriteLine($"'D:\\FY2018' resolves to {path}");
path = Path.GetFullPath(@"D:FY2018");
Console.WriteLine($"'D:FY2018' resolves to {path}");
path = Path.GetFullPath(@"D:\FY2018");
Console.WriteLine($"'D:\\FY2018' resolves to {path}");
path = Path.GetFullPath(@"D:FY2018");
// This will be "D:\Docs\FY2018" as it happens to match the drive of the current directory
Console.WriteLine($"'D:FY2018' resolves to {path}");
path = Path.GetFullPath(@"D:\FY2018");
Console.WriteLine($"'D:\\FY2018' resolves to {path}");
// This will be either "D:\FY2018" or "D:\FY2018\FY2018" in the subprocess. In the sub process,
// the command prompt set the current directory before launch of our application, which
// sets a hidden environment variable that is considered.
path = Path.GetFullPath(@"D:FY2018");
Console.WriteLine($"'D:FY2018' resolves to {path}");
if (args.Length < 1)
{
Console.WriteLine(@"Launching again, after setting current directory to D:\FY2018");
Uri currentExe = new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase, UriKind.Absolute);
string commandLine = $"/C cd D:\\FY2018 & \"{currentExe.LocalPath}\" stop";
string commandLine = $"/C cd D:\\FY2018 & \"{currentExe.LocalPath}\" stop";
ProcessStartInfo psi = new ProcessStartInfo("cmd", commandLine); ;
Process.Start(psi).WaitForExit();