Завантаження бібліотек DLL під час виконання в C #


91

Я намагаюся зрозуміти, як ви могли б імпортувати та використовувати .dll під час виконання всередині програми C #. Використовуючи 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кожен тип у збірці, який, швидше за все, викине до того, як буде знайдений "правильний" клас ...
Рід Копсі,

1
@ReedCopsey, погодився, але на його простому прикладі видно лише його тип. "Єдиними типами, видимими за межами збірки, є загальнодоступні типи та загальнодоступні типи, вкладені в інші загальнодоступні типи." Для нетривіального прикладу, очевидно, це буде проблемою ...
Темний Сокіл

1
Охайний із двома прикладами! :)
Niels Abildgaard

22
Ось чому інтерфейси часто використовуються, і ви можете виконувати виявлення функцій, наприклад, IDog dog = someInstance as IDog;і перевіряти, чи не є воно нульовим. Помістіть свої інтерфейси в загальну бібліотеку DLL, якою користуються клієнти, і будь-який плагін, який буде динамічно завантажуватися, повинен реалізувати цей інтерфейс. Потім це дозволить вам кодувати вашого клієнта проти інтерфейсу IDog і мати intellisense + сильну перевірку типу під час компіляції, а не використовувати динамічну.
AaronLS

1
@ Tarek.Mh: Для цього потрібна залежність від часу компіляції від Class1. На той момент ви можете просто використовувати new Class1(). Запитувач явно вказав залежність виконання. dynamicдозволяє програмі взагалі не вимагати залежності від часу компіляції Class1.
Темний Сокіл

39

Зараз ви створюєте екземпляр кожного типу, визначеного в збірці . Вам потрібно створити лише один екземпляр Class1, щоб викликати метод:

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

Ах, тепер я бачу, що це було також у відповіді DarkFalcon. Твоя була коротшою і полегшила її огляд. :)
skiphoppy

0

Activator.CreateInstance() повертає об'єкт, який не має методу виводу.

Схоже, ви походите з мов динамічного програмування? 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

Це не так складно.

Ви можете перевірити доступні функції завантаженого об'єкта, і якщо ви знайдете ту, яку шукаєте, за назвою, то відслідкуйте очікувані парми, якщо такі є. Якщо це виклик, який ви намагаєтеся знайти, то зателефонуйте за допомогою методу Invoke об’єкта MethodInfo.

Інший варіант - просто побудувати зовнішні об’єкти на інтерфейсі та передати завантажений об’єкт на цей інтерфейс. У разі успіху викликайте функцію спочатку.

Це досить прості речі.


Нічого собі, не впевнений, чому голоси проти. У мене є виробнича програма, яка робить саме це протягом останніх 12 років. * знизати плечима * Кому-небудь потрібен якийсь код, щоб зробити це, вистріліть мені повідомлення Я упакую частини мого виробничого коду і надішлю його разом.
ChrisH

10
Я підозрюю, що голоси проти мали б бути пов’язані з відсутністю прикладів та звужувальним тоном ... Здається, у вас є основа для повної відповіді, тому не бійтеся редагувати докладніше :)
Тінь

1
Просто якось грубо сказати "це досить прості речі", і саме тому ви отримали "проти".
ABPerson

1
Я не був грубим чи поблажливим .... 6 років тому. Тон чітко не проникає в тексті. Це насправді було призначено бути досить легким серцем ... Я також справді відчуваю, що я мав посилання на зразок коду там усі ті роки, і я не уявляю, куди воно пішло (припускаючи, що це справді було там, як я пам’ятаю ). : \
ChrisH

Я не знаю, як працює MethodInfo, але це здається цінним. Я б сказав, що ваша відповідь може бути кращою за прийняту, але її потрібно буде заповнити. Якщо ви коли-небудь дійдете до цього, це буде вдячно. Якщо так, будь ласка, не посилайтесь на зразок коду. Вони можуть зламатися в майбутньому. Найкраще надати зразок самостійно, можливо, з посиланням на джерело або додатковою інформацією для подальшого читання.
SpaghettiCook
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.