none
先にAdd-Typeで定義した内容が後から実行したAdd-Typeから認識できません。 RRS feed

  • 質問

  • まず、C#で定義したクラスAを次のようにして読み込みます。

    Add-Type -TypeDefinition 'public class A {}'
    

    その後で、このクラスAを継承したクラスBを次のようにして読み込もうとします。

    Add-Type -TypeDefinition 'public class B : A {}'
    

    すると、次のようなエラーが出て読み込めません。

    型または名前空間名 'A' が見つかりませんでした。
    using ディレクティブまたはアセンブリ参照が不足しています。
    

    ユーザー定義のクラスを記述したC#のファイルが複数に分かれているときなどにうまく読み込めず、非常に困っています。

    そこで、現状では

    Add-Type -TypeDefinition 'public class A {}' -OutputAssembly 'a.dll' -OutputType Library
    Add-Type -LiteralPath 'a.dll'
    

    として最初のクラス定義をアセンブリDLLにコンパイルしてから読み込み、後でこれを参照した別のクラス定義が必要になったときは

    Add-Type -TypeDefinition 'public class B : A {}' -OutputAssembly 'b.dll' -OutputType Library -ReferencedAssemblies 'a.dll'
    Add-Type -LiteralPath 'b.dll'
    

    として最初のアセンブリDLLを参照して読み込むという大変面倒な手順をとっています。

    あるいは、C#のコードが最初から複数のファイルとして存在しているときは、

    Add-Type -LiteralPath @( 'a.cs', 'b.cs', 'c.cs' )
    

    として一気に読み込むのですが、どのファイルが今必要なのかを予め見定めておかなければなりません。

    最初に読み込んだ内容を後から自由に参照できる方法を探しています。

    2020年1月14日 15:01

すべての返信

  • PowerShellからC#ソースコードを扱おうとしているからこそ発生している問題です。C#だけで実現するかPowerShellだけで実現し、言語間の依存関係を最小に抑えるよう設計そのものを見直すべきです。
    2020年1月14日 22:03
  • PowerShellからC#ソースコードを扱おうとしているからこそ発生している問題です。C#だけで実現するかPowerShellだけで実現し、言語間の依存関係を最小に抑えるよう設計そのものを見直すべきです。

    ご指摘ありがとうございます。確かに無用なトラブルを減らすにはその方がよいようにも見えます。

    しかし、ある程度Windowsを使い込んでくると面倒な処理の自動化のためにPowerShellとC#の合わせ技が必要な場面も出てきてしまいます。なぜなら、それぞれ一長一短がありPowerShellにはできるがC#にはできないこと、またその逆もあるからです。

    例えば、PowerShellのパイプライン機能を使えば複雑な並列処理もワンラインで書けますが、C#だとマルチスレッドプログラミングのためのそれなりの量のコードが必要になります。一方、ユーザー定義型の使用はC#のクラスや構造体の構文の方が高機能で細かい調整が利きます。さらにPowerShellとC#の両方すなわち.NET Frameworkだけではできないこともあり、Wi32 APIやCOMコンポーネント、あるいはサードパーティーのDLLに頼らなければならない場面もあります。例えば、Windowsのショートカットファイルの取り扱いなどです。この場合はこれらのネイティブライブラリをC#でP/Invokeで参照してPowerShellでこれを呼び出して使用するという手法をとらなければなりません。

    もとい、C#は本格的な大規模アプリの開発に適したコンパイラー言語であり、一方のPowerShellは日常のパソコン操作に適した手軽なシェル言語であるという特徴を持っています。このことから、おっしゃっていることには一理あると思っており、これを批判しようという考えは毛頭ありません。しかし、長年Windowsを使い続けてきて複雑な処理を面倒なGUI操作の繰り返しで行わなければならないという苦い経験を積み重ねてくると、やはりコンパイラー言語とシェルと時にネイティブライブラリの力を総動員させた自動処理の必要性を感じてきます。

    そして当方ではいつの間にか、C#でバックエンドのクラスの作成やライブラリのP/Invokeを行って、これらを実際に操作するフロントエンドとしてPowerShellに触れるという操作スタイルが出来上がっています。

    2020年1月15日 2:49
  • おっしゃることはわかりますが、結果的には歪な利用方法になっているように見受けられます。利用方法を見直す時なのかもしれません。

    例えばPowerShellでもクラス定義できるようになっています。Add-TypeでなくPowerShell上で直接定義しては?

    もしくはF#言語もお勧めです。C#と同等に操作できつつ、PowerShellと同様にスクリプト実行も前提に設計されています。ただし、PowerShellから離れる時点でPowerShellで提供されるコマンドレットが全て使えなくなるため、難しいかもしれません。

    2020年1月16日 22:04
  • 例えばPowerShellでもクラス定義できるようになっています。Add-TypeでなくPowerShell上で直接定義しては?

    一応、日常的なWindowsの操作ではその場限りのユーザー定義型を即興で打ち込むことも多々あり、以前はPowerShellのクラス構文を多用していました。しかし、PowerShellのクラス構文にはデストラクターがないなどの不備があるため不便に感じるようになったため、次第にC#のクラス構文をヒアドキュメントに打ち込んでAdd-Typeするようになった次第であります。

    具体的にどんな不便があったかというと、Windowsのショートカットファイルの操作ではCOMコンポーネントのIShellLinkを使わないといけないのですが、PowerShellだけではインターフェイスの定義ができないためにC#のコードが必要になってしまいました。以前の投稿で紹介したコードをヒアドキュメントにコピペしての使用となります。

    また、スクリプトやモジュール内にPowerShellのクラス構文でクラスを定義すると、スクリプトファイルを跨いで互いにクラスを参照できない不具合というか仕様があり、最初に紹介したアセンブリDLLを介してのAdd-Typeに行きつきました。

    さらに、PowerShellのループ構文の実行速度の遅さも気になり、長いループを伴う処理はC#にお任せするようになってしまっています。

    もちろん、同じようなクラスや関数などをその都度コンソールに打ち込むのは非効率なため、再利用したいクラスなどはこつこつと自作モジュールに追加していってます。それでも、日常操作では即興でクラスを打ち込む必要に迫られたりするのは避けられません。

    なお、F#については使用にVisual Studioのインストールが必要なため、Windowsをクリーンインストールした直後の初期設定などのシステム管理用には不向きみたいです。

    2020年1月17日 2:09