Articles

ForEach y Where métodos mágicos

ForEach y Where son dos conceptos de uso frecuente que han estado disponibles en PowerShell desde que salió la versión 1 en 2006. ForEach ha estado disponible como una instrucción y un cmdlet (ForEach-Object), lo que le permite recorrer una colección de objetos y realizar alguna acción una vez para cada objeto de esa colección. Where ha estado disponible como cmdlet (Where-Object), lo que permite filtrar los elementos de una colección que no pasan alguna condición, una condición que puede evaluarse utilizando las propiedades de los objetos de la colección o los propios objetos que pertenecen a la colección. La capacidad ForEach de actuar sobre elementos de una colección y la capacidad Where de filtrar una colección son características muy útiles que se incluyen de una forma u otra en la lógica de muchos scripts, comandos y módulos de PowerShell, independientemente de la versión de PowerShell que se esté utilizando. De hecho, son tan utilizados que han sido un área de enfoque para mejorar el rendimiento, la funcionalidad y la sintaxis en las versiones 3.0 y 4.0 de PowerShell.

Con el lanzamiento de Windows PowerShell 4.0, se introdujeron dos nuevos métodos «mágicos» para tipos de colecciones que proporcionan una nueva sintaxis para acceder a las capacidades ForEach y Where en Windows PowerShell. Estos métodos se denominan acertadamente ForEach y Where. Llamo a estos métodos «mágicos» porque son bastante mágicos en la forma en que funcionan en PowerShell. No aparecen en la salida Get-Member, incluso si aplicas-Force y solicitas-MemberType All. Si te arremangas y profundizas en la reflexión, puedes encontrarlos; sin embargo, requiere una búsqueda amplia porque son métodos de extensión privados implementados en una clase privada. Sin embargo, aunque no se pueden descubrir sin echar un vistazo debajo de las fundas, están ahí cuando las necesita, son más rápidas que sus contrapartes anteriores e incluyen funciones que no estaban disponibles en sus contrapartes anteriores, de ahí la sensación «mágica» que le dejan cuando las usa en PowerShell. Desafortunadamente, estos métodos siguen sin ser documentados incluso hoy, casi un año después de su publicación, por lo que muchas personas no se dan cuenta del poder que están disponibles en estos métodos. Este artículo intentará corregir esto explicando dónde se pueden usar y cómo funcionan para que pueda aprovechar esta magia cuando use PowerShell.

Una nota sobre PowerShell 3.0

Antes de empezar a explicar cómo funcionan los métodos ForEach y Dónde funcionan, tengo que mencionar algo con respecto a estos dos métodos y PowerShell 3.0. Si bien es cierto que los métodos ForEach y Where solo estaban disponibles en PowerShell 4.0 y versiones posteriores, PowerShell 3.0 sigue siendo muy utilizado en muchos entornos, y a menos que esté utilizando PowerShell en un entorno que se ha estandarizado en PowerShell 4.0 y versiones posteriores, es posible que desee aprovechar la sintaxis proporcionada por los nuevos métodos al usar PowerShell 3.0. Sentí que esta era una limitación que merecía la pena abordar, por lo que, como parte del módulo TypePx que publiqué recientemente en GitHub y en la Galería de recursos de PowerShell (también conocido como el repositorio público de PowerShellGet, actualmente en vista previa limitada), incluí métodos de script ForEach y Where que son funcionalmente equivalentes a los métodos introducidos en PowerShell 4.0 para que pueda aprovechar la nueva sintaxis y funcionalidad incluso si está utilizando PowerShell 3.0. Hay algunas deficiencias en esta implementación, que destacaré más adelante en este artículo.

El método ForEach

ForEach es un método que le permite iterar rápidamente a través de una colección de objetos y realizar alguna acción en cada objeto de esa colección. Este método proporciona un rendimiento más rápido que sus homólogos anteriores (la instrucción foreach y el cmdlet ForEach-Object), y también simplifica algunas de las acciones más comunes que puede desear realizar en los objetos de la colección. Todos los objetos que se generan con este método se devuelven en una colección genérica de tipo System.Colecciones.Modelo de objetos.Colección1.

Hay seis formas compatibles de invocar este método, y cada una de ellas se explicará con mayor detalle a continuación. Los argumentos soportados que se pueden usar al invocar el método ForEach son los siguientes:

  • ForEach(bloque script expresión)
  • ForEach(tipo convertToType)
  • ForEach(string propertyName)
  • ForEach(string propertyName, objeto newValue)
  • ForEach(string methodName)
  • ForEach(string methodName, objeto de argumentos)
  • ForEach(bloque script expresión, objeto de argumentos)

tenga en cuenta que estos son compatibles argumento de emparejamientos, no diferentes sobrecargas disponible para el método ForEach. El uso de emparejamientos de argumentos distintos de estos puede resultar en errores que no identifican claramente cuál es el problema real.

ForEach(expresión de bloque de scripts) y ForEach(expresión de bloque de scripts, argumentos de objeto)

Si pasa una expresión de bloque de script al método ForEach, podrá realizar el mismo tipo de tareas que haría en un bloque de script que utilizaría con la instrucción foreach o el cmdlet ForEach-Object. Además, al igual que el cmdlet ForEach-Object, las variables $_ y PS PSItem hacen referencia al elemento actual que se está procesando. Los argumentos que proporcione más allá del argumento del bloque de script inicial se utilizarán como argumentos para el bloque de script. Así es como funciona el parámetro-ArgumentList en el cmdlet ForEach-Object. Aquí hay un ejemplo que demuestra cómo puede usar esto para ejecutar un bloque de script en cada elemento de una colección:

# Get a set of services$services = Get-Service c*# Display the names and display names of all services in the collection$services.foreach{"$($_.Name) ($($_.DisplayName))"}# Select a property name to expand using a script block argument$services.foreach({param($PropertyName); $_.$PropertyName}, 'DisplayName')

Puede que haya notado algo extraño en esta sintaxis, porque no envolví el bloque de script entre corchetes. Puede envolverlo entre corchetes, aunque es opcional en PowerShell 4.0 o posterior, ya que el analizador sintáctico de PowerShell se ha mejorado para permitir que se omitan los corchetes cada vez que invoque un método que acepte un único argumento de bloque de script. Además, al igual que la instrucción foreach y el cmdlet ForEach-Object, el bloque de script que proporcione se invoca en el ámbito actual. Esto significa que cualquier asignación de variables que realice dentro de ese bloque de script persistirá después de que el método ForEach haya terminado de ejecutarse.

ForEach (type convertToType)

Exclusivo del método ForEach, puede pasar un tipo al método ForEach si desea convertir todos los elementos de una colección en otro tipo. Por ejemplo, imagine que tiene una colección de objetos y desea convertir esos objetos en su equivalente de cadena. Esto es lo que se vería con el método ForEach:

# Get a collection of processes$processes = Get-Process# Convert the objects in that collection into their string equivalent$processes.foreach()

Podría haber realizado la misma tarea encasillando la colección en una matriz de cadenas de tipo (por ejemplo,] processes processes), y encasillar la matriz es de hecho significativamente más rápido, sin embargo, es muy probable que ni siquiera notara la diferencia en el tiempo de ejecución a menos que estuviera trabajando con una colección muy, muy grande. A pesar de la diferencia horaria, tenderé a preferir la sintaxis del método ForEach en ciertas situaciones si me permite mantener la elegancia en la implementación evitando corchetes redondos adicionales en los scripts que escribo.

ForEach (nombre de propiedad de cadena)

En PowerShell 3.0 y versiones posteriores, se agregó un segundo conjunto de parámetros a ForEach-Object para que pueda recuperar más fácilmente el valor de una propiedad específica simplemente pasando un nombre de propiedad como único valor de parámetro para ForEach-Object. Esta comodidad también se ha ofrecido en el método ForEach. Este es un ejemplo que demuestra cómo puede iterar a través de una colección y devolver una propiedad de esa colección:

# Get all services whose name starts with "w"$services = Get-Service w*# Return the names of those services$services.foreach('Name')

Por supuesto, desde la versión 3.0 de PowerShell, simplemente puede invocar Power services.Name para obtener los nombres de todos los servicios, y eso se completará más rápido que la alternativa del método ForEach (aunque solo notará la diferencia de rendimiento en colecciones muy grandes del orden de cientos de miles de objetos); sin embargo, eso solo funciona para propiedades que no están en la colección en sí, y es una sintaxis con la que algunos scripters no se sienten cómodos debido a la naturaleza implícita de lo que hace el comando. La nueva sintaxis del método ForEach le proporciona una alternativa más explícita que tiene el beneficio adicional de ser un poco más auto-documentada también.

ForEach(string propertyName, object newValue)

No solo puede recuperar una propiedad en una colección de objetos, también puede establecer una propiedad en una colección de objetos. Esta es una funcionalidad que no está disponible en los otros foreach, a menos que cree explícitamente el bloque de script para hacerlo. Para establecer la propiedad, simplemente proporcione el nombre de la propiedad y el valor que desea usar al establecer esa propiedad, de la siguiente manera:

# Note, this is not a realistic example# This would be used more commonly on configuration data$services = Get-Service c*# Now change the display names of every service to some new value$services.foreach('DisplayName','Hello')

Al igual que las asignaciones que haría con el operador equals, PowerShell intentará convertir lo que proporcione como nuevo valor en el tipo apropiado para la propiedad asignada.

ForEach (nombre de método de cadena) y ForEach(nombre de método de cadena, argumentos de objeto)

Para invocar un método, simplemente proporcione el nombre del método como primer argumento y, a continuación, los argumentos para ese método como segundo, tercero, cuarto, etc. argumento. Si el método no toma ningún argumento, simplemente puede pasar el nombre del método y se invocará sin ningún argumento. Aquí hay un ejemplo que muestra cómo podría matar un montón de procesos que ejecutan un programa específico:

# Get all processes running Chrome$processes = Get-Process -Name Chrome# Now kill all of those processes$processes.foreach('Kill')

Este es otro ejemplo, esta vez usando un método con argumentos mientras muestra cómo puede verificar que los comandos están siguiendo las mejores prácticas mediante el uso de nombres y alias apropiados para los parámetros de uso común:

# Get all commands that have a ComputerName parameter$cmds = Get-Command -ParameterName ComputerName# Now show a table making sure the parameter names and aliases are consistent$cmds.foreach('ResolveParameter','ComputerName') | Format-Table Name,Aliases

Como puede ver en esos resultados, definitivamente hay algunas inconsistencias en la implementación de los parámetros de nombre de computadora que deben corregirse.

Que cubre toda la funcionalidad que está disponible actualmente en el método ForEach. Como puede ver, no hay muchas funcionalidades nuevas ofrecidas en este método, pero las mejoras de sintaxis cuando está realizando una tarea simple en una colección de objetos son agradables, y las mejoras de rendimiento del método ForEach en comparación con la instrucción foreach equivalente para la canalización de objetos ForEach son definitivamente una mejora bienvenida también. Con esa explicación fuera del camino, pasemos al método Where.

El método where

Donde es un método que permite filtrar una colección de objetos. Se parece mucho al cmdlet Where-Object, pero el método Where también es como Select-Object y Group-Object, e incluye varias características adicionales que el cmdlet Where-Object no admite de forma nativa por sí solo. Este método proporciona un rendimiento más rápido que Where-Object en un comando simple y elegante. Al igual que el método ForEach, todos los objetos que se generan con este método se devuelven en una colección genérica de tipo System.Colecciones.Modelo de objetos.Colección 1.

Solo hay una versión de este método, que se puede describir de la siguiente manera:

Where(scriptblock expression])

Como se indica entre corchetes, el bloque de script de expresión es obligatorio y el argumento enumeración de modo y el argumento entero numberToReturn son opcionales, por lo que puede invocar este método utilizando 1, 2 o 3 argumentos. Si desea utilizar un argumento en particular, debe proporcionar todos los argumentos a la izquierda de ese argumento (es decir, si desea proporcionar un valor para numberToReturn, también debe proporcionar valores para modo y expresión).

Where (expresión de bloque de scripts)

La invocación más básica del método Where simplemente toma una expresión de bloque de script como argumento. La expresión de bloque de script se evaluará una vez para cada objeto de la colección que se esté procesando, y si devuelve true, el objeto se devolverá mediante el método Where. Este es el equivalente funcional de llamar al cmdlet Where-Object y pasarle un bloque de script. Al igual que el cmdlet Where-Object, las variables $_ y PS PSItem se pueden usar para hacer referencia al elemento actual que se está procesando dentro del bloque de script.

Aquí hay un ejemplo muy simple, que muestra cómo puede obtener una lista de servicios en ejecución.

# Get all services$services = Get-Service# Now filter out any services that are not running$services.where{$_.Status -eq 'Running'}

Esto no ofrece ninguna funcionalidad nueva, pero ofrece un rendimiento mucho más rápido que Where-Object y la sintaxis es bastante fácil de seguir, por lo que realmente debe considerar esto para sus scripts cuando realice el filtrado del lado del cliente de las colecciones que ha almacenado en una variable.

Where (expresión scriptblock, modo WhereOperatorSelectionMode)

Cuando empiezas a mirar los parámetros opcionales para el método Where, las cosas empiezan a ponerse mucho más interesantes. La versión 4.0 de Windows PowerShell incluía una nueva enumeración con un nombre de tipo de sistema.Gestión.Automatización.Donde el modo de selección del operador. Nota: el sufijo de que typename: «SelectionMode». Se utiliza para proporcionar potentes capacidades de selección en una sintaxis Where. Estos son los valores incluidos en esta enumeración, junto con sus definiciones:

Default Filtre la colección utilizando el bloque de script de expresión, hasta un número máximo si se proporcionó uno o recolección si no se proporcionó un recuento máximo en numberToReturn.
First Devuelve los primeros N objetos que pasan el filtro de bloque de script de expresión, con un valor predeterminado de solo 1 objeto si no se solicitó un recuento específico en numberToReturn.
Last Devuelve los últimos N objetos que pasan el filtro de bloque de script de expresión, con un valor predeterminado de solo 1 objeto si no se solicitó un recuento específico en numberToReturn.
SkipUntil Omita objetos de la colección hasta que un objeto pase el filtro de bloque de script de expresión, y luego devuelve los primeros N objetos, de forma predeterminada a todos los objetos restantes si no se proporcionó un recuento máximo en numberToReturn.
Hasta Devuelve los primeros N objetos de una colección hasta que un objeto pasa el filtro de bloque de script de expresión, de forma predeterminada a todos los objetos que conducen al primer objeto que pasó si no se proporcionó un recuento máximo en numberToReturn.
Dividir Dividir una colección en dos, colocando todos los objetos que pasan el filtro de bloque de script de expresión en la primera colección hasta un número máximo si se proporcionó uno en numberToReturn, o todos los objetos que pasan si no se proporcionó un número máximo, y colocando todos los demás objetos que no se colocan en la primera colección en la segunda colección.

Cada uno de estos ofrece un valor único cuando está procesando colecciones de datos, por lo que proporcionaré más detalles de cada modo de selección a continuación.

Default

No es sorprendente que el valor predeterminado del argumento mode sea ‘Default’. El modo de selección predeterminado ofrece la misma funcionalidad que se obtiene cuando no se proporciona un modo de selección en absoluto. Por ejemplo, podríamos haber escrito la última línea de nuestro ejemplo anterior de esta manera:

# Now filter out any services that are not running$services.where({$_.Status -eq 'Running'},'Default')

En ese ejemplo, el argumento extra no es necesario, porque hace exactamente lo mismo que haría si no proporcionaras el argumento. También puede proporcionar el número máximo de objetos que desea devolver mientras usa el modo de selección predeterminado utilizando el argumento numberToReturn, como este:

# Get the first 10 services in our collection that are running$services.where({$_.Status -eq 'Running'},'Default',10)

Es importante tener en cuenta que la funcionalidad exacta también está disponible cuando se usa el Primer modo de selección (del que hablaremos en un momento), por lo que realmente no es práctico usar ninguno de los parámetros opcionales cuando se usa el modo de selección predeterminado.

First

Como puede haber adivinado, el modo de Primera selección le permite seleccionar los primeros objetos de la colección que pasan el filtro de expresión de bloque de script. Cuando se usa Primero sin un valor para el argumento numberToReturn, o cuando se usa primero con un valor de 0 para el argumento numberToReturn, solo se devolverá el primer objeto que pase el filtro. Opcionalmente, puede especificar cuántos objetos devolver en el argumento numberToReturn, en cuyo caso se devolverán tantos objetos (suponiendo que haya tantos objetos que pasen el filtro).

Estos son algunos ejemplos de uso de nuestra colección de servicios que muestran el Primer modo de selección en acción:

# Get the first service in our collection that is running$services.where({$_.Status -eq 'Running'},'First')# Get the first service in our collection that is running$services.where({$_.Status -eq 'Running'},'First',1)# Get the first 10 services in our collection that are running$services.where({$_.Status -eq 'Running'},'First',10)

Tenga en cuenta que el segundo comando de estos ejemplos devuelve los mismos resultados que el primer comando porque simplemente pasa explícitamente el valor predeterminado del argumento numberToReturn cuando se usa el Primer modo de selección.

Last

El Último modo de selección funciona de manera similar al Primer modo de selección, lo que le permite seleccionar los últimos objetos de la colección que pasan el filtro de expresión de bloque de script. Cuando utilice Last sin un valor para el argumento numberToReturn, o cuando utilice Last con un valor de 0 para el argumento numberToReturn, solo se devolverá el último objeto que pase el filtro. Opcionalmente, puede especificar cuántos objetos devolver en el argumento numberToReturn, en cuyo caso se devolverán tantos objetos (suponiendo que haya tantos objetos que pasen el filtro).

Estos son algunos ejemplos de uso de nuestra colección de servicios que muestran el Último modo de selección en acción:

# Get the last service in our collection that is running$services.where({$_.Status -eq 'Running'},'Last')# Get the last service in our collection that is running$services.where({$_.Status -eq 'Running'},'Last',1)# Get the last 10 services in our collection that are running$services.where({$_.Status -eq 'Running'},'Last',10)

También al igual que los ejemplos del Primer modo de selección, el segundo comando de estos ejemplos devuelve los mismos resultados que el primer comando porque simplemente pasa explícitamente el valor predeterminado del argumento numberToReturn cuando se usa el último modo de selección.

SkipUntil

El modo de selección de SkipUntil le permite omitir todos los objetos de una colección hasta que encuentre uno que pase el filtro de expresión de bloque de script. Una vez que encuentre un objeto que pase el filtro, el modo SkipUntil devolverá todos los objetos restantes en la colección si no se proporcionó ningún valor o se proporcionó un valor de 0 al argumento numberToReturn, o devolverá los primeros N objetos restantes en la colección si se proporcionó un valor mayor que cero al argumento numberToReturn. En ambos casos, los resultados incluirán el primer objeto que pasó el filtro.

Estos son algunos ejemplos que utilizan un subconjunto de nuestra colección de servicios para mostrar el modo de selección SkipUntil en acción:

# Get a collection of services whose name starts with "c"$services = Get-Service c*# Skip all services until we find one with a status of "Running"$services.where({$_.Status -eq 'Running'},'SkipUntil')# Skip all services until we find one with a status of "Running", then# return the first 2$services.where({$_.Status -eq 'Running'},'SkipUntil',2)

Hasta

El modo de selección Hasta proporciona la funcionalidad opuesta al modo de selección SkipUntil. Le permite devolver objetos de una colección hasta que encuentre uno que pase el filtro de expresión de bloque de script. Una vez que encuentre un objeto que pase el filtro, el método Where detiene el procesamiento de objetos. Si no proporciona un valor para el argumento numberToReturn, o si proporciona un valor de 0, el modo de selección Hasta devolverá todos los objetos de la colección anteriores al primero que pase el filtro de expresión de bloque de script. Si proporciona un valor para el argumento numberToReturn mayor que 0, el modo de selección Hasta devolverá como máximo ese número de objetos, lo que significa que es posible que ni siquiera encuentre un objeto que pase el filtro de expresión de bloque de script.

Estos son algunos ejemplos que utilizan un subconjunto diferente de nuestra colección de servicios para mostrar el modo de selección hasta en acción:

# Get a collection of services whose name starts with "p"$services = Get-Service p*# Return all services until we find one with a status of "Stopped"$services.where({$_.Status -eq 'Stopped'},'Until')# Return the first 2 services unless we find one with a status of# "Stopped" first$services.where({$_.Status -eq 'Stopped'},'Until',2)

Split

El modo de selección dividido es único. En lugar de devolver un subconjunto de la colección con la que comienza en una nueva colección, devuelve una nueva colección que contiene internamente dos colecciones separadas. El contenido de esas colecciones anidadas depende de cómo utilice el modo de selección dividida. Split permite dividir una colección de objetos en dos. De forma predeterminada, si no proporciona un valor para el argumento numberToReturn o si proporciona un valor de 0 para el argumento numberToReturn, Split colocará todos los objetos que pasen el filtro de expresión de bloque de script en la primera colección anidada y todos los demás objetos (los que no pasen el filtro de expresión de bloque de script) en la segunda colección anidada. Si proporciona un valor mayor que 0 para el argumento numberToReturn, split limitará el tamaño de la primera colección a esa cantidad máxima, y todos los objetos restantes de la colección, incluso los que coincidan con el filtro de expresión de bloque de script, se colocarán en la segunda colección.

Aquí hay algunos ejemplos que muestran cómo se puede usar el modo de selección dividida para dividir una colección de objetos de diferentes maneras:

# Get all services$services = Get-Service# Split the services into two groups: Running and not Running$running,$notRunning = $services.Where({$_.Status -eq 'Running'},'Split')# Show the Running services$running# Show the services that are not Running$notRunning# Split the services into the same two groups, but limit the Running group# to a maximum of 10 items$10running,$others = $services.Where({$_.Status -eq 'Running'},'Split',10)# Show the first 10 Running services$10running# Show all other services$others

Como puede ver en este ejemplo, Split es un modo de selección bastante potente, que proporciona una mezcla de filtrado, agrupación y selección en una sola llamada de comando.

Esta colección de modos de selección hace que el método Where sea rápido y elegante, pero al mismo tiempo más potente que el Objeto Where, el Objeto de Grupo y el Objeto Select combinados en una sola canalización. ¿Qué tiene de malo eso?

Deficiencias en los métodos de script ForEach y Where en TypePx

Como mencioné anteriormente en este artículo, he escrito extensiones de tipo para PowerShell 3.0 y posteriores y los empaquetó en un módulo llamado TypePx. TypePx es un módulo de script que se escribió en su totalidad en PowerShell y se ejecuta en PowerShell 3.0 o posterior. Si usa PowerShell 3.0 (y solo si usa PowerShell 3.0), TypePx define métodos de script ForEach y Where que imitan el comportamiento de los métodos ForEach y Where en PowerShell 4.0 y versiones posteriores. Si bien el sistema de tipos extendidos de PowerShell permite imitar este comportamiento, existen algunas deficiencias debido a que la implementación en PowerShell afectó lo lejos que pude llegar con estas extensiones de tipos. En esta sección se describirán algunas de las diferencias y limitaciones que existen en los métodos de script ForEach y Where en TypePx que es posible que desee tener en cuenta si está utilizando PowerShell 3.0.

Los bloques de script invocados desde un método de script se ejecutan en un ámbito secundario

A diferencia de los métodos ForEach y Where implementados como parte de PowerShell 4.0 o posterior, que invocan el bloque de script de expresión en el ámbito actual en el que se llama al método, los métodos ForEach y Where script implementados en PowerShell 3.0 invocan el bloque de script de expresión en un ámbito secundario. Esta es una limitación en PowerShell que ha estado ahí desde el principio (una limitación, debo agregar, que creo que es, con mucho, la mayor deficiencia de PowerShell como lenguaje).

Debido a esta limitación, cualquier variable que asigne dentro de un bloque de script de expresión solo se modificará en el ámbito secundario. Esto tiene implicaciones si el bloque de script de expresión está destinado a actualizar una variable en el ámbito en el que invoca ForEach o Where. Es poco probable que esto cause un problema al usar Where porque no es muy común modificar variables en un bloque de script de expresión Where, pero en los bloques de script ForEach esto puede plantear un problema, por lo que debe tenerlo en cuenta si usa estas extensiones.

Debo señalar que me gustaría eliminar esta limitación por completo, y creo que podré hacerlo, sin embargo, en el momento en que escribí este artículo aún no he implementado una solución para esto.

La mayoría de las colecciones, pero no todas, tendrán métodos ForEach y Where

En PowerShell 4.0 y versiones posteriores, los métodos ForEach y Where están disponibles mágicamente para todos los tipos que implementan ForEumerable, excepto para String, XmlNode y tipos que implementan IDictionary. En PowerShell, el sistema de tipos extendidos no permite crear extensiones para interfaces, solo para tipos. Este es un desafío si desea crear una extensión amplia como ForEach y Where. En la implementación actual de estas extensiones en TypePx, el módulo TypePx encuentra todos los tipos en todos los ensamblados cargados en el dominio de aplicación actual, y para todos los tipos no genéricos que definenerableumerable pero no IDictionary (excluyendo String y XmlNode), además de para todos los tipos genéricos que definenumumerable pero no IDictionary para colecciones genéricas de PSObject, Object, String, Int32 o Int64, se crearán los métodos ForEach y Where script.

Esto cubre un gran número de tipos que en mis propias pruebas han sido suficientes, sin embargo, puede encontrar tipos en los que desea usar estos métodos y no están disponibles. Si ese es el caso, házmelo saber a través de GitHub y veré qué puedo hacer. Esta es también una limitación que me gustaría eliminar, pero necesito más tiempo para investigar cómo implementar la funcionalidad equivalente en un ensamblaje compilado donde pueda definirlo más como se define en PowerShell 4.0 y versiones posteriores.

Los scripts de PowerShell no son tan rápidos como el código compilado

Esto es obvio, pero cuando escribes algo en PowerShell, que es un lenguaje interpretado, no se ejecutará tan rápido como lo haría si escribieras la lógica equivalente en un lenguaje como C#. Este es absolutamente el caso de los métodos de script ForEach y Where, que internamente usan ForEach-Object, Where-Object y pipelining para imitar el comportamiento de los métodos nativos de ForEach y Where. En este caso, la ventaja de tener estos comandos proviene de la elegante sintaxis y funcionalidad que proporcionan, además de poder usarlos en scripts para PowerShell 3.0 y 4.0. Los beneficios de rendimiento de ForEach y Where solo se encuentran en la implementación nativa de PowerShell 4.0.

PowerShell 3.0 requiere corchetes alrededor de todos los parámetros del método

Mencioné en los ejemplos anteriores que podía invocar un método con un solo parámetro de bloque de script literal sin envolver ese bloque de script literal en corchetes adicionales. Esta capacidad solo existe en PowerShell 4.0 o posterior debido a las mejoras que se hicieron al analizador sintáctico en esa versión. En PowerShell 3.0, el analizador sintáctico no admite esto, por lo que siempre se requieren corchetes para que los métodos de script ForEach y Where funcionen con un único parámetro de bloque de script literal en esa versión.

Conclusión

Cuando empecé a intentar imitar el comportamiento de los métodos ForEach y Where magic en PowerShell 3.0, no me di cuenta de cuánta funcionalidad proporcionaban. Profundizar en los detalles técnicos detrás de estos métodos para poder crear las extensiones que quería en TypePx ayudó a descubrir todas las características ocultas en estos métodos muy poderosos, y estoy muy feliz de compartirlas con ustedes en este artículo. Espero que esta información le ayude a aprovechar este nuevo y maravilloso conjunto de características en su trabajo de PowerShell, incluso si sigue utilizando PowerShell 3.0. ¡Feliz guión!