Jenis .NET di kelas PowerShell

Oct 23 2020

Saya mencoba untuk memahami praktik terbaik Kelas PowerShell, dan mengalami kebingungan dengan kelas sederhana untuk menangani Tip Balon.

Add-Type -assemblyName:System.Drawing
Add-Type -assemblyName:System.Windows.Forms

class PxMessage {
    static [PxMessage] $instance static $balloon
    static $defaultIcon static [PxMessage] GetInstance($processIcon) {
        if ([PxMessage]::instance -eq $null) { [PxMessage]::instance = [PxMessage]::new() #[PxMessage]::balloon = [Windows.Forms.NotifyIcon]::new() [PxMessage]::balloon = New-Object Windows.Forms.NotifyIcon [PxMessage]::defaultIcon = $processIcon
        }

        return [PxMessage]::instance
    }

    [Void] SendMessage ([String]$title, [String]$message, [String]$messageIcon) { [PxMessage]::balloon.icon = [PxMessage]::defaultIcon [PxMessage]::balloon.balloonTipTitle = $title
        [PxMessage]::balloon.balloonTipText = $message [PxMessage]::balloon.balloonTipIcon = $messageIcon
        [PxMessage]::balloon.visible = $true [PxMessage]::balloon.ShowBalloonTip(0) [PxMessage]::balloon.Dispose } } $processIcon = [System.Drawing.Icon]::ExtractAssociatedIcon($(Get-Process -id:$PID | Select-Object -expandProperty:path))
$message = [PxMessage]::GetInstance($processIcon)

$message.SendMessage('Title', "$(Get-Date)", 'Info')

Saya punya dua pertanyaan:

1: Mengapa [PxMessage]::balloon = New-Object Windows.Forms.NotifyIconberhasil, tetapi [PxMessage]::balloon = [Windows.Forms.NotifyIcon]::new()tidak ( Unable to find typeerror)? Dan apakah ini menunjukkan bahwa penggunaan [Type]::new()belum sepenuhnya didukung, dan demi konsistensi saya lebih baik menggunakan New-Object di mana-mana? Atau setidaknya di mana pun di Kelas saya sendiri?

2: Saya ingin mengetik properti & parameter saya, tetapi saya juga mendapatkan Unable to find typekesalahan saat saya mengetik $balloon& $defaultIconproperti, atau jika saya mengetik $processIconparameter di GetInstancemetode.

Jelas saya bisa mengetik Properties, bahkan dengan tipe saya yang didefinisikan. Jadi apa yang berbeda tentang keduanya [System.Drawing]& [System.Windows.Forms], dan apakah ini bug, atau fitur? Dan apakah ada tipe lain yang berperilaku serupa?

Jawaban

4 MathiasR.Jessen Oct 22 2020 at 22:44

Ini pada dasarnya adalah kondisi balapan!

Saat PowerShell mulai menjalankan file skrip, ini melewati 3 fase:

  • Parsing
  • Kompilasi
  • Eksekusi

Hal pertama yang diproses dalam fase kompilasi , ( bahkan sebelum eksekusi dimulai) adalah:

  • Semua usingpernyataan di atas skrip
  • Definisi tipe apapun - apapun classatau enumkata kunci - dikompilasi secara terpisah

Jadi kesalahan yang terkait dengan menyelesaikan [Windows.Forms.NotifyIcon]tipe literal di dalam definisi kelas sebenarnya dilemparkan sebelum Add-Type -AssemblyName:System.Windows.Forms mendapat kesempatan untuk dijalankan!

Beberapa opsi:

Skrip bertingkat

Tulis skrip pemuat terpisah yang memuat ketergantungan:

# loader.ps1
Add-Type -AssemblyName System.Windows.Forms,System.Drawing
. .\scriptWithTypes.ps1
# scriptWithTypes.ps1
class ClassDependentOnForms
{
  [Windows.Forms.NotifyIcon]$BalloonTipIcon
}

Dengan modul

Dengan modul, sedikit lebih mudah untuk mengelola dependensi sebelum menyusun definisi jenis kustom - cukup tambahkan nama assembly RequiredAssembliesuntuk manifes modul:

New-ModuleManifest ... -RootModule moduleWithTypes.psm1 -RequiredAssemblies System.Windows.Forms,System.Drawing

Muat dari disk dengan using assembly ...

Jika jalur assembly yang diperlukan diketahui, Anda dapat memuatnya pada waktu parse dengan using assemblypernyataan:

using assembly '.\path\to\System.Drawing.dll'
using assembly '.\path\to\System.Windows.Forms.dll'

class FormsDependentClass
{
  [Windows.Forms.NotifyIcon]$BallonTipIcon
}

Untuk kasus penggunaan Anda, yang terakhir ini tidak terlalu menarik karena Anda perlu melakukan hardcode pada assembly alih-alih hanya memuatnya dari GAC dengan nama.


Mengapa ini terjadi?

Perilaku ini mungkin sedikit membingungkan karena semua hal lain di PowerShell hanyalah interpretasi langsung "satu pernyataan pada satu waktu".

Alasan pengecualian ini adalah untuk mengizinkan skrip dan fungsi merangkum jenis parameter khusus:

param(
  [CustomEnumType]$Option ) begin { enum CustomEnumType { None Option1 Option2 } } end { # do something based on $Option
}

Tanpa kompilasi preemptive CustomEnumTypepada waktu parse ini, PowerShell tidak akan dapat menawarkan pelengkapan otomatis dan validasi input untuk -Optionargumen parameter - karena tipenya tidak akan ada