Próbuję dowiedzieć się, jak można przejść do importowania i używania .dll w czasie wykonywania wewnątrz aplikacji C #. Używając Assembly.LoadFile () udało mi się zmusić mój program do załadowania biblioteki dll (ta część zdecydowanie działa, ponieważ jestem w stanie uzyskać nazwę klasy za pomocą ToString ()), jednak nie mogę użyć opcji „Wyjście” metoda z wnętrza mojej aplikacji konsoli. Kompiluję plik .dll, a następnie przenoszę go do projektu mojej konsoli. Czy istnieje dodatkowy krok między CreateInstance a możliwością korzystania z metod?
To jest klasa w mojej bibliotece DLL:
namespace DLL
{
using System;
public class Class1
{
public void Output(string s)
{
Console.WriteLine(s);
}
}
}
a oto aplikacja, którą chcę załadować bibliotekę 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();
}
}
}
c#
reflection
dll
danbroooks
źródło
źródło
Odpowiedzi:
Elementy członkowskie muszą być rozpoznawalne w czasie kompilacji, aby były wywoływane bezpośrednio z C #. W przeciwnym razie musisz użyć odbić lub obiektów dynamicznych.
Odbicie
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(); } } }
Dynamiczny (.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(); } } }
źródło
Output
każdy typ w assemblerze, który prawdopodobnie wyrzuci, zanim "właściwa" klasa zostanie znaleziona ...IDog dog = someInstance as IDog;
i sprawdzać, czy nie są puste. Umieść swoje interfejsy we wspólnej bibliotece DLL współdzielonej przez klientów, a każda wtyczka, która zostanie załadowana dynamicznie, musi implementować ten interfejs. Pozwoli to następnie zakodować klienta w interfejsie IDog i mieć intelisense + silne sprawdzanie typów w czasie kompilacji zamiast używania dynamicznego.Class1
. W tym momencie możesz po prostu użyćnew Class1()
. Pytający jawnie określił zależność środowiska wykonawczego.dynamic
pozwala programowi w ogóle nie wymagać zależności od czasu kompilacjiClass1
.W tej chwili tworzysz wystąpienie każdego typu zdefiniowanego w zestawie .
Class1
Aby wywołać metodę, wystarczy utworzyć jedną instancję :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(); } }
źródło
Musisz utworzyć instancję typu, który uwidacznia
Output
metodę: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(); }
źródło
Activator.CreateInstance()
zwraca obiekt, który nie ma metody Output.Wygląda na to, że pochodzisz z dynamicznych języków programowania? C # na pewno nie jest tym, a to, co próbujesz zrobić, będzie trudne.
Ponieważ ładujesz konkretną bibliotekę dll z określonej lokalizacji, może chcesz po prostu dodać ją jako odniesienie do aplikacji konsoli?
Jeśli absolutnie chcesz załadować zestaw przez
Assembly.Load
, będziesz musiał przejść przez refleksję, aby wezwać dowolnych członkówc
Coś takiego
type.GetMethod("Output").Invoke(c, null);
powinno to zrobić.źródło
foreach (var f in Directory.GetFiles(".", "*.dll")) Assembly.LoadFrom(f);
To ładuje wszystkie biblioteki DLL obecne w folderze twojego pliku wykonywalnego.
W moim przypadku próbowałem
Reflection
znaleźć wszystkie podklasy klasy, nawet w innych bibliotekach DLL. To zadziałało, ale nie jestem pewien, czy to najlepszy sposób.EDYCJA: ustaliłem czas i wydaje się, że ładuję je tylko za pierwszym razem.
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); }
Wyjście: 34 0 0 0
Tak więc, na wszelki wypadek, można by potencjalnie uruchomić ten kod przed jakimikolwiek przeszukiwaniem Reflection.
źródło
To nie jest takie trudne.
Możesz sprawdzić dostępne funkcje załadowanego obiektu, a jeśli znajdziesz tę, której szukasz po nazwie, przeszukaj jej oczekiwane parametry, jeśli takie istnieją. Jeśli jest to wywołanie, które próbujesz znaleźć, wywołaj je przy użyciu metody Invoke obiektu MethodInfo.
Inną opcją jest po prostu zbudowanie zewnętrznych obiektów do interfejsu i przesłanie załadowanego obiektu do tego interfejsu. Jeśli się powiedzie, wywołaj funkcję natywnie.
To całkiem prosta sprawa.
źródło