Umm. no había pensado hacer eso. en verdad podría ser mas optimo , ya que con la solución que encontré tarda mucho.
La metodología propuesta por el compañero NEBIRE de obtener una colección con todos los tipos de archivos e ir filtrando según la extensión, tiene sus ventajas pero también sus desventajas, resultando así en una solución óptima en muchos casos, y en otros no. Todo depende del escenario / circunstancias donde vayas a aplicar esa (o cualquier otra) solución.
la solución que encontré tarda mucho.
Si el rendimiento (en general, como la velocidad de ejecución) es un factor de importancia para ti, entonces deberías utilizar un tipo de evaluación vaga (o Lazy Evaluation por su nombre en Inglés) como sería la interfáz IEnumerable, y hacer uso de la programación asincrónica o del paralelismo.
Voy a compartir contigo un pequeño extracto del código fuente de mi framework comercial ElektroKit (el cual se puede encontrar en mi firma de usuario del foro). Este código es una implementación personal (bueno, es un wrapper mejorado de la función Directory.GetFiles() ) de un buscador de archivos y directorios, aunque solo he dejado visible dos métodos en el código fuente para que puedas buscar archivos.
Aparte de los grandes beneficios en velocidad que podrás comprobar por ti miso en los tests aquí abajo del todo de este comentario, este buscador también soporta la característica de buscar por múltiples patrones de nombre y por múltiples patrones de extensiones al mismo tiempo, así como la capacidad de poder excluir sin miedo algunos cualquier directorio en el que el usuario no disponga de permisos de lectura (con la posibilidad adicional de lanzar una excepción de acceso no autorizado si así se desea hacer), cosa que con las funciones built-in de .NET Framework no se puede controlar de forma natural.
Simplemente copiar y pegar. Aquí tienes:
Código
' *********************************************************************** ' Author : Elektro ' Modified : 14-November-2015 ' *********************************************************************** #Region " Option Statements " Option Strict On Option Explicit On Option Infer Off #End Region #Region " Imports " Imports System.Collections.Concurrent Imports System.IO Imports System.Linq Imports System.Security Imports System.Threading.Tasks #End Region #Region " ElektroKit.Core.IO.Tools.Files " Namespace ElektroKit.Core.IO.Tools ''' ---------------------------------------------------------------------------------------------------- ''' <summary> ''' Contains file related utilities. ''' </summary> ''' ---------------------------------------------------------------------------------------------------- #Region " Constructors " ''' ---------------------------------------------------------------------------------------------------- ''' <summary> ''' Prevents a default instance of the <see cref="Files"/> class from being created. ''' </summary> ''' ---------------------------------------------------------------------------------------------------- <DebuggerNonUserCode> Private Sub New() End Sub #End Region #Region " Public Methods " ''' ---------------------------------------------------------------------------------------------------- ''' <summary> ''' Gets the files those matches the criteria inside the specified directory and/or sub-directories. ''' </summary> ''' ---------------------------------------------------------------------------------------------------- ''' <param name="dirPath"> ''' The root directory path to search for files. ''' </param> ''' ''' <param name="searchOption"> ''' The searching mode. ''' </param> ''' ''' <param name="fileNamePatterns"> ''' The file name pattern(s) to match. ''' </param> ''' ''' <param name="fileExtPatterns"> ''' The file extension pattern(s) to match. ''' </param> ''' ''' <param name="ignoreCase"> ''' If <see langword="True"/>, ignores the comparing case of <paramref name="fileNamePatterns"/> and <paramref name="fileExtPatterns"/> patterns. ''' </param> ''' ''' <param name="throwOnError"> ''' If set to <see langword="True"/>, exceptions will be thrown, like access denied to file or directory. ''' </param> ''' ---------------------------------------------------------------------------------------------------- ''' <returns> ''' An <see cref="IEnumerable(Of FileInfo)"/> instance containing the files information. ''' </returns> ''' ---------------------------------------------------------------------------------------------------- ''' <exception cref="ArgumentException">dirPath or searchOption ''' </exception> ''' ---------------------------------------------------------------------------------------------------- <DebuggerStepThrough> Friend Shared Function GetFiles(ByVal dirPath As String, ByVal searchOption As SearchOption, Optional ByVal fileNamePatterns As String() = Nothing, Optional ByVal fileExtPatterns As String() = Nothing, Optional ByVal ignoreCase As Boolean = True, Optional ByVal throwOnError As Boolean = False) As IEnumerable(Of FileInfo) ' Analyze and resolve path problems. (eg. 'C:' -> 'C:\') ' Analyze the passed arguments. ' Get and return the files. Dim queue As New ConcurrentQueue(Of FileInfo) Files.CollectFiles(queue, dirPath, searchOption, fileNamePatterns, fileExtPatterns, ignoreCase, throwOnError) Return queue.AsEnumerable End Function ''' ---------------------------------------------------------------------------------------------------- ''' <summary> ''' Gets the filepaths those matches the criteria inside the specified directory and/or sub-directories. ''' </summary> ''' ---------------------------------------------------------------------------------------------------- ''' <param name="dirPath"> ''' The root directory path to search for files. ''' </param> ''' ''' <param name="searchOption"> ''' The searching mode. ''' </param> ''' ''' <param name="fileNamePatterns"> ''' The file name pattern(s) to match. ''' </param> ''' ''' <param name="fileExtPatterns"> ''' The file extension pattern(s) to match. ''' </param> ''' ''' <param name="ignoreCase"> ''' If <see langword="True"/>, ignores the comparing case of <paramref name="fileNamePatterns"/> and <paramref name="fileExtPatterns"/> patterns. ''' </param> ''' ''' <param name="throwOnError"> ''' If set to <see langword="True"/>, exceptions will be thrown, like access denied to file or directory. ''' </param> ''' ---------------------------------------------------------------------------------------------------- ''' <returns> ''' An <see cref="IEnumerable(Of String)"/> instance containing the filepaths. ''' </returns> ''' ---------------------------------------------------------------------------------------------------- ''' <exception cref="ArgumentException">dirPath or searchOption ''' </exception> ''' ---------------------------------------------------------------------------------------------------- <DebuggerStepThrough> Friend Shared Function GetFilePaths(ByVal dirPath As String, ByVal searchOption As SearchOption, Optional ByVal fileNamePatterns As String() = Nothing, Optional ByVal fileExtPatterns As String() = Nothing, Optional ByVal ignoreCase As Boolean = True, Optional ByVal throwOnError As Boolean = False) As IEnumerable(Of String) ' Analyze and resolve path problems. (eg. 'C:' -> 'C:\') ' Analyze the passed arguments. ' Get and return the filepaths. Dim queue As New ConcurrentQueue(Of String) Files.CollectFilePaths(queue, dirPath, searchOption, fileNamePatterns, fileExtPatterns, ignoreCase, throwOnError) Return queue.AsEnumerable End Function #End Region #Region " Private Methods " ''' ---------------------------------------------------------------------------------------------------- ''' <summary> ''' Analyzes a directory path and perform specific changes on it. ''' </summary> ''' ---------------------------------------------------------------------------------------------------- ''' <param name="refDirPath"> ''' The directory path. ''' </param> ''' ---------------------------------------------------------------------------------------------------- ''' <exception cref="ArgumentNullException">dirPath;Value is null, empty, or white-spaced. ''' </exception> ''' ---------------------------------------------------------------------------------------------------- <DebuggerStepThrough> Private Shared Sub AnalyzePath(ByRef refDirPath As String) If String.IsNullOrEmpty(refDirPath) OrElse String.IsNullOrWhiteSpace(refDirPath) Then Throw New ArgumentNullException("dirPath", "Value is null, empty, or white-spaced.") Else ' Trim unwanted characters. refDirPath = refDirPath.TrimStart({" "c}).TrimEnd({" "c}) If Path.IsPathRooted(refDirPath) Then ' The root paths contained on the returned FileInfo objects will start with the same string-case as this root path. ' So just for a little visual improvement, I'll treat this root path as a Drive-Letter and I convert it to UpperCase. refDirPath = Char.ToUpper(refDirPath.First()) & refDirPath.Substring(1) End If If Not refDirPath.EndsWith("\"c) Then ' Possibly its a drive letter without backslash ('C:') or else just a normal path without backslash ('C\Dir'). ' In any case, fix the ending backslash. refDirPath = refDirPath.Insert(refDirPath.Length, "\"c) End If End If End Sub ''' ---------------------------------------------------------------------------------------------------- ''' <summary> ''' Analyzes the specified directory values. ''' </summary> ''' ---------------------------------------------------------------------------------------------------- ''' <param name="dirPath"> ''' The root directory path to search for files. ''' </param> ''' ''' <param name="searchOption"> ''' The searching mode. ''' </param> ''' ---------------------------------------------------------------------------------------------------- ''' <exception cref="ArgumentException">dirPath or searchOption ''' </exception> ''' ---------------------------------------------------------------------------------------------------- <DebuggerStepThrough> Private Shared Sub AnalyzeArgs(ByVal dirPath As String, ByVal searchOption As SearchOption) If Not Directory.Exists(dirPath) Then Throw New ArgumentException(String.Format("Directory doesn't exists: '{0}'", dirPath), "dirPath") ElseIf (searchOption <> SearchOption.TopDirectoryOnly) AndAlso (searchOption <> SearchOption.AllDirectories) Then Throw New ArgumentException(String.Format("Value of '{0}' is not valid enumeration value.", CStr(searchOption)), "searchOption") End If End Sub ''' ---------------------------------------------------------------------------------------------------- ''' <summary> ''' Tries to instance the by-reference <see cref="DirectoryInfo"/> object using the given directory path. ''' </summary> ''' ---------------------------------------------------------------------------------------------------- ''' <param name="dirPath"> ''' The directory path used to instance the by-reference <see cref="DirectoryInfo"/> object. ''' </param> ''' ''' <param name="refDirInfo"> ''' The by-reference <see cref="DirectoryInfo"/> object to instance it using the given directory path. ''' </param> ''' ''' <param name="throwOnError"> ''' If set to <see langword="True"/>, exceptions will be thrown, like access denied to directory. ''' </param> ''' ---------------------------------------------------------------------------------------------------- <DebuggerStepThrough> Private Shared Sub SetupDirInfoObject(ByVal dirPath As String, ByRef refDirInfo As DirectoryInfo, ByVal throwOnError As Boolean) Try refDirInfo = New DirectoryInfo(dirPath) Catch ex As Exception Select Case ex.GetType ' Handle or suppress exceptions by its type, ' I've wrote different types just to feel free to extend this feature in the future. Case GetType(ArgumentNullException), GetType(ArgumentException), GetType(SecurityException), GetType(PathTooLongException), ex.GetType If throwOnError Then Throw End If End Select End Try End Sub ''' ---------------------------------------------------------------------------------------------------- ''' <summary> ''' Tries to instance the by-reference <paramref name="refCol"/> object using the given directory path. ''' </summary> ''' ---------------------------------------------------------------------------------------------------- ''' <typeparam name="A"> ''' The type of the <paramref name="refCol"/> object used to cast and fill the by-reference collection. ''' </typeparam> ''' ''' <param name="objectAction"> ''' The method to invoke, only for <see cref="FileInfo"/> or <see cref="DirectoryInfo"/> objects, this parameter can be <see langword="Nothing"/>. ''' </param> ''' ''' <param name="sharedAction"> ''' The method to invoke, only for filepaths or directorypaths, this parameter can be <see langword="Nothing"/>. ''' </param> ''' ''' <param name="dirPath"> ''' The directory path used to instance the by-reference <paramref name="refCol"/> object. ''' </param> ''' ''' <param name="searchPattern"> ''' The search pattern to list files or directories. ''' </param> ''' ''' <param name="refCol"> ''' The by-reference <see cref="IEnumerable(Of A)"/> object to instance it using the given directory path. ''' </param> ''' ''' <param name="throwOnError"> ''' If set to <see langword="True"/>, exceptions will be thrown, like access denied to file or directory. ''' </param> ''' ---------------------------------------------------------------------------------------------------- <DebuggerStepThrough> Private Shared Sub SetupFileDirCollection(Of A)(ByVal objectAction As Func(Of String, SearchOption, IEnumerable(Of A)), ByVal sharedAction As Func(Of String, String, SearchOption, IEnumerable(Of A)), ByVal dirPath As String, ByVal searchPattern As String, ByRef refCol As IEnumerable(Of A), ByVal throwOnError As Boolean) Try If objectAction IsNot Nothing Then refCol = objectAction.Invoke(searchPattern, SearchOption.TopDirectoryOnly) ElseIf sharedAction IsNot Nothing Then refCol = sharedAction.Invoke(dirPath, searchPattern, SearchOption.TopDirectoryOnly) Else Throw New ArgumentException("Any Action has been defined.") End If Catch ex As Exception Select Case ex.GetType ' Handle or suppress exceptions by its type, ' I've wrote different types just to feel free to extend this feature in the future. Case GetType(UnauthorizedAccessException), GetType(DirectoryNotFoundException), ex.GetType If throwOnError Then Throw End If End Select End Try End Sub ''' ---------------------------------------------------------------------------------------------------- ''' <summary> ''' Determines whether at least one of the specified patterns matches the given value. ''' </summary> ''' ---------------------------------------------------------------------------------------------------- ''' <param name="value"> ''' The value, which can be a filename, file extension, direcrory path, or directory name. ''' </param> ''' ''' <param name="patterns"> ''' The patterns to match the given value. ''' </param> ''' ''' <param name="ignoreCase"> ''' If set to <see langword="True"/>, compares ignoring string-case rules. ''' </param> ''' ---------------------------------------------------------------------------------------------------- ''' <returns> ''' <see langword="True"/> at least one of the specified patterns matches the given value; <see langword="False"/> otherwise. ''' </returns> ''' ---------------------------------------------------------------------------------------------------- <DebuggerStepThrough> Private Shared Function IsMatchPattern(ByVal value As String, ByVal patterns As IEnumerable(Of String), ByVal ignoreCase As Boolean) As Boolean ' Iterate the filename pattern(s) to match each name pattern on the current name. For Each pattern As String In patterns ' Supress consecuent conditionals if pattern its an asterisk. If pattern.Equals("*", StringComparison.OrdinalIgnoreCase) Then Return True ElseIf ignoreCase Then ' Compare name ignoring string-case rules. If value.ToLower Like pattern.ToLower Then Return True End If Else ' Compare filename unignoring string-case rules. If value Like pattern Then Return True End If End If ' ignoreCase Next pattern Return False End Function ''' <summary> ''' Runs the next collector tasks synchronouslly. ''' </summary> <DebuggerStepThrough> Private Shared Sub RunNextTasks(Of T)(ByVal action As Action(Of ConcurrentQueue(Of T), String, SearchOption, IEnumerable(Of String), IEnumerable(Of String), Boolean, Boolean), ByVal queue As ConcurrentQueue(Of T), ByVal dirPath As String, ByVal firstPatterns As IEnumerable(Of String), ByVal secondPatterns As IEnumerable(Of String), ByVal ignoreCase As Boolean, ByVal throwOnError As Boolean) Try Task.WaitAll(New DirectoryInfo(dirPath). GetDirectories. Select(Function(dir As DirectoryInfo) Return Task.Factory.StartNew(Sub() action.Invoke(queue, dir.FullName, SearchOption.AllDirectories, firstPatterns, secondPatterns, ignoreCase, throwOnError) End Sub) End Function).ToArray) Catch ex As Exception Select Case ex.GetType ' Handle or suppress exceptions by its type, ' I've wrote different types just to feel free to extend this feature in the future. Case GetType(UnauthorizedAccessException), GetType(DirectoryNotFoundException), ex.GetType If throwOnError Then Throw End If End Select End Try End Sub ''' <summary> ''' Collects the files those matches the criteria inside the specified directory and/or sub-directories. ''' </summary> <DebuggerStepThrough> Private Shared Sub CollectFiles(ByVal queue As ConcurrentQueue(Of FileInfo), ByVal dirPath As String, ByVal searchOption As SearchOption, ByVal fileNamePatterns As IEnumerable(Of String), ByVal fileExtPatterns As IEnumerable(Of String), ByVal ignoreCase As Boolean, ByVal throwOnError As Boolean) ' Initialize a FileInfo collection. Dim fileInfoCol As IEnumerable(Of FileInfo) = Nothing ' Initialize a DirectoryInfo. Dim dirInfo As DirectoryInfo = Nothing SetupDirInfoObject(dirPath, dirInfo, throwOnError) If fileExtPatterns IsNot Nothing Then ' Decrease time execution by searching for files that has extension. SetupFileDirCollection(Of FileInfo)(AddressOf dirInfo.GetFiles, Nothing, dirInfo.FullName, "*.*", fileInfoCol, throwOnError) Else ' Search for all files. SetupFileDirCollection(Of FileInfo)(AddressOf dirInfo.GetFiles, Nothing, dirInfo.FullName, "*", fileInfoCol, throwOnError) End If ' If the fileInfoCol collection is not empty then... If fileInfoCol IsNot Nothing Then ' Iterate the files. For Each fInfo As FileInfo In fileInfoCol ' Flag to determine whether a filename pattern is matched. Activated by default. Dim flagNamePattern As Boolean = True ' Flag to determine whether a file extension pattern is matched. Activated by default. Dim flagExtPattern As Boolean = True ' If filename patterns collection is not empty then... If fileNamePatterns IsNot Nothing Then flagNamePattern = IsMatchPattern(fInfo.Name, fileNamePatterns, ignoreCase) End If ' If file extension patterns collection is not empty then... If fileExtPatterns IsNot Nothing Then flagExtPattern = IsMatchPattern(fInfo.Extension, fileExtPatterns, ignoreCase) End If ' If fileName and also fileExtension patterns are matched then... If flagNamePattern AndAlso flagExtPattern Then queue.Enqueue(fInfo) ' Enqueue this FileInfo object. End If Next fInfo End If ' fileInfoCol IsNot Nothing ' If searchOption is recursive then... If searchOption = SearchOption.AllDirectories Then RunNextTasks(Of FileInfo)(AddressOf CollectFiles, queue, dirInfo.FullName, fileNamePatterns, fileExtPatterns, ignoreCase, throwOnError) End If End Sub ''' <summary> ''' Collects the filepaths those matches the criteria inside the specified directory and/or sub-directories. ''' </summary> <DebuggerStepThrough> Private Shared Sub CollectFilePaths(ByVal queue As ConcurrentQueue(Of String), ByVal dirPath As String, ByVal searchOption As SearchOption, ByVal fileNamePatterns As IEnumerable(Of String), ByVal fileExtPatterns As IEnumerable(Of String), ByVal ignoreCase As Boolean, ByVal throwOnError As Boolean) ' Initialize a filepath collection. Dim filePathCol As IEnumerable(Of String) = Nothing If fileExtPatterns IsNot Nothing Then ' Decrease time execution by searching for files that has extension. SetupFileDirCollection(Of String)(Nothing, AddressOf Directory.GetFiles, dirPath, "*.*", filePathCol, throwOnError) Else ' Search for all files. SetupFileDirCollection(Of String)(Nothing, AddressOf Directory.GetFiles, dirPath, "*", filePathCol, throwOnError) End If ' If the filepath collection is not empty then... If filePathCol IsNot Nothing Then ' Iterate the filepaths. For Each filePath As String In filePathCol ' Flag to determine whether a filename pattern is matched. Activated by default. Dim flagNamePattern As Boolean = True ' Flag to determine whether a file extension pattern is matched. Activated by default. Dim flagExtPattern As Boolean = True ' If filename patterns collection is not empty then... If fileNamePatterns IsNot Nothing Then flagNamePattern = IsMatchPattern(Path.GetFileNameWithoutExtension(filePath), fileNamePatterns, ignoreCase) End If ' If file extension patterns collection is not empty then... If fileExtPatterns IsNot Nothing Then flagExtPattern = IsMatchPattern(Path.GetExtension(filePath), fileExtPatterns, ignoreCase) End If ' If fileName and also fileExtension patterns are matched then... If flagNamePattern AndAlso flagExtPattern Then queue.Enqueue(filePath) ' Enqueue this filepath. End If Next filePath End If ' filePathCol IsNot Nothing ' If searchOption is recursive then... If searchOption = SearchOption.AllDirectories Then RunNextTasks(Of String)(AddressOf CollectFilePaths, queue, dirPath, fileNamePatterns, fileExtPatterns, ignoreCase, throwOnError) End If End Sub #End Region End Class End Namespace #End Region
Puedes testear la velocidad de mi función y compararlo con las funciones Directory.GetFiles() y My.Computer.FileSystem.GetFiles() mediante el siguiente código:
Código
Imports System.Collections.ObjectModel Imports System.IO Module Module1 Sub Main() Dim dirPath As String = "C:\" Dim methodDirectoryGetFiles As String() Dim methodMyComputerGetFiles As ReadOnlyCollection(Of String) Dim methodElektroGetFiles As IEnumerable(Of String) Dim sw As New Stopwatch() sw.Start() methodDirectoryGetFiles = Directory.GetFiles(dirPath, "*", IO.SearchOption.AllDirectories) sw.Stop() Debug.WriteLine(String.Format("method DirectoryGetFiles - Elapsed Time: {0}", sw.Elapsed.ToString("mm\:ss\:ffff"))) sw.Reset() sw.Start() methodMyComputerGetFiles = My.Computer.FileSystem.GetFiles(dirPath, FileIO.SearchOption.SearchAllSubDirectories, "*") sw.Stop() Debug.WriteLine(String.Format("method MyComputerGetFiles - Elapsed Time: {0}", sw.Elapsed.ToString("mm\:ss\:ffff"))) sw.Reset() sw.Start() methodElektroGetFiles = ElektroKit.Core.IO.Tools.Files.GetFilePaths(dirPath, IO.SearchOption.AllDirectories, Nothing, {"*"}, True, True) sw.Stop() Debug.WriteLine(String.Format("method ElektroGetFiles - Elapsed Time: {0}", sw.Elapsed.ToString("mm\:ss\:ffff"))) End Sub End Module
Aquí tienes unos tests que he realizado:
Código:
Directorio: P:\
Patrón de búsqueda: "*"
Búsqueda recursiva: Sí
Cantidad de archivos a analizar: 142.880 y 7.374 carpetas
Tamaño total de los archivos: 238 Gigabytes
Tamaño por Colección/Array: 142.880 elementos
------------------------------------------------------------
method DirectoryGetFiles - Elapsed Time: 00:01:4564 ++
method MyComputerGetFiles - Elapsed Time: 02:59:5497 +++
method ElektroGetFiles - Elapsed Time: 00:00:5751 +
Código:
Directorio: G:\
Patrón de búsqueda: "*"
Búsqueda recursiva: Sí
Cantidad de archivos a analizar: 373.384 y 28.721 carpetas
Tamaño total de los archivos: 1.58 Terabytes
Tamaño por Colección/Array: 373.384 elementos
------------------------------------------------------------
method DirectoryGetFiles - Elapsed Time: 00:04:5291 ++
method MyComputerGetFiles - Elapsed Time: 24:00:5846 +++ (si, eso han sido 24 ***** minutos los que ha tardado)
method ElektroGetFiles - Elapsed Time: 00:01:7399 +
Código:
Directorio: C:\Windows\System32
Patrón de búsqueda: "*.dll"
Búsqueda recursiva: No
Cantidad de archivos a analizar: 3.465
Tamaño de archivos: 1.60 Gigabytes
Tamaño por Colección/Array: 2.620 elementos
------------------------------------------------------------
method DirectoryGetFiles - Elapsed Time: 00:00:0138 +
method MyComputerGetFiles - Elapsed Time: 00:00:0608 +++
method ElektroGetFiles - Elapsed Time: 00:00:0151 ++
Como puedes comprobar, mi implementación es altamente velóz cuanto mayor es la cantidad de archivos a analizar, y es casi igual de eficiente a la función Directory.GetFiles() cuando se trata de una cantidad pequeña de archivos a analizar, con una diferencia ínfima de milisegundos... lo que hace que merezca mucho la pena usarlo si necesitamos buscar varias extensiones de archivo al mismo tiempo.
Bueno, y de la función My.Computer.FileSystem.GetFiles mejor ni hablemos, los resultados hablan por si mismos. Los miembros expuestos en "My.Computer" al igual que en Microsoft.VisualBasic están muy poco optimizados y ningún programador de VB.NET al que le preocupe el rendimiento de su app debería usarlos. De hecho en el foro he repetido mil veces que los miembros del namespace Microsoft.VisualBasic solo existen por temas de compatibilidad y conformidad por gente que migre de VB6 a VB.NET y lo tengan más facil para orientarse, que ningún programador de VB.NET debería utilizar el namespace Microsoft.VisualBasic más que para mostrar un MsgBox y listo xD, se creerán que hablo por hablar o algo... pues bueno, pueden ver por ustedes mismos el nefasto código fuente en la referencia online del source de .NET Framework.
PD: Estos tests se han realizado bajo circunstancias óptimas, realizando dos veces consecutivas cada llamada a cada uno de los tres métodos para que la caché de archivos enumerados del disco se actualizase y evitar así diferencias dispares en el tiempo de ejecución entre cada método.
Un saludo.