以下这段代码是动态执行C#表达试,执行完后,内存资源没有释放,
public object GetValue( string value,string eval)
{
string codeSnippet = "using System; " + "\r\n" +
"using System.Collections;"+
"namespace WrokFlowExpression {" + "\r\n" +
" public class Eval" + "\r\n" +
" {" + "\r\n" +
" public Eval(){} " + "\r\n" +
" public object GetValue()" + "\r\n" +
" {" + "\r\n" +
" " + eval +";"+
" " + "\r\n" +
" try{"+
" return " + value + ";" + " \r\n " +
" }"+
" catch{ \r\n "+
" return false; \r\n }\r\n finally{ \r\n "+
" \r\n }"+
" return false; \r\n "+
" }" + " \r\n" +
" } }";
CodeSnippetCompileUnit unit = new CodeSnippetCompileUnit( codeSnippet ); ICodeCompiler compiler = new CSharpCodeProvider().CreateCompiler();
CompilerParameters para = new CompilerParameters();
para.ReferencedAssemblies.Add( "System.dll" );
para.GenerateInMemory = false;//这里改城true与false没有用
para.GenerateExecutable = false;
para.OutputAssembly = "Eval.dll";//这里输出内在与输出本地程序集也无效 Assembly asm = compiler.CompileAssemblyFromDom( para , unit ).CompiledAssembly; Type type = asm.GetType( "WrokFlowExpression.Eval" );
MethodInfo mi = type.GetMethod( "GetValue" , BindingFlags.Public | BindingFlags.Instance );
object obj = asm.CreateInstance( "WrokFlowExpression.Eval" );
ParameterInfo[] parame = mi.GetParameters();
object[] ars = new object[parame.Length];
for(int i=0;i<parame.Length;i++)
{
// if(i==0) ars[i] = StartUser;
// if(i==1) ars[i] = CurrentUser;
// if(i==2) ars[i] = Entity;
// if(i>3)
// {
// ars[i] = null;
// }
}
return mi.Invoke(obj , ars );
}
public object GetValue( string value,string eval)
{
string codeSnippet = "using System; " + "\r\n" +
"using System.Collections;"+
"namespace WrokFlowExpression {" + "\r\n" +
" public class Eval" + "\r\n" +
" {" + "\r\n" +
" public Eval(){} " + "\r\n" +
" public object GetValue()" + "\r\n" +
" {" + "\r\n" +
" " + eval +";"+
" " + "\r\n" +
" try{"+
" return " + value + ";" + " \r\n " +
" }"+
" catch{ \r\n "+
" return false; \r\n }\r\n finally{ \r\n "+
" \r\n }"+
" return false; \r\n "+
" }" + " \r\n" +
" } }";
CodeSnippetCompileUnit unit = new CodeSnippetCompileUnit( codeSnippet ); ICodeCompiler compiler = new CSharpCodeProvider().CreateCompiler();
CompilerParameters para = new CompilerParameters();
para.ReferencedAssemblies.Add( "System.dll" );
para.GenerateInMemory = false;//这里改城true与false没有用
para.GenerateExecutable = false;
para.OutputAssembly = "Eval.dll";//这里输出内在与输出本地程序集也无效 Assembly asm = compiler.CompileAssemblyFromDom( para , unit ).CompiledAssembly; Type type = asm.GetType( "WrokFlowExpression.Eval" );
MethodInfo mi = type.GetMethod( "GetValue" , BindingFlags.Public | BindingFlags.Instance );
object obj = asm.CreateInstance( "WrokFlowExpression.Eval" );
ParameterInfo[] parame = mi.GetParameters();
object[] ars = new object[parame.Length];
for(int i=0;i<parame.Length;i++)
{
// if(i==0) ars[i] = StartUser;
// if(i==1) ars[i] = CurrentUser;
// if(i==2) ars[i] = Entity;
// if(i>3)
// {
// ars[i] = null;
// }
}
return mi.Invoke(obj , ars );
}
Imports System.Reflection
Imports System.CodeDom
Imports System.CodeDom.Compiler
Imports Microsoft.CSharp
Imports System.Windows.FormsPublic Class cCompiler
#Region " 变量 "
Private _CodeLanguage As enuCodeLanguage = mdlPublic.enuCodeLanguage.Default
Private _lstErrors As ListView
Private _hasError As Boolean = False
Private _oAssembly As [Assembly]
Private _Type As Type Private Provider As Object
Private oCompiler As ICodeCompiler
Private AsmNameString As String() = {"System.DLL", "System.Data.dll", "System.Windows.Forms.dll", "System.XML.dll", "Microsoft.VisualBasic.dll"} Private oCompParams As CompilerParameters
#End Region#Region " 属性 "
Friend Property CodeLanguage() As enuCodeLanguage
Get
Return _CodeLanguage
End Get
Set(ByVal Value As enuCodeLanguage)
_CodeLanguage = Value
End Set
End Property Friend Property ListViewForErrors() As ListView
Get
Return _lstErrors
End Get
Set(ByVal Value As ListView)
_lstErrors = Value
End Set
End Property Friend ReadOnly Property HasError() As Boolean
Get
Return _hasError
End Get
End Property Friend ReadOnly Property OutAssembly() As [Assembly]
Get
Return _oAssembly
End Get
End Property#End Region#Region " 私有方法 "
Private Sub ListErrors(ByVal ErrorCollection As CompilerErrorCollection)
If _lstErrors Is Nothing Then Return
Dim i As Integer
_lstErrors.Items.Clear()
Dim pErr As CompilerError
For i = 0 To ErrorCollection.Count - 1
Dim pListItem As Windows.Forms.ListViewItem
pErr = ErrorCollection.Item(i) If pErr.IsWarning Then
pListItem = _lstErrors.Items.Add((i + 1).ToString, 1)
Else
pListItem = _lstErrors.Items.Add((i + 1).ToString, 0)
End If
'pListItem.SubItems.Add(pErr.FileName)
pListItem.SubItems.Add(pErr.Line)
pListItem.SubItems.Add(pErr.Column)
pListItem.SubItems.Add(pErr.ErrorNumber)
pListItem.SubItems.Add(pErr.ErrorText)
Next End Sub Private Sub Init()
If _CodeLanguage = mdlPublic.enuCodeLanguage.CSharp Then
Provider = New Microsoft.CSharp.CSharpCodeProvider
Else
Provider = New Microsoft.VisualBasic.VBCodeProvider
End If
oCompiler = Provider.CreateCompiler()
oCompParams = New CompilerParameters(AsmNameString)
oCompParams.GenerateExecutable = False
oCompParams.GenerateInMemory = True
End Sub
#End Region#Region " 公共方法 "
Friend Function Compiler(ByVal StrSourceCode As String) As [Assembly]
Dim oCodeUnit As CodeSnippetCompileUnit
Dim oCompResults As CompilerResults
oCodeUnit = New CodeSnippetCompileUnit(StrSourceCode)
oCompResults = oCompiler.CompileAssemblyFromDom(oCompParams, oCodeUnit) '开始编译 _oAssembly = Nothing
If oCompResults.Errors.HasErrors Then
ListErrors(oCompResults.Errors)
_hasError = True
Else
Try
_oAssembly = oCompResults.CompiledAssembly
Return _oAssembly
_hasError = False
Catch ex As Exception
'Throw ex
cMsgBox(ex.Message)
End Try
_lstErrors.Items.Clear()
End If
End Function Friend Function Run(ByVal ParamArray strParams As String()) As Boolean
If _oAssembly Is Nothing Then Return False
Dim oType As Type
Dim sNameSpace, sTypeName As String
Dim oMeth As MethodInfo
For Each oType In _oAssembly.GetTypes If oType.IsClass Then 'oMeth = oType.GetMethod("Main")
'If Not oMeth Is Nothing Then
sNameSpace = oType.Namespace
sTypeName = oType.Name
For Each oMeth In oType.GetMethods(BindingFlags.Public)
cMsgBox(oMeth.Name)
Next
Exit For
'End If
End If
Next
Try
If sTypeName <> "" Then
If sNameSpace <> "" Then sTypeName = sNameSpace & "." & sTypeName
Dim oObj As Object = _oAssembly.CreateInstance(sTypeName)
If strParams.Length > 0 Then oObj.main(strParams) Else oObj.main()
End If
Return True
Catch ex As Exception
Throw ex
Return False
End Try
End Function Friend Function GetOutType(ByVal typeName As String) As Type
Try
If _oAssembly Is Nothing Then Return Nothing
Return _oAssembly.GetType(typeName)
Catch ex As Exception
Throw ex
Return Nothing
End Try
End Function Friend Function GetOutTypes(ByVal typeName As String) As Type()
Try
If _oAssembly Is Nothing Then Return Nothing
Return _oAssembly.GetTypes
Catch ex As Exception
Throw ex
Return Nothing
End Try
End Function
Friend Function GetMethod(ByVal typeName As String, ByVal MethodName As String) As MethodInfo
Try
If _oAssembly Is Nothing Then Return Nothing
Dim oType As Type = _oAssembly.GetType(typeName)
Return oType.GetMethod("Main", BindingFlags.Public Or BindingFlags.InvokeMethod)
Catch ex As Exception
Throw ex
End Try
End Function#End Region#Region " 构造函数 "
Public Sub New()
Init()
End Sub Public Sub New(ByVal CodeLanguage As enuCodeLanguage)
_CodeLanguage = CodeLanguage
Init()
End Sub
#End RegionEnd Class
一部分就是编译代码部分,首先用StringBuilder来替换string进行操作,
其次如果是多次调用,把CodeSnippetCompileUnit和ICodeCompiler两个类型对象定义为成员,防止多个对象创建。第二部分是通过反射来调用,因此定义为成员不太合理,此时显示调用GC.Collect()来看看效果。
现在换了一种思路。卸载子域方式也不行,不明白,动态编译所占用的内存存在哪里??
1、自己创建一个子域,用这个子域去载另一个程序集,则另一个程序集中的一个类实现动态编译功能。
TestCompiler.dll程序集中的类代码如下:
using System;
using System.Reflection;
using System.Windows.Forms;
using System.Runtime.Remoting.Lifetime;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Text;
using Microsoft.CSharp;namespace TestCompiler
{
/// <summary>
/// Class1 的摘要说明。
/// </summary>
[System.Serializable]
public class Eval
{
private CodeSnippetCompileUnit unit;
private ICodeCompiler compiler;
public Eval()
{
//
// TODO: 在此处添加构造函数逻辑
//
} public object GetValue( string value,string eval)
{ string codeSnippet = eval;
StringBuilder MyStringBuilder = new StringBuilder(codeSnippet);
unit = new CodeSnippetCompileUnit( MyStringBuilder.ToString() ); compiler = new CSharpCodeProvider().CreateCompiler();
CompilerParameters para = new CompilerParameters();
para.ReferencedAssemblies.Add( "System.dll" );
para.GenerateInMemory = false;
para.GenerateExecutable = false;
compiler.CompileAssemblyFromDom(para , unit );
Assembly asm = compiler.CompileAssemblyFromDom( para , unit ).CompiledAssembly;
Type type = asm.GetType( "WrokFlowExpression.Eval" );
MethodInfo mi = type.GetMethod( "GetValue" , BindingFlags.Public | BindingFlags.Instance );
object obj = asm.CreateInstance( "WrokFlowExpression.Eval" );
ParameterInfo[] parame = mi.GetParameters();
object[] ars = new object[parame.Length];
for(int i=0;i<parame.Length;i++)
{
// if(i==0) ars[i] = StartUser;
// if(i==1) ars[i] = CurrentUser;
// if(i==2) ars[i] = Entity;
// if(i>3)
// {
// ars[i] = null;
// }
}
obj = mi.Invoke(obj , ars );
return obj;
}
}
}
2、在TestAssembly.dll程序中类TestClass中的代码如下:
using System;
using System.Reflection;
using System.Windows.Forms;
using System.Runtime.Remoting.Lifetime;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Text;
using Microsoft.CSharp;namespace TestAssembly
{
/// <summary>
/// Class1 的摘要说明。
/// </summary>
public class TestClass:MarshalByRefObject
{
protected string AssblPath = Application.StartupPath+@"\Bin\";
protected int i=0;
private CodeSnippetCompileUnit unit;
private ICodeCompiler compiler;
private Hashtable hash=new Hashtable();
public TestClass()
{
}
public string TestLoadAssembly(string Assemblyname)
{
Assembly Myassembly = Assembly.LoadFrom(AssblPath+ Assemblyname);
return Myassembly.FullName;
} public int GetCount()
{
return i++;
}
/// <summary>
/// 重写InitializeLifetimeService
/// 获取控制此实例的生存期策略的生存期服务对象
/// </summary>
/// <returns>生存期服务对象</returns>
public override Object InitializeLifetimeService()
{
ILease lease = (ILease)base.InitializeLifetimeService();
if (lease.CurrentState == LeaseState.Initial)
{
lease.InitialLeaseTime = TimeSpan.Zero;
}
return lease;
} public object GetValue( string value,string eval)
{
AppDomainSetup setup = new AppDomainSetup();
setup.ShadowCopyFiles = "true"; object obj = null;
string codeSnippet = "using System; " + "\r\n" +
"using System.Collections;"+
"namespace WrokFlowExpression {" + "\r\n" +
"[System.Serializable]"+
" public class Eval" + "\r\n" +
" {" + "\r\n" +
" public Eval(){} " + "\r\n" +
" public object GetValue()" + "\r\n" +
" {" + "\r\n" +
" " + eval +";"+
" " + "\r\n" +
" try{"+
" return " + value + ";" + " \r\n " +
" }"+
" catch{ \r\n "+
" return false; \r\n }\r\n finally{ \r\n "+
" \r\n }"+
" return false; \r\n "+
" }" + " \r\n" +
" } }"; string guids = Guid.NewGuid().ToString();
AppDomain domain = AppDomain.CreateDomain(guids,null,setup);
hash["1"]=domain;
object objent = domain.CreateInstanceFromAndUnwrap("TestCompiler.dll","TestCompiler.Eval" );
Type type = objent.GetType();
MethodInfo mi = type.GetMethod( "GetValue" , BindingFlags.Public | BindingFlags.Instance );
// object obj = asm.CreateInstance( "WrokFlowExpression.Eval" );
ParameterInfo[] parame = mi.GetParameters();
object[] ars = new object[parame.Length];
for(int i=0;i<parame.Length;i++)
{
ars[i] = codeSnippet;
// if(i==0) ars[i] = StartUser;
// if(i==1) ars[i] = CurrentUser;
// if(i==2) ars[i] = Entity;
// if(i>3)
// {
// ars[i] = null;
// }
}
obj = mi.Invoke(objent , ars );
GC.Collect();
return obj;
} public object GetValue1( string value,string eval)
{
object val = GetValue(value,eval);
// if(domain!= null)
// {
GC.Collect();
AppDomain d=hash["1"] as AppDomain;
hash.Remove("1");
AppDomain.Unload(d);
GC.Collect();
// }
return val;
}
在外面调用GetValue1,但每执行一次内存只涨不回收。郁闷啊
至于编译这部分,我没有单独去写,借用如下这个例子
http://www.codeproject.com/dotnet/DynamicCompileAndRun.asp所做的第一个修改,修改其中的CompileEngine这个类,让它继承IDisposable这个接口,然后在接口函数中如下进行实现。
#region IDisposable Members
public void Dispose()
{
// TODO: Add CompileEngine.Dispose implementation
GC.SuppressFinalize( this );
}
#endregion第二个修改就是调用部分(FormMain.cs文件中)
using( CompileEngine engine = new CompileEngine( code, language, entry ) )
{
engine.Run();
}第三个修改就是线程开启(FormMain.cs文件中)
GC.Collect();
// we use a new thread to run it
new Thread( new ThreadStart( RunTheProg ) ).Start();
经过如上处理后,动态编译和运行所占用的资源不会立刻释放,不过再显式调用GC.Collect之后内存会降下来。你可以把上面的方法应用到你的程序当中,应该能满足你的要求。