C # 'da çalışma zamanında DLL'leri yükleme


91

Bir C # uygulaması içinde çalışma zamanında bir .dll dosyasını içe aktarmayı ve kullanmayı nasıl yapabileceğinizi anlamaya çalışıyorum. Assembly.LoadFile () kullanarak programımı dll'yi yüklemeyi başardım (ToString () ile sınıfın adını alabildiğim için bu kısım kesinlikle çalışıyor), ancak 'Çıktı'yı kullanamıyorum konsol uygulamamın içinden yöntem. .Dll dosyasını derliyorum ve ardından konsolumun projesine taşıyorum. CreateInstance ile yöntemleri kullanabilmek arasında fazladan bir adım var mı?

Bu benim DLL dosyamdaki sınıf:

namespace DLL
{
    using System;

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

ve işte DLL'yi yüklemek istediğim uygulama

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

Yanıtlar:


128

Üyelerin doğrudan C # üzerinden çağrılabilmesi için derleme sırasında çözümlenebilir olması gerekir. Aksi takdirde yansıma veya dinamik nesneler kullanmanız gerekir.

Yansıma

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

Dinamik (.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
Bu çalışır çağırmak için geldiğini hatırlatırız Outputmontaj, muhtemelen "doğru" ders öncesi atacağım gösterdiği her türüne ... bulunduğunda
Reed Copsey

1
@ReedCopsey, Kabul Edildi, ancak basit örneği için, türü görünen tek şey. "Bir derlemenin dışında görülebilen türler, genel türler ve diğer genel türler içinde yuvalanmış genel türlerdir." Önemsiz olmayan bir örnek için, belli ki bu bir sorun olacak ...
Dark Falcon

1
İki örnekle harika! :)
Niels Abildgaard

22
Bu yüzden arayüzler sıklıkla kullanılır ve IDog dog = someInstance as IDog;null olup olmadığını test edebilir ve gibi özellik algılaması yapabilirsiniz . Arayüzlerinizi istemciler tarafından paylaşılan ortak bir DLL'ye koyun ve dinamik olarak yüklenecek herhangi bir eklenti bu arayüzü uygulamalıdır. Bu, daha sonra istemcinizi IDog arayüzüne göre kodlamanıza ve dinamik kullanmak yerine derleme zamanında intellisense + güçlü tip kontrolüne sahip olmanıza izin verecektir.
AaronLS

1
@ Tarek.Mh: Bu, üzerinde bir derleme zamanı bağımlılığı gerektirir Class1. Bu noktada sadece kullanabilirsiniz new Class1(). Soru soran, açıkça bir çalışma zamanı bağımlılığı belirtti. dynamicprogramın derleme zamanı bağımlılığı gerektirmemesini sağlar Class1.
Dark Falcon

39

Şu anda, montajda tanımlanan her türden bir örnek oluşturuyorsunuz . Class1Yöntemi çağırmak için yalnızca tek bir örneğini oluşturmanız gerekir :

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

OutputYöntemi ortaya çıkaran türün bir örneğini oluşturmanız gerekir :

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

Çok teşekkür ederim - tam da aradığım şey bu. Dinamik anahtar kelimenin kullanımını gösterdiğinden, bunun diğer yanıtlardan daha yüksek derecelendirilmediğine inanamıyorum.
2016

Ah, şimdi görüyorum ki DarkFalcon'un cevabında da var. Seninki daha kısaydı ve görmeyi kolaylaştırdı. :)
skiphoppy

0

Activator.CreateInstance() Çıktı yöntemi olmayan bir nesne döndürür.

Görünüşe göre dinamik programlama dillerinden geliyorsunuz? C # kesinlikle öyle değildir ve yapmaya çalıştığınız şey zor olacaktır.

Belirli bir konumdan belirli bir dll yüklediğinize göre, belki de bunu konsol uygulamanıza bir referans olarak eklemek istersiniz?

Montajı kesinlikle yüklemek Assembly.Loadistiyorsanız, herhangi bir üyeyi aramak için yansıma yoluyla gitmeniz gerekecektir.c

Böyle bir şey type.GetMethod("Output").Invoke(c, null);yapmalı.


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

Bu, yürütülebilir dosyanın klasöründe bulunan tüm DLL'leri yükler.

Benim durumumda Reflection, diğer DLL'lerde bile bir sınıfın tüm alt sınıflarını bulmak için kullanmaya çalışıyordum . Bu işe yaradı, ancak bunu yapmanın en iyi yolu olup olmadığından emin değilim.

DÜZENLEME: Zamanını belirledim ve yalnızca ilk kez yüklüyor gibi görünüyor.

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

Çıkış: 34 0 0 0

Dolayısıyla, her ihtimale karşı herhangi bir Yansıma aramasından önce bu kodu potansiyel olarak çalıştırabiliriz.


-1

O kadar zor değil.

Yüklenen nesnenin kullanılabilir işlevlerini inceleyebilir ve aradığınızı adıyla bulursanız, varsa beklenen parametrelerini gözden geçirebilirsiniz. Bulmaya çalıştığınız çağrı ise, MethodInfo nesnesinin Invoke yöntemini kullanarak çağırın.

Diğer bir seçenek de, harici nesnelerinizi bir arabirimde oluşturmak ve yüklenen nesneyi bu arabirime atmaktır. Başarılı olursa, işlevi yerel olarak çağırın.

Bu oldukça basit şeyler.


Vay be, neden olumsuz oy kullandığından emin değilim. Son 12 yıldır tam olarak bunu yapan bir üretim uygulamam var. * omuz silkme * Bunu yapmak için koda ihtiyacı olan varsa, bana bir mesaj at. Üretim kodumun bazı kısımlarını paketleyip göndereceğim.
ChrisH

10
Olumsuz oyların örneklerin eksikliğinden ve yoğunlaştırıcı tondan kaynaklanacağından şüpheleniyorum ... Yine de tam bir cevap için temeliniz var gibi görünüyor, bu yüzden daha fazla ayrıntı düzenlemekten korkmayın :)
Shadow

1
"Bu oldukça basit bir şey" demek biraz kabalık ve bu yüzden size olumsuz oylar verdiniz.
ABPerson

1
Kaba ya da küçümseyici değildim .... 6 yıl önce. Ton açıkça metinde geçmiyor. Gerçekten ben de ... kalpli oldukça hafif olması gerekiyordu gerçekten orada o yıllarda bir kod örneği için bir bağlantı varmış gibi hissediyorum ve ben hatırlayarak gibi mi gerçekten orada varsayarak (gitti hiç bir fikrim yok ). : \
ChrisH

MethodInfo'nun nasıl çalıştığını bilmiyorum ama değerli görünüyor. Cevabınızın şu anda kabul edilmiş olandan daha iyi olma potansiyeline sahip olduğunu söyleyebilirim, ancak tamamlanması gerekir. Eğer bir gün ona yaklaşırsan, takdir edilecektir. Öyleyse, lütfen bir kod örneğine bağlantı vermeyin. Bunlar gelecekte kırılabilir. Örneği, muhtemelen bir kaynağa bağlantı veya okumaya devam etmek için ekstra bilgi ile kendiniz sağlamak en iyisidir.
SpaghettiCook
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.