Laden von DLLs zur Laufzeit in C #

90

Ich versuche herauszufinden, wie Sie zur Laufzeit eine DLL in eine C # -Anwendung importieren und verwenden können. Verwenden von Assembly.LoadFile () Ich habe es geschafft, dass mein Programm die DLL lädt (dieser Teil funktioniert definitiv, da ich den Namen der Klasse mit ToString () abrufen kann), kann jedoch die 'Ausgabe' nicht verwenden. Methode aus meiner Konsolenanwendung. Ich kompiliere die DLL und verschiebe sie dann in das Projekt meiner Konsole. Gibt es einen zusätzlichen Schritt zwischen CreateInstance und der Verwendung der Methoden?

Dies ist die Klasse in meiner DLL:

namespace DLL
{
    using System;

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

und hier ist die Anwendung, die ich die DLL laden möchte

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();
        }
    }
}
Danbroooks
quelle

Antworten:

126

Mitglieder müssen zur Kompilierungszeit auflösbar sein, damit sie direkt von C # aufgerufen werden können. Andernfalls müssen Sie Reflexions- oder dynamische Objekte verwenden.

Reflexion

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();
        }
    }
}

Dynamisch (.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();
        }
    }
}
Dunkler Falke
quelle
10
Beachten Sie, dass dies versucht, Outputjeden Typ in der Assembly aufzurufen , der wahrscheinlich werfen wird, bevor die "richtige" Klasse gefunden wird ...
Reed Copsey
1
@ReedCopsey, Einverstanden, aber für sein einfaches Beispiel ist nur sein Typ sichtbar. "Die einzigen Typen, die außerhalb einer Assembly sichtbar sind, sind öffentliche Typen und öffentliche Typen, die in anderen öffentlichen Typen verschachtelt sind." Für ein nicht triviales Beispiel wird dies offensichtlich ein Problem sein ...
Dark Falcon
1
Ordentlich mit den beiden Beispielen! :)
Niels Abildgaard
22
Aus diesem Grund werden häufig Schnittstellen verwendet, und Sie können Funktionen erkennen, z. B. IDog dog = someInstance as IDog;testen, ob sie nicht null sind. Stellen Sie Ihre Schnittstellen in eine gemeinsame DLL, die von Clients gemeinsam genutzt wird, und jedes Plugin, das dynamisch geladen wird, muss diese Schnittstelle implementieren. Auf diese Weise können Sie Ihren Client anhand der IDog-Schnittstelle codieren und zur Kompilierungszeit eine Intellisense + Strong-Typprüfung durchführen, anstatt Dynamic zu verwenden.
AaronLS
Sie können c.Output in einen try catch-Block einfügen und wenn Sie die Ausnahme abfangen, continueso dass dies, wie @ReedCopsey sagte, nicht ausgelöst wird, bevor die Klasse gefunden wurde.
Alan Deep
39

Im Moment erstellen Sie eine Instanz für jeden in der Assembly definierten Typ . Sie müssen nur eine einzelne Instanz von erstellen Class1, um die Methode aufzurufen:

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();
    }
}
Reed Copsey
quelle
19

Sie müssen eine Instanz des Typs erstellen, der die OutputMethode verfügbar macht :

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();
     }
Alberto
quelle
Vielen Dank - genau das suche ich. Ich kann nicht glauben, dass dies nicht höher bewertet ist als die anderen Antworten, da es die Verwendung des dynamischen Schlüsselworts zeigt.
Skiphoppy
Ah, jetzt sehe ich, dass es auch in DarkFalcons Antwort war. Ihre war kürzer und machte es jedoch leichter zu sehen. :)
Skiphoppy
0

Activator.CreateInstance() Gibt ein Objekt zurück, das keine Ausgabemethode hat.

Sie kommen aus dynamischen Programmiersprachen? C # ist definitiv nicht das, und was Sie versuchen zu tun, wird schwierig sein.

Da Sie eine bestimmte DLL von einem bestimmten Speicherort laden, möchten Sie sie möglicherweise nur als Referenz zu Ihrer Konsolenanwendung hinzufügen.

Wenn Sie die Assembly unbedingt über laden möchten Assembly.Load, müssen Sie über Reflection gehen, um Mitglieder anzurufenc

So etwas type.GetMethod("Output").Invoke(c, null);sollte es tun.

Fredrik
quelle
-1

Es ist nicht so schwierig.

Sie können die verfügbaren Funktionen des geladenen Objekts überprüfen. Wenn Sie das gesuchte Objekt anhand des Namens finden, können Sie gegebenenfalls die erwarteten Parameter überprüfen. Wenn es sich um den Aufruf handelt, den Sie suchen, rufen Sie ihn mit der Invoke-Methode des MethodInfo-Objekts auf.

Eine andere Möglichkeit besteht darin, Ihre externen Objekte einfach in eine Schnittstelle zu erstellen und das geladene Objekt in diese Schnittstelle umzuwandeln. Wenn erfolgreich, rufen Sie die Funktion nativ auf.

Das ist ziemlich einfaches Zeug.

ChrisH
quelle
Wow, ich weiß nicht warum die Abstimmungen. Ich habe eine Produktionsanwendung, die genau dies wie in den letzten 12 Jahren tut. * Achselzucken * Jeder braucht einen Code, um dies zu tun, schieß mir eine Nachricht. Ich werde Teile meines Produktionscodes verpacken und mitschicken.
ChrisH
9
Ich vermute, die Abstimmungen hätten mit dem Mangel an Beispielen und dem komprimierenden Ton zu tun ... Scheint, als hätten Sie die Grundlage für eine vollständige Antwort, also haben Sie keine Angst, weitere Details zu bearbeiten :)
Shadow
1
Es ist einfach unhöflich zu sagen, "das ist ziemlich einfaches Zeug", und deshalb hast du die Downvotes bekommen.
ABPerson
1
Ich war nicht unhöflich oder herablassend ... vor 6 Jahren. Der Ton kommt im Text nicht klar durch. Es sollte wirklich ziemlich unbeschwert sein ... Ich habe auch das Gefühl, dass ich in all den Jahren einen Link zu einem Codebeispiel hatte, und ich habe keine Ahnung, wohin es ging (vorausgesetzt, es war wirklich da, wie ich mich erinnere ). : \
ChrisH
Ich weiß nicht, wie MethodInfo funktioniert, aber es scheint wertvoll. Ich würde sagen, Ihre Antwort hat das Potenzial, besser zu sein als die derzeit akzeptierte, aber sie müsste vervollständigt werden. Wenn Sie jemals dazu kommen, wäre es dankbar. Wenn ja, verlinken Sie bitte nicht auf ein Codebeispiel. Diese können in Zukunft kaputt gehen. Es ist am besten, das Beispiel selbst bereitzustellen, möglicherweise mit einem Link zu einer Quelle oder zusätzlichen Informationen zum weiteren Lesen.
SpaghettiCook