From 730bd476b467a5b635e710ddadb097ed2699b1ab Mon Sep 17 00:00:00 2001 From: "Gregoire L. D." Date: Tue, 12 Aug 2025 15:52:45 +0200 Subject: [PATCH 1/7] Property-less Object Display fix after ETS Caching --- .github/workflows/dependency-review.yml | 2 +- .github/workflows/labels.yml | 2 +- .github/workflows/linux-ci.yml | 22 +++++------ .github/workflows/macos-ci.yml | 16 ++++---- .github/workflows/markdownLink.yml | 4 +- .github/workflows/markdownLinkDaily.yml | 2 +- .github/workflows/scorecards.yml | 4 +- .github/workflows/windows-ci.yml | 14 +++---- .../OutGridView/OutGridViewCommand.cs | 2 +- .../common/FormatViewManager.cs | 39 +++++++++++++------ 10 files changed, 61 insertions(+), 46 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index b85021ab000..1b090f2cab8 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -17,6 +17,6 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout Repository' - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: 'Dependency Review' uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index befb416aeaf..2a3ee69799b 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Check out the repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Verify PR has label starting with 'cl-' id: verify-labels diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index 513db9cc487..ec77ee4d9a9 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -52,7 +52,7 @@ jobs: source: ${{ steps.filter.outputs.source }} steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: persist-credentials: false @@ -69,7 +69,7 @@ jobs: if: ${{ needs.changes.outputs.source == 'true' }} steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 @@ -84,7 +84,7 @@ jobs: runs-on: ubuntu-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: Linux Unelevated CI @@ -101,7 +101,7 @@ jobs: runs-on: ubuntu-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: Linux Elevated CI @@ -118,7 +118,7 @@ jobs: runs-on: ubuntu-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: Linux Unelevated Others @@ -135,7 +135,7 @@ jobs: runs-on: ubuntu-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: Linux Elevated Others @@ -152,7 +152,7 @@ jobs: runs-on: ubuntu-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: Verify xUnit test results @@ -179,7 +179,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: '0' @@ -189,7 +189,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5 + uses: github/codeql-action/init@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.5 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -215,7 +215,7 @@ jobs: shell: pwsh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5 + uses: github/codeql-action/analyze@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.5 ready_to_merge: name: Linux ready to merge @@ -242,7 +242,7 @@ jobs: # runs-on: ubuntu-20.04 # steps: # - name: checkout - # uses: actions/checkout@v4 + # uses: actions/checkout@v5 # with: # fetch-depth: 1000 # - name: Verify xUnit test results diff --git a/.github/workflows/macos-ci.yml b/.github/workflows/macos-ci.yml index 18a684de2d9..172a09250c7 100644 --- a/.github/workflows/macos-ci.yml +++ b/.github/workflows/macos-ci.yml @@ -52,7 +52,7 @@ jobs: source: ${{ steps.filter.outputs.source }} steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Change Detection id: filter @@ -67,7 +67,7 @@ jobs: if: ${{ needs.changes.outputs.source == 'true' }} steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: Build @@ -81,7 +81,7 @@ jobs: runs-on: macos-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: macOS Unelevated CI @@ -98,7 +98,7 @@ jobs: runs-on: macos-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: macOS Elevated CI @@ -115,7 +115,7 @@ jobs: runs-on: macos-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: macOS Unelevated Others @@ -132,7 +132,7 @@ jobs: runs-on: macos-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: macOS Elevated Others @@ -149,7 +149,7 @@ jobs: runs-on: ubuntu-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: Verify xUnit test results @@ -163,7 +163,7 @@ jobs: - macos-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Bootstrap packaging if: success() || failure() run: |- diff --git a/.github/workflows/markdownLink.yml b/.github/workflows/markdownLink.yml index 26ea99cc2ef..dbc5ccf139f 100644 --- a/.github/workflows/markdownLink.yml +++ b/.github/workflows/markdownLink.yml @@ -13,7 +13,7 @@ jobs: if: github.repository_owner == 'PowerShell' steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # v1 with: use-quiet-mode: 'yes' @@ -27,7 +27,7 @@ jobs: statuses: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: # Full git history is needed to get a proper # list of changed files within `super-linter` diff --git a/.github/workflows/markdownLinkDaily.yml b/.github/workflows/markdownLinkDaily.yml index 87cdbdfd122..7f5789ed96d 100644 --- a/.github/workflows/markdownLinkDaily.yml +++ b/.github/workflows/markdownLinkDaily.yml @@ -18,7 +18,7 @@ jobs: if: github.repository == 'PowerShell/PowerShell' steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Check Links uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # v1 with: diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 451944eaee9..fd59c0d0193 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -32,7 +32,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5 + uses: github/codeql-action/upload-sarif@df559355d593797519d70b90fc8edd5db049e7a2 # v3.29.5 with: sarif_file: results.sarif diff --git a/.github/workflows/windows-ci.yml b/.github/workflows/windows-ci.yml index 3aaa19fcdf6..94e1102a31d 100644 --- a/.github/workflows/windows-ci.yml +++ b/.github/workflows/windows-ci.yml @@ -53,7 +53,7 @@ jobs: source: ${{ steps.filter.outputs.source }} steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Change Detection id: filter @@ -68,7 +68,7 @@ jobs: runs-on: windows-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: Build @@ -82,7 +82,7 @@ jobs: runs-on: windows-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: Windows Unelevated CI @@ -99,7 +99,7 @@ jobs: runs-on: windows-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: Windows Elevated CI @@ -116,7 +116,7 @@ jobs: runs-on: windows-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: Windows Unelevated Others @@ -133,7 +133,7 @@ jobs: runs-on: windows-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: Windows Elevated Others @@ -150,7 +150,7 @@ jobs: runs-on: windows-latest steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 1000 - name: Verify xUnit test results diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs index 43e24e8b5b6..bc9fe57f5e7 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs @@ -312,7 +312,7 @@ internal GridHeader(OutGridViewCommand parentCmd) internal static GridHeader ConstructGridHeader(PSObject input, OutGridViewCommand parentCmd) { if (DefaultScalarTypes.IsTypeInList(input.TypeNames) || - !OutOfBandFormatViewManager.HasNonRemotingProperties(input)) + !OutOfBandFormatViewManager.IsPropertyLessObject(input)) { return new ScalarTypeHeader(parentCmd, input); } diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs b/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs index 891dfce6829..7b004bf24c2 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs @@ -465,19 +465,39 @@ private static ViewGenerator SelectViewGeneratorFromProperties(FormatShape shape /// internal static class OutOfBandFormatViewManager { + internal static bool IsPropertyLessObject(PSObject so) + { + List allProperties = AssociationManager.ExpandAll(so); + + //Fast Exit if there is no visible properties + if (allProperties.Count == 0) + { + return true; + } + + // Handling Skipping Remoting Properties + foreach (MshResolvedExpressionParameterAssociation property in allProperties) + { + if (IsNotRemotingProperty(property.ResolvedExpression.ToString())) + { + return false; + } + } + + // If all visible Properties are Remote Properties the object is still considered PropertyLess + return true; + } + private static bool IsNotRemotingProperty(string name) { var isRemotingPropertyName = name.Equals(RemotingConstants.ComputerNameNoteProperty, StringComparison.OrdinalIgnoreCase) || name.Equals(RemotingConstants.ShowComputerNameNoteProperty, StringComparison.OrdinalIgnoreCase) || name.Equals(RemotingConstants.RunspaceIdNoteProperty, StringComparison.OrdinalIgnoreCase) - || name.Equals(RemotingConstants.SourceJobInstanceId, StringComparison.OrdinalIgnoreCase); + || name.Equals(RemotingConstants.SourceJobInstanceId, StringComparison.OrdinalIgnoreCase) + || name.Equals(RemotingConstants.EventObject, StringComparison.OrdinalIgnoreCase); return !isRemotingPropertyName; } - private static readonly MemberNamePredicate NameIsNotRemotingProperty = IsNotRemotingProperty; - - internal static bool HasNonRemotingProperties(PSObject so) => so.GetFirstPropertyOrDefault(NameIsNotRemotingProperty) != null; - internal static FormatEntryData GenerateOutOfBandData(TerminatingErrorContext errorContext, PSPropertyExpressionFactory expressionFactory, TypeInfoDataBase db, PSObject so, int enumerationLimit, bool useToStringFallback, out List errors) { @@ -503,8 +523,9 @@ internal static FormatEntryData GenerateOutOfBandData(TerminatingErrorContext er } else { + // If we have a scalar type, or no visible properties, we fallback to a ToString call if (DefaultScalarTypes.IsTypeInList(typeNames) - || !HasNonRemotingProperties(so)) + || IsPropertyLessObject(so)) { // we force a ToString() on well known types return GenerateOutOfBandObjectAsToString(so); @@ -515,12 +536,6 @@ internal static FormatEntryData GenerateOutOfBandData(TerminatingErrorContext er return null; } - // we must check we have enough properties for a list view - if (new PSPropertyExpression("*").ResolveNames(so).Count == 0) - { - return null; - } - // we do not have a view, we default to list view // process an out of band view as a default outOfBandViewGenerator = new ListViewGenerator(); From 9a55f5b1d9c6fd39f6a0d46ddd3058b61cb0988f Mon Sep 17 00:00:00 2001 From: "Gregoire L. D." Date: Sat, 16 Aug 2025 16:04:22 +0200 Subject: [PATCH 2/7] Added Pester test in appropriate BugFix.Tests.ps1 --- .../engine/Formatting/BugFix.Tests.ps1 | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/powershell/engine/Formatting/BugFix.Tests.ps1 b/test/powershell/engine/Formatting/BugFix.Tests.ps1 index 5be7797134c..bb114b3981b 100644 --- a/test/powershell/engine/Formatting/BugFix.Tests.ps1 +++ b/test/powershell/engine/Formatting/BugFix.Tests.ps1 @@ -46,6 +46,27 @@ Describe "Hidden properties should not be returned by the 'FirstOrDefault' primi $outstring.Trim() | Should -BeLike "*.Hidden2" } + It "Formatting for an object with only hidden property should use 'ToString' after a Get-Member call" { + class Hidden { + hidden $Param = 'Foo' + [String]ToString() { return 'MyString' } + } + + $hiddenObjectOne = [Hidden]::new() + $hiddenObjectOne | Get-Member | Out-Null + $outstring = $hiddenObjectOne | Out-String + $outstring.Trim() | Should -BeExactly "MyString" + + class Hidden2 { + hidden $Param = 'Foo' + } + + $hiddenObjectTwo = [Hidden2]::new() + $hiddenObjectTwo | Get-Member | Out-Null + $outstring = $hiddenObjectTwo | Out-String + $outstring.Trim() | Should -BeLike "*.Hidden2" + } + It 'Formatting for an object with no-hidden property should use the default view' { class Params { $Param = 'Foo' From f5b5801e6db5cc55f38ab8fcfda9b8fe3df483ad Mon Sep 17 00:00:00 2001 From: GregoireLD <48733955+GregoireLD@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:44:42 +0000 Subject: [PATCH 3/7] Update src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs Grammatical fix Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../FormatAndOutput/common/FormatViewManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs b/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs index 7b004bf24c2..15d77aa0121 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs @@ -469,7 +469,7 @@ internal static bool IsPropertyLessObject(PSObject so) { List allProperties = AssociationManager.ExpandAll(so); - //Fast Exit if there is no visible properties + //Fast Exit if there are no visible properties if (allProperties.Count == 0) { return true; From 227f154eeb3ec081df2fb53f6808d76cfebd441e Mon Sep 17 00:00:00 2001 From: GregoireLD <48733955+GregoireLD@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:46:57 +0000 Subject: [PATCH 4/7] Update src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs Capitalization fix Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../FormatAndOutput/common/FormatViewManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs b/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs index 15d77aa0121..69cab2c4e8c 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs @@ -484,7 +484,7 @@ internal static bool IsPropertyLessObject(PSObject so) } } - // If all visible Properties are Remote Properties the object is still considered PropertyLess + // If all visible properties are remote properties the object is still considered propertyless return true; } From 85ac6b7c29f5734f136c5549d3b171d90425a27a Mon Sep 17 00:00:00 2001 From: GregoireLD <48733955+GregoireLD@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:59:34 +0000 Subject: [PATCH 5/7] Typo Space nitpicking --- .../FormatAndOutput/common/FormatViewManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs b/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs index 69cab2c4e8c..a2c6048d3a3 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs @@ -469,7 +469,7 @@ internal static bool IsPropertyLessObject(PSObject so) { List allProperties = AssociationManager.ExpandAll(so); - //Fast Exit if there are no visible properties + // Fast Exit if there are no visible properties if (allProperties.Count == 0) { return true; From 367674b8ec25906ad967e5f1a5df119c504e2a12 Mon Sep 17 00:00:00 2001 From: GregoireLD <48733955+GregoireLD@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:08:38 +0000 Subject: [PATCH 6/7] Update src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs index 9b2e4900010..945b713f7e6 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs @@ -312,7 +312,7 @@ internal GridHeader(OutGridViewCommand parentCmd) internal static GridHeader ConstructGridHeader(PSObject input, OutGridViewCommand parentCmd) { if (DefaultScalarTypes.IsTypeInList(input.TypeNames) || - !OutOfBandFormatViewManager.IsPropertyLessObject(input)) + OutOfBandFormatViewManager.IsPropertyLessObject(input)) { return new ScalarTypeHeader(parentCmd, input); } From 0309ddfb0b1650f3f5f7be8a2b6f7c9868513920 Mon Sep 17 00:00:00 2001 From: GregoireLD <48733955+GregoireLD@users.noreply.github.com> Date: Sat, 22 Nov 2025 15:02:16 +0100 Subject: [PATCH 7/7] Lazy property evaluation Uses a GetEnumerator instead of ExpandAll to mitigate speed loss --- .../common/FormatViewManager.cs | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs b/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs index a2c6048d3a3..8611ef5759e 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs @@ -467,25 +467,32 @@ internal static class OutOfBandFormatViewManager { internal static bool IsPropertyLessObject(PSObject so) { - List allProperties = AssociationManager.ExpandAll(so); + // Using an enumerator to avoid the ExpandAll costs + var propertiesEnumerator = so.Properties.GetEnumerator(); - // Fast Exit if there are no visible properties - if (allProperties.Count == 0) + try { + // Manually enumerate properties - Maximum iterations : 6 (5 Remoting properties + 1) + while (propertiesEnumerator.MoveNext()) + { + var property = propertiesEnumerator.Current; + + // Skip remoting properties + if (IsNotRemotingProperty(property.Name)) + { + // Found a non-remoting property - object is NOT propertyless + return false; + } + } + + // Only remoting properties found (or no properties at all) return true; } - - // Handling Skipping Remoting Properties - foreach (MshResolvedExpressionParameterAssociation property in allProperties) + finally { - if (IsNotRemotingProperty(property.ResolvedExpression.ToString())) - { - return false; - } + // Clean up the enumerator if it's IDisposable + (propertiesEnumerator as IDisposable)?.Dispose(); } - - // If all visible properties are remote properties the object is still considered propertyless - return true; } private static bool IsNotRemotingProperty(string name)