This question was originally (and reasonably) closed as a duplicate of the following questions:
However @mklement0 and others provide a lot of details in comments in this question which deserve to be captured in a more visible and collected answer, though they may be repeated somewhat in mklement0's answers to the other questions. As I (OP) am the learner in this situation, feel free to edit the answer/provide corrections.
The core of this question (and the linked duplicates) revolves around the technical differences between $null and "automation null" ([System.Management.Automation.Internal.AutomationNull]::Value), a.k.a. "the enumerable null".
To answer the direct questions in the OP:
What is the difference between $a and $c in this situation?
$a is $null, while $c is automation null. Automation null is returned via a pipeline that has no results, which can be achieved in different ways, as demonstrated by the different questions:
Get-Process | Where-Object { $_.Name -eq "oiawmij3209j23oifnoeicn" }1,2,3,4 | ? { $_ -ge 5 }& { }Why does $c.PSObject.Copy() work when $a.PSObject.Copy() doesn't?
$c, as automation null is technically a [PSObject] with a PSObject property, while $a is just $null.$c.PSObject can be accessed is arguably a bug, see further context below.Why does $c.PSObject.Copy() work when $c.GetType() doesn't?
$c.$c is effectively being evaluated to $null, and is thus equivalent to $null.GetType().What causes this? Is it something to do with Where-Object, or to do with PSObject.Copy(), or something else?
Where-Object returns no results.How can I/should I reliably differentiate between/test for these cases?
[PSObject], and so detecting it, and differentiating it from $null can be achieved with ($null -eq $value) -and ($value -is [PSObject]).($null -eq $value) -and (@($value).length -eq 0), or ($null -eq $value) -and (@($value).count -eq 0).
0, while that of @($null) will be 1 (for reasons someone more experienced would have to explain).Further context:
$null in expressions, but acts differently in enumeration contexts such as the pipeline, where it acts like an empty collection and therefore enumerates nothing.(& {}).psobject and (& {}) -is [psobject] is arguably a bug; a leaky implementation of an abstraction which reveals the fact that it is technically an object (of type [psobject] a.k.a. [System.Management.Automation.PSObject], namely the aforementioned [System.Management.Automation.Internal.AutomationNull]::Value singleton).$value -is [System.Management.Automation.Null].