C#で実行時にDLLをロードする


91

C#アプリケーション内で実行時に.dllをインポートして使用する方法を理解しようとしています。Assembly.LoadFile()を使用して、プログラムにdllをロードさせることができました(ToString()でクラスの名前を取得できるため、この部分は確実に機能しています)が、「出力」を使用できません。コンソールアプリケーション内からのメソッド。.dllをコンパイルしてから、コンソールのプロジェクトに移動しています。CreateInstanceとメソッドを使用できるようになるまでの間に追加の手順はありますか?

これは私のDLLのクラスです:

namespace DLL
{
    using System;

    public class Class1
    {
        public void Output(string s)
        {
            Console.WriteLine(s);
        }
    }
}

これがDLLをロードしたいアプリケーションです

namespace ConsoleApplication1
{
    using System;
    using System.Reflection;

    class Program
    {
        static void Main(string[] args)
        {
            var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

            foreach(Type type in DLL.GetExportedTypes())
            {
                var c = Activator.CreateInstance(type);
                c.Output(@"Hello");
            }

            Console.ReadLine();
        }
    }
}

回答:


128

メンバーは、C#から直接呼び出されるために、コンパイル時に解決可能である必要があります。それ以外の場合は、反射オブジェクトまたは動的オブジェクトを使用する必要があります。

反射

namespace ConsoleApplication1
{
    using System;
    using System.Reflection;

    class Program
    {
        static void Main(string[] args)
        {
            var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

            foreach(Type type in DLL.GetExportedTypes())
            {
                var c = Activator.CreateInstance(type);
                type.InvokeMember("Output", BindingFlags.InvokeMethod, null, c, new object[] {@"Hello"});
            }

            Console.ReadLine();
        }
    }
}

動的(.NET 4.0)

namespace ConsoleApplication1
{
    using System;
    using System.Reflection;

    class Program
    {
        static void Main(string[] args)
        {
            var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

            foreach(Type type in DLL.GetExportedTypes())
            {
                dynamic c = Activator.CreateInstance(type);
                c.Output(@"Hello");
            }

            Console.ReadLine();
        }
    }
}

12
これはOutput、アセンブリ内のすべてのタイプを呼び出そうとすることに注意してください。これは、「正しい」クラスが見つかる前にスローされる可能性があります...
Reed Copsey 2013

1
@ReedCopsey、同意しましたが、彼の簡単な例では、彼のタイプだけが表示されています。「アセンブリの外部に表示されるタイプは、パブリックタイプと他のパブリックタイプ内にネストされたパブリックタイプのみです。」自明ではない例として、明らかにこれが問題になるでしょう...
Dark Falcon

1
2つの例できちんと!:)
Niels Abildgaard 2014年

22
これが、インターフェイスが頻繁に使用される理由IDog dog = someInstance as IDog;であり、nullでないかどうかなどの特徴検出を実行できます。クライアントが共有する共通のDLLにインターフェースを配置し、動的にロードされるプラグインはそのインターフェースを実装する必要があります。これにより、IDogインターフェースに対してクライアントをコーディングし、動的を使用するのではなく、コンパイル時にインテリセンス+強力な型チェックを行うことができます。
AaronLS 2014年

1
@ Tarek.Mh:コンパイル時の依存関係が必要になりますClass1。その時点で、を使用できますnew Class1()。質問者は、実行時の依存関係を明示的に指定しました。dynamicプログラムがコンパイル時の依存関係をまったく必要としないようにClass1します。
ダークファルコン

39

現在、アセンブリで定義されているすべてのタイプのインスタンスを作成しています。Class1メソッドを呼び出すには、のインスタンスを1つだけ作成する必要があります。

class Program
{
    static void Main(string[] args)
    {
        var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

        var theType = DLL.GetType("DLL.Class1");
        var c = Activator.CreateInstance(theType);
        var method = theType.GetMethod("Output");
        method.Invoke(c, new object[]{@"Hello"});

        Console.ReadLine();
    }
}

19

Outputメソッドを公​​開するタイプのインスタンスを作成する必要があります。

static void Main(string[] args)
    {
        var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");

        var class1Type = DLL.GetType("DLL.Class1");

        //Now you can use reflection or dynamic to call the method. I will show you the dynamic way

        dynamic c = Activator.CreateInstance(class1Type);
        c.Output(@"Hello");

        Console.ReadLine();
     }

どうもありがとうございました-これはまさに私が探しているものです。動的キーワードの使用を示しているので、これが他の回答よりも高く評価されていないことは信じられません。
skiphoppy 2016年

ああ、今ではそれがDarkFalconの答えにもあったことがわかります。ただし、あなたの方が短く、見やすくなりました。:)
skiphoppy 2016年

0

Activator.CreateInstance() Outputメソッドを持たないオブジェクトを返します。

あなたは動的計画言語から来ているように見えますか?C#は間違いなくそうではなく、あなたがやろうとしていることは難しいでしょう。

特定の場所から特定のdllをロードしているので、コンソールアプリケーションへの参照として追加したいだけかもしれません。

を介してアセンブリを絶対にロードするAssembly.Load場合は、リフレクションを介してメンバーを呼び出す必要があります。c

のような何かtype.GetMethod("Output").Invoke(c, null);がそれを行う必要があります。


0
foreach (var f in Directory.GetFiles(".", "*.dll"))
            Assembly.LoadFrom(f);

これにより、実行可能ファイルのフォルダにあるすべてのDLLが読み込まれます。

私の場合Reflection、他のDLLであっても、クラスのすべてのサブクラスを見つけるために使用しようとしていました。これはうまくいきましたが、それが最善の方法かどうかはわかりません。

編集:私はそれを計時しました、そしてそれはそれらを最初にロードするだけのようです。

Stopwatch stopwatch = new Stopwatch();
for (int i = 0; i < 4; i++)
{
    stopwatch.Restart();
    foreach (var f in Directory.GetFiles(".", "*.dll"))
        Assembly.LoadFrom(f);
    stopwatch.Stop();
    Console.WriteLine(stopwatch.ElapsedMilliseconds);
}

出力:34 0 0 0

したがって、万が一の場合に備えて、Reflectionが検索する前にそのコードを実行できる可能性があります。


-1

それほど難しいことではありません。

ロードされたオブジェクトの使用可能な関数を調べることができ、名前で探している関数が見つかった場合は、予想されるパラメーターがある場合はそれをスヌープします。検索しようとしている呼び出しの場合は、MethodInfoオブジェクトのInvokeメソッドを使用して呼び出します。

もう1つのオプションは、外部オブジェクトをインターフェイスにビルドし、ロードされたオブジェクトをそのインターフェイスにキャストすることです。成功した場合は、関数をネイティブに呼び出します。

これはかなり単純なものです。


うわー、なぜ反対票を投じたのかわからない。私は過去12年間のように、まさにこれを行う本番アプリケーションを持っています。*肩をすくめる*これを行うにはコードが必要です。メッセージを送ってください。プロダクションコードの一部をパッケージ化して送信します。
ChrisH 2016

10
反対票は例の欠如と凝縮のトーンに関係しているのではないかと思います...しかし、完全な答えの根拠があるようですので、詳細を編集することを恐れないでください:)
Shadow

1
「これはかなり単純なことです」と言うのはちょっと失礼です、そしてそれがあなたに反対票を投じた理由です。
ABPerson

1
私は失礼でも見下していませんでした.... 6年前。明らかに、トーンはテキストでは伝わりません。それは本当にとても気さくなことを意味していました...私はまた、その間ずっとそこにコードサンプルへのリンクがあったように感じます、そしてそれがどこに行ったのか分かりません(私が覚えているようにそれが本当にそこにあったと仮定して)。:\
ChrisH

MethodInfoがどのように機能するかはわかりませんが、価値があるようです。あなたの答えは現在受け入れられているものよりも良い可能性があると思いますが、それを完了する必要があります。あなたがそれに近づいたら、それはありがたいです。その場合は、コードサンプルにリンクしないでください。これらは将来壊れてしまうかもしれません。サンプルを自分で提供することをお勧めします。ソースへのリンクや、読み続けるための追加情報を提供することもできます。
SpaghettiCook
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.