简体   繁体   中英

Powershell - How-to use $List.convertAll() or alternate C# method to clone a list

Suppose I have a list:

$DeletedUsers = New-Object System.Collections.Generic.List[System.object]

So I can easily add and remove users from the collection.

I want to be able to pass this list to a function that does something, but without modifying the original list, and it must stay of the same generic list type.

convertAll() seems to do exactly what I want without having to script out the creation of a new list myself with foreach-object , but I don't understand how to utilize the overload definitions (or quite understand what they mean).

There are many examples in C#, but I haven't been able to find one that demonstrates it in PoSH.

Example Scenario:

Assume $DeletedUsers contains a list of User objects of PSCustomObject type. With typical "User" properties such as department or Employment status. This list should be be capable of being passed to functions that will change statuses of the users property that can then be added to a separate output list of the same Generic.List type.

Currently any changes by the example function.

    Function ProcessUser {

    [Cmdletbinding()]
    Param($DeletedUsers)

    begin{$DeletedUsersClone = $($DeletedUsers).psobject.copy()} #OR similar

    process{
    $DeletedUsersClone | foreach { $_ | Add-Member -NotePropertyName 
    "Processed" -NotePropertyValue "Processed:00"; $Outputlist.add($_)} 
           }
                         }

Impacts the original $DeletedUsers, erroneously adding processed information to a list that should stay static.

There are alternate ways to prevent this from impacting the ultimate objective of the script, but the question is:

How do I create a True, non-referenced clone of a System.Collections.Generic.List[System.object] using built-in C# methods.

The trick is to use a scriptblock with an explicit cast to the delegate type. This looks like:

$DeletedUsers.ConvertAll([converter[object,object]] {param ($i) <# do convert #> })

Note:

  • As became clear later, the OP is looking for a deep clone of the original list; ie, not only should the list as a whole be cloned, but also its elements .

  • This answer only shows how to create a shallow clone (and how to pass a list read-only).

    • See Bruce Payette's helpful answer for a deep -cloning approach based on the .ConvertAll method ; with [pscustomobject] instances, you'd use the following (but note that .psobject.Copy() only creates shallow copies of the [pscustomobject] instances themselves):

       $DeletedUsers.ConvertAll([converter[pscustomobject, pscustomobject]] {param ($obj) $obj.psobject.copy() }) 

  • If you want to pass a shallow clone of your list to a callee:

    • Pass [Collections.Generic.List[object]]::new($DeletedUsers) (PSv5+ syntax)
    • Alternatively, if the type of the list elements isn't known or if you don't want to repeat it, pass: $DeletedUsers.GetRange(0, $DeletedUsers.Count)
  • If you just want to prevent accidental modification of your list by a callee:

    • Pass $DeletedUsers.AsReadOnly() - however, that does change the type, namely to [Collections.ObjectModel.ReadOnlyCollection[object]]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM