Question [Astuce]Cmt savoir si 1 objet est une collection ?

Plus d'informations
il y a 15 ans 9 mois - il y a 2 ans 11 mois #2498 par Laurent Dardenne
Je viens de passer qq temps à rechercher une solution, voici comment on peut faire :
$Tab=@(1,2)
$I=10
#Est-ce que l'object est une collection ? (On teste si l'objet implémente l'interface IEnumerable)
$Tab -is [System.Collections.IEnumerable]
$I -is [System.Collections.IEnumerable]
On peut donc ensuite effectuer un Foreach sur la collection sans problème.
Cela peut être utile, par exemple, dans un traitement utilisant le pipeline.

L'opérateur -is/-isnot permet d'interroger la classe de l'objet et les interfaces qu'il implémente.

Tutoriels PowerShell
Dernière édition: il y a 2 ans 11 mois par Laurent Dardenne.

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 15 ans 9 mois #2499 par Arnaud Petitjean
Merci Laurent pour ta contribution. Je vais la déplacer dans la section Articles/Tuto.

Arnaud

MVP PowerShell et créateur de ce magnifique forum :-)
Auteur de 6 livres PowerShell aux éditions ENI
Fondateur de la société Start-Scripting
Besoin d'une formation PowerShell ?

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 15 ans 5 mois - il y a 2 ans 11 mois #2880 par Laurent Dardenne
Un petit complément à propos du test d'une interface générique :
 #Test si $Str implémente System.IEquatable<String>
$Str="Test"
$Str -is [System.IEquatable``1[System.String]]
#True
#Test si $Str implémente System.IEquatable<Int32>
$Str -is [System.IEquatable``1[System.Int32]]
#False
#Liste des interfaces supportées par le type de la variable $Str
$Str.GetType().GetInterfaces()
<br><br>Message édité par: Laurent Dardenne, à: 16/11/08 18:57

Tutoriels PowerShell
Dernière édition: il y a 2 ans 11 mois par Laurent Dardenne.

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 14 ans 9 mois - il y a 2 ans 11 mois #4853 par Laurent Dardenne
Comment savoir si une collection est accessible par un index ?
#Collection indexée
[system.array].GetInterfaces()
#Collection indexé
[object[]].GetInterfaces()
#Collection sans index
[System.Management.ManagementObjectCollection].GetInterfaces()
La fonction de test :
function Test-AccessedByIndex($Object)
{ #Renvoi true si $Object est une collection accessible par un index

    if ($object -is [type])
    {
      #Object =[System.Array]
      $Interfaces=$Object.GetInterfaces()
    }
    else
    {
      #Object ="String"
      $Interfaces=$Object.GetType().GetInterfaces()
    }
    #on force le résultat en un tableau
    @($Interfaces|Where-Object {$_.name -match "IList"}).count -gt 0
}

$a=@(1,2,3)
Test-AccessedByIndex $a
#True
Pour tester un type la syntaxe suivante ne fonctionne pas car PS cast le paramètre en un paramètre de type String :
Test-AccessedByIndex [system.Array]
#False
On doit forcer le type ainsi :
Test-AccessedByIndex ([system.Array] -as [type])
#True

#ou
Test-AccessedByIndex ("system.Array" -as [type])
$T="system.Array"
Test-AccessedByIndex ($T -as [type])
Dans la fonction on teste sur les noms d'interfaces car une classe peut implémenter soit l'interface IList, soit l'interface IList génerique ( IList<T> ), ou les deux.
Dans le cas de l'interface générique on ne peut pas l'utiliser avec le paramètre -is car il nécessite de préciser le type complet ( [System.IList``1[System.String]] ).
Mais comme on ne connait pas le type utilisé lors de la construction de la collection à tester, bien qu'on puisse le rechercher via le système de réflexion de dotnet, on testera donc sur le nom de l'interface et pas sur son type, ce qui simplifie le code :
[System.array],[object[]],[System.Management.ManagementObjectCollection]|
% { "Le type {0} est-il une collection indexée ? {1}" -F $_.Name, (Test-AccessedByIndex $_)}
# Le type Array est-il une collection indexée ? True
# Le type Object[] est-il une collection indexée? True
# Le type ManagementObjectCollection est-il une collection indexée ? False

Tutoriels PowerShell
Dernière édition: il y a 2 ans 11 mois par Laurent Dardenne.

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 2 ans 11 mois #30834 par Laurent Dardenne
Une fonction permettant de vérifier si un objet implémente l'interface générique IEnumerable et ce sans avoir à tester un type fermé :
${Function:Test-GenericIEnumerable}=.{
    #On recherche le type ouvert de l'interface générique IEnumerable
    $GenericIEnumerable=[Type]'System.Collections.Generic.IEnumerable`1'

    Return {
     #La variable $InputObject implémente-t-elle l'interface générique IEnumerable ?
      param(
        [ValidateNotNull()]
        $InputObject
      )
        foreach ($Interface in $InputObject.GetType().GetInterfaces())
        {
           if ($Interface.IsGenericType)
           {
                 #On suppose une seule implémentation de l'interface générique IEnumerable (https://stackoverflow.com/a/7852650)
               if ($Interface.GetGenericTypeDefinition() -eq $GenericIEnumerable)
               {return $true}
           }
        }
        return $false
    }.GetNewClosure()
}
On crée une closure pour ne pas avoir à reconstruire le type genrique ouvert.
L'usage d'un module simplifierait ce code :
$script:GenericIEnumerable=[Type]'System.Collections.Generic.IEnumerable`1'

Function Test-GenericIEnumerable{
#La variable $InputObject implémente-t-elle l'interface générique IEnumerable ?
 param(
   [ValidateNotNull()]
  $InputObject
 )

 foreach ($Interface in $InputObject.GetType().GetInterfaces())
 {
    if ($Interface.IsGenericType)
    {
          #On suppose une seule implémentation de l'interface générique IEnumerable (https://stackoverflow.com/a/7852650)
        if ($Interface.GetGenericTypeDefinition() -eq $GenericIEnumerable)
        {return $true}
    }
  }
    return $false
}

Quelques exemples avec les types les plus courant :
[void][Reflection.Assembly]::LoadWithPartialName("System.Data.DataSetExtensions");
$Dt = New-Object System.Data.DataTable
[void]$Dt.Columns.Add( 'C1')
[void]$Dt.Columns.Add( 'C2')
[void]$Dt.Rows.Add( '1','2')
[void]$Dt.Rows.Add( 'Y','Z')

$Dt.Rows -is [System.Collections.IEnumerable]
Test-GenericIEnumerable $Dt.Rows

$A = New-Object System.Collections.ArrayList
$A.Addrange((1..5))
$A -is [System.Collections.IEnumerable]

Test-GenericIEnumerable $A

$H=@{'A'=1;'B'=2}
$H -is [System.Collections.IEnumerable]

Test-GenericIEnumerable $H

#----

$T=1..5
$T -is [System.Collections.IEnumerable]

Test-GenericIEnumerable $T

$SL = new-object 'system.collections.Generic.SortedList[String,Int]'
$SL.Add("Un", 1)
$SL.Add("Deux", 2)
$SL.Add("Trois", 3)

$SL -is [System.Collections.IEnumerable]

Test-GenericIEnumerable $SL

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 2 ans 11 mois #30842 par Laurent Dardenne
Un complément, la fonction 'Test-GenericIEnumerable' recherche à partir d'un objet, mais pas à partir d'un type qui n'implémente pas d'interface.
Pour tester un type on procédera ainsi :
Import-Module ExtensionMethod
 #recherche les méthodes d'extension LINQ génériques ou pas
$t=[Linq.Enumerable].Assembly.ExportedTypes|Find-ExtensionMethod

#On recherche les méthodes d'extension 'Where'
$l=$t|? {$_.IsGenericMethod -and $_.name -eq 'Where'}

#Ici l'élément 2 est la méthode Where envoyant un IEnumerable`1
"$($l[2])"
#System.Collections.Generic.IEnumerable`1[TSource] Where[TSource](System.Collections.Generic.IEnumerable`1[TSource], System.Func`2[TSource,System.Boolean])

#Teste le type et pas l'objet Method
$l[2].ReturnType.GetGenericTypeDefinition() -eq $GenericIEnumerable
#True
On recherche toutes les méthodes d'extension générique renvoyant un IEnumerable`1 (opérateurs LINQ)
$Methods=Foreach ($Method in $t)
{ 
    $ReturnType=$Method.ReturnType
    if ($Method.IsGenericMethod  -and $ReturnType.IsGenericType -and $ReturnType.GetGenericTypeDefinition() -eq $GenericIEnumerable)
    {Write-Output $Method}
}
$Groups=$Methods|Group-Object -Property Name
$Groups[0].Group.foreach({$_.tostring()})
#System.Collections.Generic.IEnumerable`1[TSource] Where[TSource](System.Collections.Generic.IEnumerable`1[TSource], System.Func`2[TSource,System.Boolean])
#System.Collections.Generic.IEnumerable`1[TSource] Where[TSource](System.Collections.Generic.IEnumerable`1[TSource], System.Func`3[TSource,System.Int32,System.Boolean])
$Groups

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Temps de génération de la page : 0.081 secondes
Propulsé par Kunena