[c#] 어셈블리를로드하고 클래스를 찾고 Run () 메서드를 호출하는 올바른 방법

샘플 콘솔 프로그램.

class Program
{
    static void Main(string[] args)
    {
        // ... code to build dll ... not written yet ...
        Assembly assembly = Assembly.LoadFile(@"C:\dyn.dll");
        // don't know what or how to cast here
        // looking for a better way to do next 3 lines
        IRunnable r = assembly.CreateInstance("TestRunner");
        if (r == null) throw new Exception("broke");
        r.Run();

    }
}

어셈블리 (.dll)를 동적으로 빌드 한 다음 어셈블리를로드하고 클래스를 인스턴스화하고 해당 클래스의 Run () 메서드를 호출하고 싶습니다. TestRunner 클래스를 다른 것으로 캐스팅해야합니까? 한 어셈블리 (동적 코드)의 유형이 내 (정적 어셈블리 / 셸 앱)의 유형에 대해 어떻게 알 수 있는지 확실하지 않습니다. 객체에 대해 Run ()을 호출하기 위해 몇 줄의 리플렉션 코드를 사용하는 것이 더 낫습니까? 그 코드는 어떤 모습이어야합니까?

업데이트 : William Edmondson-코멘트 참조



답변

AppDomain 사용

어셈블리를 AppDomain먼저 로드하는 것이 더 안전하고 유연합니다 .

따라서 이전에 주어진 대답 대신 :

var asm = Assembly.LoadFile(@"C:\myDll.dll");
var type = asm.GetType("TestRunner");
var runnable = Activator.CreateInstance(type) as IRunnable;
if (runnable == null) throw new Exception("broke");
runnable.Run();

나는 다음을 제안했습니다 ( 관련 질문에 대한이 답변 에서 수정 됨 ).

var domain = AppDomain.CreateDomain("NewDomainName");
var t = typeof(TypeIWantToLoad);
var runnable = domain.CreateInstanceFromAndUnwrap(@"C:\myDll.dll", t.Name) as IRunnable;
if (runnable == null) throw new Exception("broke");
runnable.Run();

이제 어셈블리를 언로드하고 다른 보안 설정을 가질 수 있습니다.

어셈블리의 동적로드 및 언로드를 위해 더 많은 유연성과 성능을 원한다면 Managed Add-ins Framework (즉, System.AddIn네임 스페이스)를 살펴 봐야합니다 . 자세한 내용은 MSDN의 추가 기능 및 확장성에 대한 이 문서를 참조하십시오 .


답변

TestRunner호출하는 어셈블리 의 형식 정보에 액세스 할 수없는 경우 (그렇지 않은 것 같음) 다음과 같이 메서드를 호출 할 수 있습니다.

Assembly assembly = Assembly.LoadFile(@"C:\dyn.dll");
Type     type     = assembly.GetType("TestRunner");
var      obj      = Activator.CreateInstance(type);

// Alternately you could get the MethodInfo for the TestRunner.Run method
type.InvokeMember("Run",
                  BindingFlags.Default | BindingFlags.InvokeMethod,
                  null,
                  obj,
                  null);

IRunnable인터페이스 유형에 액세스 할 수 TestRunner있는 경우 동적으로 생성되거나로드 된 어셈블리에서 구현되는 유형이 아닌 인스턴스를 해당 유형으로 캐스팅 할 수 있습니다 .

  Assembly assembly  = Assembly.LoadFile(@"C:\dyn.dll");
  Type     type      = assembly.GetType("TestRunner");
  IRunnable runnable = Activator.CreateInstance(type) as IRunnable;
  if (runnable == null) throw new Exception("broke");
  runnable.Run();


답변

C #을 동적으로 컴파일,로드 및 실행하기 위해 CS-Script 를 사용하는 내 규칙 엔진에서 원하는 작업을 정확히 수행하고 있습니다 . 찾고있는 내용으로 쉽게 번역 할 수 있어야하며 예를 들어 보겠습니다. 첫째, 코드 (줄임) :

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using CSScriptLibrary;

namespace RulesEngine
{
    /// <summary>
    /// Make sure <typeparamref name="T"/> is an interface, not just any type of class.
    /// 
    /// Should be enforced by the compiler, but just in case it's not, here's your warning.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class RulesEngine<T> where T : class
    {
        public RulesEngine(string rulesScriptFileName, string classToInstantiate)
            : this()
        {
            if (rulesScriptFileName == null) throw new ArgumentNullException("rulesScriptFileName");
            if (classToInstantiate == null) throw new ArgumentNullException("classToInstantiate");

            if (!File.Exists(rulesScriptFileName))
            {
                throw new FileNotFoundException("Unable to find rules script", rulesScriptFileName);
            }

            RulesScriptFileName = rulesScriptFileName;
            ClassToInstantiate = classToInstantiate;

            LoadRules();
        }

        public T @Interface;

        public string RulesScriptFileName { get; private set; }
        public string ClassToInstantiate { get; private set; }
        public DateTime RulesLastModified { get; private set; }

        private RulesEngine()
        {
            @Interface = null;
        }

        private void LoadRules()
        {
            if (!File.Exists(RulesScriptFileName))
            {
                throw new FileNotFoundException("Unable to find rules script", RulesScriptFileName);
            }

            FileInfo file = new FileInfo(RulesScriptFileName);

            DateTime lastModified = file.LastWriteTime;

            if (lastModified == RulesLastModified)
            {
                // No need to load the same rules twice.
                return;
            }

            string rulesScript = File.ReadAllText(RulesScriptFileName);

            Assembly compiledAssembly = CSScript.LoadCode(rulesScript, null, true);

            @Interface = compiledAssembly.CreateInstance(ClassToInstantiate).AlignToInterface<T>();

            RulesLastModified = lastModified;
        }
    }
}

이렇게하면 T 유형의 인터페이스를 사용하고 .cs 파일을 어셈블리로 컴파일하고 지정된 유형의 클래스를 인스턴스화하고 인스턴스화 된 해당 클래스를 T 인터페이스에 맞 춥니 다. 기본적으로 인스턴스화 된 클래스가 해당 인터페이스를 구현하는지 확인하기 만하면됩니다. 속성을 사용하여 다음과 같이 모든 것을 설정하고 액세스합니다.

private RulesEngine<IRulesEngine> rulesEngine;

public RulesEngine<IRulesEngine> RulesEngine
{
    get
    {
        if (null == rulesEngine)
        {
            string rulesPath = Path.Combine(Application.StartupPath, "Rules.cs");

            rulesEngine = new RulesEngine<IRulesEngine>(rulesPath, typeof(Rules).FullName);
        }

        return rulesEngine;
    }
}

public IRulesEngine RulesEngineInterface
{
    get { return RulesEngine.Interface; }
}

예를 들어 Run ()을 호출하고 싶으므로 다음과 같이 Run () 메서드를 정의하는 인터페이스를 만듭니다.

public interface ITestRunner
{
    void Run();
}

그런 다음이를 구현하는 클래스를 다음과 같이 만듭니다.

public class TestRunner : ITestRunner
{
    public void Run()
    {
        // implementation goes here
    }
}

RulesEngine의 이름을 TestHarness와 같은 이름으로 변경하고 속성을 설정합니다.

private TestHarness<ITestRunner> testHarness;

public TestHarness<ITestRunner> TestHarness
{
    get
    {
        if (null == testHarness)
        {
            string sourcePath = Path.Combine(Application.StartupPath, "TestRunner.cs");

            testHarness = new TestHarness<ITestRunner>(sourcePath , typeof(TestRunner).FullName);
        }

        return testHarness;
    }
}

public ITestRunner TestHarnessInterface
{
    get { return TestHarness.Interface; }
}

그런 다음 원하는 곳 어디에서나 다음을 실행할 수 있습니다.

ITestRunner testRunner = TestHarnessInterface;

if (null != testRunner)
{
    testRunner.Run();
}

플러그인 시스템에서는 잘 작동 할 수 있지만 모든 규칙이 하나의 C # 소스 파일에 있기 때문에있는 그대로의 코드는 하나의 파일을로드하고 실행하는 것으로 제한됩니다. 그래도 실행하려는 각 파일에 대해 유형 / 소스 파일을 전달하도록 수정하는 것이 매우 쉬울 것이라고 생각합니다. getter에서이 두 매개 변수를 사용하는 메소드로 코드를 이동하면됩니다.

또한 ITestRunner 대신 IRunnable을 사용하십시오.


답변

“TestRunner”유형을 얻으려면 리플렉션을 사용해야합니다. Assembly.GetType 메서드를 사용합니다.

class Program
{
    static void Main(string[] args)
    {
        Assembly assembly = Assembly.LoadFile(@"C:\dyn.dll");
        Type type = assembly.GetType("TestRunner");
        var obj = (TestRunner)Activator.CreateInstance(type);
        obj.Run();
    }
}


답변

어셈블리를 빌드 할 때를 호출 AssemblyBuilder.SetEntryPoint한 다음 Assembly.EntryPoint속성 에서 다시 가져 와서 호출 할 수 있습니다.

이 서명을 사용하고 싶을 것이며 이름을 지정할 필요가 없습니다 Main.

static void Run(string[] args)


답변