어셈블리를로드하고 클래스를 찾고 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
}
}
Change the name of RulesEngine to something like TestHarness, and set your properties:
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; }
}
Then, anywhere you want to call it, you can just run:
ITestRunner testRunner = TestHarnessInterface;
if (null != testRunner)
{
testRunner.Run();
}
It would probably work great for a plugin system, but my code as-is is limited to loading and running one file, since all of our rules are in one C# source file. I would think it'd be pretty easy to modify it to just pass in the type/source file for each one you wanted to run, though. You'd just have to move the code from the getter into a method that took those two parameters.
Also, use your IRunnable in place of ITestRunner.
You will need to use reflection to get the type "TestRunner". Use the Assembly.GetType method.
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();
}
}
When you build your assembly, you can call AssemblyBuilder.SetEntryPoint
, and then get it back from the Assembly.EntryPoint
property to invoke it.
Keep in mind you'll want to use this signature, and note that it doesn't have to be named Main
:
static void Run(string[] args)
'programing' 카테고리의 다른 글
응용 프로그램의 경로 가져 오기 (0) | 2020.10.10 |
---|---|
덮어 쓰지 않고 vim 레지스터에 줄을 추가하는 방법 (0) | 2020.10.10 |
짧은 해시를 생성하는 해시 함수? (0) | 2020.10.10 |
System.getenv ()와 System.getProperty ()의 차이점 (0) | 2020.10.09 |
Perl에서 변수에 숫자 값이 있는지 어떻게 알 수 있습니까? (0) | 2020.10.09 |