실행중에 스크립트나 외부dll의 Method를 불러 오는 방법이 없을까?
그런데 C#에서 이런 문제를 어떻게 해결 할 까 찾던 중 묘한 것을 발견했다.
나중에 참조 하기 위해서 해당 코드와 문서를 캡쳐해서 보관 해 놓는다.!
아래의 내용과 같이 설명 되어 있는데 상당히 의미 있는 코드로 보인다. 원본은여기서 참조
#런타임, #runtime, #메서드 호출, #문자열로 함수호출, #문자열로 메소드 호출
Introduction
Sometimes, it is very useful to compile code at runtime. Personally, I use this feature mostly in these two cases:
- Simple web tutorials – writing a small piece of code into
TextBox
control and its execution instead of the necessity to own or to run some IDE. - User-defined functions – I have written an application for symbolic regression with simple configuration file where user can choose some of my predefined functions (sin, cos, etc.). The user can also simply write his own mathematical expression with basic knowledge of C# language.
If you want to use this feature, you don’t have to install any third-party libraries. All functionality is provided by the .NET Framework in Microsoft.CSharp
and System.CodeDom.Compiler
namespaces.
Hello World Program
For compiling code at runtime, you need to follow these few steps:
- Write your code – most important part. Here you can write classes and methods into the
string
variable. We will add onlyMain
method:Hide Copy Codestring code = @" using System; namespace First { public class Program { public static void Main() { " + "Console.WriteLine(\"Hello, world!\");" + @" } } } ";
- Create the provider and parameters of the compiler:Hide Copy Code
CSharpCodeProvider provider = new CSharpCodeProvider(); CompilerParameters parameters = new CompilerParameters();
- Define parameters of the compiler (optional) – at this point, we can add a reference to external libraries. We can also define whether our compiled code will be generated only in the memory or into the DLL or EXE file:Hide Copy Code
// Reference to System.Drawing library parameters.ReferencedAssemblies.Add("System.Drawing.dll"); // True - memory generation, false - external file generation parameters.GenerateInMemory = true; // True - exe file generation, false - dll file generation parameters.GenerateExecutable = true;
- Compile assembly:Hide Copy Code
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
- Check errors:Hide Copy Code
if (results.Errors.HasErrors) { StringBuilder sb = new StringBuilder(); foreach (CompilerError error in results.Errors) { sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText)); } throw new InvalidOperationException(sb.ToString()); }
- Get assembly, type and the
Main
method:Hide Copy CodeAssembly assembly = results.CompiledAssembly; Type program = assembly.GetType("First.Program"); MethodInfo main = program.GetMethod("Main");
- Run it:Hide Copy Code
main.Invoke(null, null);
And that’s it. We have run our first runtime-compiled program!
User-defined Functions
As I mentioned in the Introduction, I use this feature for runtime defining mathematical expressions. Let’s look at creating simple functions with two parameters.
This is very similar to the previous example (it’s assumed that parameters of the function are named x
and y
). In this code, we simply replace part of the string
with our desired function and compile it:
public static MethodInfo CreateFunction(string function)
{
string code = @"
using System;
namespace UserFunctions
{
public class BinaryFunction
{
public static double Function(double x, double y)
{
return func_xy;
}
}
}
";
string finalCode = code.Replace("func_xy", function);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerResults results = provider.CompileAssemblyFromSource(new CompilerParameters(), finalCode);
Type binaryFunction = results.CompiledAssembly.GetType("UserFunctions.BinaryFunction");
return binaryFunction.GetMethod("Function");
}
At this point, we have written code for compiling user-defined functions so we use it by creating some function and invoking it:
MethodInfo function = CreateFunction("x + 2 * y");
object result = function.Invoke(null, new object[] { 2, 3 });
Console.WriteLine(result);
In this case, the result is equal to 8 so our compiler works fine but we have got result of the Object
type and we must also provide parameters of the Object
type. There is a much better way – creating a delegate:
var betterFunction = (Func<double, double, double>)Delegate.CreateDelegate
(typeof(Func<double, double, double>), function);
And there is a very simple way to invoke it:
double result = betterFunction(2, 3);
Console.WriteLine(result);
Speed Comparison
We have written a simple function compiler so now we have one function of 4 types:
- Original, at compile-time-defined function
- Runtime-compiled function invoked by Reflection
- Delegate created from the runtime-compiled function
- Lambda Expression Delegate (
(x, y) => x + 2 * y
)
Let’s write a simple program to compare their speed:
DateTime start;
DateTime stop;
double result;
int repetitions = 5000000;
start = DateTime.Now;
for (int i = 0; i < repetitions; i++)
{
result = OriginalFunction(2, 3);
}
stop = DateTime.Now;
Console.WriteLine("Original - time: {0} ms", (stop - start).TotalMilliseconds);
start = DateTime.Now;
for (int i = 0; i < repetitions; i++)
{
result = (double)function.Invoke(null, new object[] { 2, 3 });
}
stop = DateTime.Now;
Console.WriteLine("Reflection - time: {0} ms", (stop - start).TotalMilliseconds);
start = DateTime.Now;
for (int i = 0; i < repetitions; i++)
{
result = betterFunction(2, 3);
}
stop = DateTime.Now;
Console.WriteLine("Delegate - time: {0} ms", (stop - start).TotalMilliseconds);
start = DateTime.Now;
for (int i = 0; i < repetitions; i++)
{
result = lambda(2, 3);
}
stop = DateTime.Now;
Console.WriteLine("Lambda - time: {0} ms", (stop - start).TotalMilliseconds);
After several tests, we will get the following results:
- Original – time: 92 ms
- Reflection – time: 3686 ms
- Delegate – time: 64 ms
- Lambda – time – 90 ms
Conclusion
Compiling C# code at runtime is very useful and easy task for various applications. I have shown how to create your own simple compiler. From speed results, it is obvious that speed of the runtime-compiled code is equal to the classical code (except the reflection-invoked case).
'개발언어 > C#' 카테고리의 다른 글
C#크로스 스레드 작업이 잘못 되었습니다.(delegate,Invoke 사용하기) (0) | 2017.06.14 |
---|---|
관리자 권한 상승 (0) | 2017.05.24 |
c#Typeof로 동적 인스턴스 만들기 (0) | 2017.05.19 |
C#에서 Docking Control 사용하기 (0) | 2017.05.18 |
C# 동적컨트롤생성 (0) | 2017.05.17 |