201

C# コード フラグメントをテキスト ファイル (または任意の入力ストリーム) に保存して、それらを動的に実行できるかどうか疑問に思っていました。提供されたものが任意の Main() ブロック内で正常にコンパイルされると仮定すると、このコードをコンパイルおよび/または実行することは可能ですか? パフォーマンス上の理由から、コンパイルすることをお勧めします。

少なくとも、実装が必要なインターフェイスを定義でき、そのインターフェイスを実装するコード「セクション」を提供できます。

4

7 に答える 7

194

C#/すべての静的 .NET 言語での最善の解決策は、そのようなことにはCodeDOMを使用することです。(注記として、その他の主な目的は、コードのビットまたはクラス全体を動的に構築することです。)

これは、 LukeH のブログから取った素敵な短い例です。この例では、楽しみのために LINQ も使用しています。

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CSharp;
using System.CodeDom.Compiler;

class Program
{
    static void Main(string[] args)
    {
        var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
        var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
        parameters.GenerateExecutable = true;
        CompilerResults results = csc.CompileAssemblyFromSource(parameters,
        @"using System.Linq;
            class Program {
              public static void Main(string[] args) {
                var q = from i in Enumerable.Range(1,100)
                          where i % 2 == 0
                          select i;
              }
            }");
        results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
    }
}

ここで最も重要なクラスはCSharpCodeProvider、コンパイラを使用してオンザフライでコードをコンパイルするクラスです。コードを実行したい場合は、少しのリフレクションを使用して、アセンブリを動的にロードして実行するだけです。

C# の別の例を次に示します (やや簡潔ではありませんが)、さらに、System.Reflection名前空間を使用してランタイム コンパイルされたコードを実行する方法を正確に示しています。

于 2009-05-05T19:03:40.173 に答える
42

実行時にコードを生成する方法について他の人がすでに良い答えを出しているので、2番目の段落に対処すると思いました。私はこれについていくつかの経験があり、その経験から学んだ教訓を共有したいと思います.

少なくとも、実装が必要なインターフェイスを定義でき、そのインターフェイスを実装するコード「セクション」を提供できます。

interfaceを基本型として使用すると、問題が発生する可能性があります。将来、単一の新しいメソッドを に追加すると、interfaceを実装するすべての既存のクライアント提供のクラスinterfaceが抽象化されます。つまり、実行時にクライアント提供のクラスをコンパイルまたはインスタンス化できなくなります。

古いインターフェイスを出荷してから約 1 年後、サポートが必要な大量の「レガシー」データを配布した後、新しいメソッドを追加する時期になったときに、この問題が発生しました。古いインターフェイスを継承した新しいインターフェイスを作成することになりましたが、このアプローチでは、どのインターフェイスが利用可能かを確認する必要があったため、クライアント提供のクラスをロードしてインスタンス化することが難しくなりました。

当時私が考えた解決策の 1 つは、代わりに、以下のような実際のクラスを基本型として使用することでした。クラス自体は抽象としてマークできますが、すべてのメソッドは空の仮想メソッド (抽象メソッドではない) にする必要があります。その後、クライアントは必要なメソッドをオーバーライドできます。クライアントが提供する既存のコードを無効にすることなく、基本クラスに新しいメソッドを追加できます。

public abstract class BaseClass
{
    public virtual void Foo1() { }
    public virtual bool Foo2() { return false; }
    ...
}

この問題が当てはまるかどうかに関係なく、コード ベースとクライアントが提供するコードの間のインターフェイスをバージョン管理する方法を検討する必要があります。

于 2009-05-05T19:47:45.123 に答える
3

コンパイルするには、csc コンパイラへのシェル呼び出しを開始するだけです。パスとスイッチをまっすぐに保つために頭痛がするかもしれませんが、それは確かに可能です.

C# コーナー シェルの例

編集:または、さらに良いことに、Noldorinが提案したようにCodeDOMを使用してください...

于 2009-05-05T18:59:55.970 に答える