Câu trả lời:
Powershell 7 giới thiệu liên kết null nguyên bản, gán có điều kiện null và toán tử bậc ba trong Powershell.
Null Coalescing
$null ?? 100 # Result is 100
"Evaluated" ?? (Expensive-Operation "Not Evaluated") # Right side here is not evaluated
Chuyển nhượng có điều kiện rỗng
$x = $null
$x ??= 100 # $x is now 100
$x ??= 200 # $x remains 100
Nhà điều hành bậc ba
$true ? "this value returned" : "this expression not evaluated"
$false ? "this expression not evaluated" : "this value returned"
Không cần Tiện ích mở rộng cộng đồng Powershell, bạn có thể sử dụng câu lệnh if Powershell tiêu chuẩn dưới dạng biểu thức:
variable = if (condition) { expr1 } else { expr2 }
Vì vậy, để thay thế cho biểu thức C # đầu tiên của bạn:
var s = myval ?? "new value";
trở thành một trong những điều sau (tùy thuộc vào sở thích):
$s = if ($myval -eq $null) { "new value" } else { $myval }
$s = if ($myval -ne $null) { $myval } else { "new value" }
hoặc tùy thuộc vào những gì $ myval có thể chứa mà bạn có thể sử dụng:
$s = if ($myval) { $myval } else { "new value" }
và biểu thức C # thứ hai ánh xạ theo cách tương tự:
var x = myval == null ? "" : otherval;
trở thành
$x = if ($myval -eq $null) { "" } else { $otherval }
Công bằng mà nói, chúng không phải là rất linh hoạt và không dễ sử dụng như các dạng C #.
Bạn cũng có thể cân nhắc gói nó trong một hàm rất đơn giản để làm cho mọi thứ dễ đọc hơn:
function Coalesce($a, $b) { if ($a -ne $null) { $a } else { $b } }
$s = Coalesce $myval "new value"
hoặc có thể là IfNull:
function IfNull($a, $b, $c) { if ($a -eq $null) { $b } else { $c } }
$s = IfNull $myval "new value" $myval
$x = IfNull $myval "" $otherval
Như bạn có thể thấy, một hàm rất đơn giản có thể cung cấp cho bạn một chút tự do về cú pháp.
CẬP NHẬT: Một tùy chọn bổ sung cần xem xét trong hỗn hợp là một hàm IsTrue chung chung hơn:
function IfTrue($a, $b, $c) { if ($a) { $b } else { $c } }
$x = IfTrue ($myval -eq $null) "" $otherval
Sau đó, kết hợp đó là khả năng của Powershell để khai báo các bí danh trông hơi giống toán tử, bạn kết thúc với:
New-Alias "??" Coalesce
$s = ?? $myval "new value"
New-Alias "?:" IfTrue
$ans = ?: ($q -eq "meaning of life") 42 $otherval
Rõ ràng điều này sẽ không phù hợp với sở thích của mọi người, nhưng có thể là thứ bạn đang tìm kiếm.
Như Thomas lưu ý, một điểm khác biệt tinh tế khác giữa phiên bản C # và phiên bản ở trên là C # thực hiện việc ghi ngắn mạch các đối số, nhưng các phiên bản Powershell liên quan đến các hàm / bí danh sẽ luôn đánh giá tất cả các đối số. Nếu đây là một vấn đề, hãy sử dụng if
biểu mẫu.
The variable '$myval' cannot be retrieved because it has not been set.
.
$myval = $null
trước khi thực hiện kiểm tra, lỗi sẽ biến mất.
$null -ne $a
Hiện tại không thể tìm thấy tài liệu tham khảo phù hợp, nhưng đây là cách làm lâu dài.
PowerShell 7 giới thiệu nhiều tính năng mới và chuyển từ .NET Framework sang .NET Core. Kể từ giữa năm 2020, nó vẫn chưa thay thế hoàn toàn các phiên bản PowerShell kế thừa do sự phụ thuộc vào .NET Core, nhưng Microsoft đã chỉ ra rằng họ dự định dòng Core sẽ thay thế dòng Framework kế thừa. Vào thời điểm bạn đọc thông báo này, một phiên bản PowerShell tương thích có thể được cài đặt sẵn trên hệ thống của bạn; nếu không, hãy xem https://github.com/powershell/powershell .
Theo tài liệu , các toán tử sau được hỗ trợ ngay trong PowerShell 7.0:
??
??=
... ? ... : ...
Những điều này hoạt động như bạn mong đợi đối với liên kết rỗng:
$x = $a ?? $b ?? $c ?? 'default value'
$y ??= 'default value'
Kể từ khi toán tử bậc ba đã được giới thiệu, bây giờ có thể thực hiện điều sau đây, mặc dù không cần thiết khi bổ sung toán tử liên kết null:
$x = $a -eq $null ? $b : $a
Kể từ phiên bản 7.0, các tính năng sau cũng khả dụng nếu PSNullConditionalOperators
tính năng tùy chọn được bật , như được giải thích trong tài liệu ( 1 , 2 ):
?.
?[]
Chúng có một số lưu ý:
${}
nếu theo sau bởi một trong các toán tử thử nghiệm vì dấu chấm hỏi được phép sử dụng trong tên biến. Không rõ liệu điều này có xảy ra hay không nếu / khi các tính năng chuyển sang trạng thái thử nghiệm (xem sự cố # 11379 ). Ví dụ: ${x}?.Test()
sử dụng toán tử mới, nhưng $x?.Test()
chạy Test()
trên một biến có tên$x?
.?(
toán tử như bạn mong đợi nếu bạn đến từ TypeScript. Những điều sau sẽ không hoạt động:$x.Test?()
Các phiên bản PowerShell trước 7 có toán tử kết hợp rỗng thực sự, hoặc ít nhất là toán tử có khả năng thực hiện hành vi đó. Toán tử đó là -ne
:
# Format:
# ($a, $b, $c -ne $null)[0]
($null, 'alpha', 1 -ne $null)[0]
# Output:
alpha
Nó linh hoạt hơn một chút so với toán tử kết hợp null, vì nó tạo ra một mảng tất cả các mục không phải null:
$items = $null, 'alpha', 5, 0, '', @(), $null, $true, $false
$instances = $items -ne $null
[string]::Join(', ', ($instances | ForEach-Object -Process { $_.GetType() }))
# Result:
System.String, System.Int32, System.Int32, System.String, System.Object[],
System.Boolean, System.Boolean
-eq
hoạt động tương tự, rất hữu ích để đếm các mục nhập rỗng:
($null, 'a', $null -eq $null).Length
# Result:
2
Nhưng dù sao, đây là một trường hợp điển hình để phản ánh ??
toán tử của C # :
'Filename: {0}' -f ($filename, 'Unknown' -ne $null)[0] | Write-Output
Giải thích này dựa trên đề xuất chỉnh sửa từ một người dùng ẩn danh. Cảm ơn, dù bạn là ai!
Dựa trên thứ tự của các hoạt động, điều này hoạt động theo thứ tự sau:
,
nhà điều hành tạo ra một mảng các giá trị để được kiểm tra.-ne
nhà điều hành lọc ra bất kỳ mục từ các mảng phù hợp với giá trị nhất định - trong trường hợp này, null. Kết quả là một mảng các giá trị không rỗng theo thứ tự như mảng được tạo ở Bước 1.[0]
được sử dụng để chọn phần tử đầu tiên của mảng được lọc.Đơn giản hóa điều đó:
Không giống như toán tử liên kết rỗng của C #, mọi biểu thức có thể sẽ được đánh giá, vì bước đầu tiên là tạo một mảng.
function DidItRun($a) { Write-Host "It ran with $a"; return $a }
và chạy ((DidItRun $null), (DidItRun 'alpha'), 1 -ne $null)[0]
để xem điều này.
,
có mức độ ưu tiên cao như vậy. Đối với bất kỳ ai nhầm lẫn về cách hoạt động của điều này, ($null, 'alpha', 1 -ne $null)[0]
cũng giống như (($null, 'alpha', 1) -ne $null)[0]
. Trên thực tế, hai toán tử "dấu gạch ngang" duy nhất có mức độ ưu tiên cao hơn là -split
và -join
(và dấu gạch ngang đơn vị? Tức là. -4
Hoặc -'56'
).
Đây chỉ là một nửa câu trả lời cho nửa đầu của câu hỏi, vì vậy một phần tư câu trả lời nếu bạn muốn, nhưng có một giải pháp thay thế đơn giản hơn nhiều cho toán tử liên kết null miễn là giá trị mặc định bạn muốn sử dụng thực sự là giá trị mặc định cho loại :
string s = myval ?? "";
Có thể được viết trong Powershell là:
([string]myval)
Hoặc là
int d = myval ?? 0;
dịch sang Powershell:
([int]myval)
Tôi thấy điều đầu tiên hữu ích khi xử lý một phần tử xml có thể không tồn tại và nếu nó tồn tại có thể có khoảng trắng không mong muốn bao quanh nó:
$name = ([string]$row.td[0]).Trim()
Việc ép kiểu thành chuỗi bảo vệ khỏi phần tử là null và ngăn ngừa mọi rủi ro Trim()
thất bại.
([string]$null)
-> ""
có vẻ như một "hữu ích, nhưng không may tác dụng phụ" :(
$null, $null, 3 | Select -First 1
trả lại
3
Nếu bạn cài đặt Mô-đun Tiện ích mở rộng Cộng đồng Powershell thì bạn có thể sử dụng:
?? là bí danh của Invoke-NullCoalescing.
$s = ?? {$myval} {"New Value"}
?: là bí danh của Invoke-Ternary.
$x = ?: {$myval -eq $null} {""} {$otherval}
Cuối cùng, PowerShell 7 có các toán tử gán Null Coalescing !
PS > $null ?? "a"
a
PS > "x" ?? "y"
x
PS > $x = $null
PS > $x ??= "abc"
PS > $x
abc
PS > $x ??= "xyz"
PS > $x
abc
Thường thì tôi thấy rằng tôi cũng cần phải coi chuỗi rỗng là null khi sử dụng kết hợp. Cuối cùng tôi đã viết một hàm cho việc này, sử dụng giải pháp của Zenexer để liên kết với null đơn giản, và sau đó sử dụng Keith Hill để kiểm tra null hoặc rỗng, và thêm nó làm cờ để hàm của tôi có thể thực hiện cả hai.
Một trong những ưu điểm của hàm này là nó cũng xử lý việc có tất cả các phần tử null (hoặc rỗng) mà không đưa ra một ngoại lệ nào. Nó cũng có thể được sử dụng cho nhiều biến đầu vào tùy ý, nhờ vào cách PowerShell xử lý đầu vào mảng.
function Coalesce([string[]] $StringsToLookThrough, [switch]$EmptyStringAsNull) {
if ($EmptyStringAsNull.IsPresent) {
return ($StringsToLookThrough | Where-Object { $_ } | Select-Object -first 1)
} else {
return (($StringsToLookThrough -ne $null) | Select-Object -first 1)
}
}
Điều này tạo ra các kết quả thử nghiệm sau:
Null coallesce tests:
1 (w/o flag) - empty/null/'end' :
1 (with flag) - empty/null/'end' : end
2 (w/o flag) - empty/null :
2 (with flag) - empty/null :
3 (w/o flag) - empty/null/$false/'end' :
3 (with flag) - empty/null/$false/'end' : False
4 (w/o flag) - empty/null/"$false"/'end' :
4 (with flag) - empty/null/"$false"/'end' : False
5 (w/o flag) - empty/'false'/null/"$false"/'end':
5 (with flag) - empty/'false'/null/"$false"/'end': false
Mã kiểm tra:
Write-Host "Null coalesce tests:"
Write-Host "1 (w/o flag) - empty/null/'end' :" (Coalesce '', $null, 'end')
Write-Host "1 (with flag) - empty/null/'end' :" (Coalesce '', $null, 'end' -EmptyStringAsNull)
Write-Host "2 (w/o flag) - empty/null :" (Coalesce('', $null))
Write-Host "2 (with flag) - empty/null :" (Coalesce('', $null) -EmptyStringAsNull)
Write-Host "3 (w/o flag) - empty/null/`$false/'end' :" (Coalesce '', $null, $false, 'end')
Write-Host "3 (with flag) - empty/null/`$false/'end' :" (Coalesce '', $null, $false, 'end' -EmptyStringAsNull)
Write-Host "4 (w/o flag) - empty/null/`"`$false`"/'end' :" (Coalesce '', $null, "$false", 'end')
Write-Host "4 (with flag) - empty/null/`"`$false`"/'end' :" (Coalesce '', $null, "$false", 'end' -EmptyStringAsNull)
Write-Host "5 (w/o flag) - empty/'false'/null/`"`$false`"/'end':" (Coalesce '', 'false', $null, "$false", 'end')
Write-Host "5 (with flag) - empty/'false'/null/`"`$false`"/'end':" (Coalesce '', 'false', $null, "$false", 'end' -EmptyStringAsNull)
Gần nhất tôi có thể nhận được là: $Val = $MyVal |?? "Default Value"
Tôi đã triển khai toán tử liên kết null cho phần trên như thế này:
function NullCoalesc {
param (
[Parameter(ValueFromPipeline=$true)]$Value,
[Parameter(Position=0)]$Default
)
if ($Value) { $Value } else { $Default }
}
Set-Alias -Name "??" -Value NullCoalesc
Các nhà điều hành ternary có điều kiện có thể được thực hiện một cách similary.
function ConditionalTernary {
param (
[Parameter(ValueFromPipeline=$true)]$Value,
[Parameter(Position=0)]$First,
[Parameter(Position=1)]$Second
)
if ($Value) { $First } else { $Second }
}
Set-Alias -Name "?:" -Value ConditionalTernary
Và được sử dụng như: $Val = $MyVal |?: $MyVal "Default Value"