我有一个包含工厂方法的可编组类。工厂方法可用于在测试AppDomain中实例化类。我试图理解我是否可以使用带有using(...)dispose模式的类。
我关心的主要问题是测试AppDomain是否已卸载 - 以及何时。我已经为调用实例方法时设置的类添加了一个静态标志。如果没有卸载appdomain,那么我相信这个标志应该在后续的调用中保留它的设置。
示例类和测试控制台应用程序如下所示:
using System;
using System.Reflection;
using System.Threading;
namespace AppDomainInDispose
{
public class TestClass : MarshalByRefObject, IDisposable
{
public void Run()
{
Console.WriteLine("Hello from {0}", Thread.GetDomain().FriendlyName);
if (_flag)
Console.WriteLine("Flagged!");
else
_flag = true;
}
private static bool _flag = false;
public static TestClass InstantiateInTestDomain()
{
var callingDomain = Thread.GetDomain();
var setup = new AppDomainSetup() { ApplicationBase = callingDomain.SetupInformation.ApplicationBase };
_domain = AppDomain.CreateDomain("test-domain", null, setup);
_domain.DomainUnload += _domain_DomainUnload;
var assembly = Assembly.GetAssembly(typeof(TestClass)).CodeBase;
var proxy = _domain.CreateInstanceFromAndUnwrap(assembly, "AppDomainInDispose.TestClass") as TestClass;
return proxy;
}
static void _domain_DomainUnload(object sender, EventArgs e)
{
Console.WriteLine("Unloading");
}
public static AppDomain _domain = null;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~TestClass()
{
Dispose(false);
}
private void Dispose(bool disposing)
{
if(disposing)
{
//AppDomain.Unload(_domain); // can't, as I'm in the AppDomain
}
}
}
class Program
{
static void Main(string[] args)
{
using(var testClass = TestClass.InstantiateInTestDomain())
{
testClass.Run();
}
using (var testClass = TestClass.InstantiateInTestDomain())
{
testClass.Run(); // if the appdomain hasn't been unloaded then the static flag will still be set
}
Console.ReadKey();
}
}
}
似乎在这个有限的测试中AppDomain正在被卸载 - 但我不知道在哪里。有人可以解释AppDomain发生了什么吗?这种方法是一个非常糟糕的主意吗?
编辑:看起来你可以拥有多个同名的AppDomain,我没有意识到。此外,不会触发DomainUnload事件,因此我可能会假设未卸载域。那个,或者在某些情况下不会触发事件(可能在托管过程正在关闭时)。
答案 0 :(得分:2)
public class GenericDisposable<T> : IDisposable
{
public Action Dispose { get; set; }
public T Object { get; set; }
void IDisposable.Dispose()
{
Dispose();
}
}
public static GenericDisposable<T> CreateDomainWithType<T>()
{
var appDomain = AppDomain.CreateDomain("test-domain");
var inst = appDomain.CreateInstanceAndUnwrap(typeof(T).Assembly.FullName, typeof(T).FullName);
appDomain.DomainUnload += (a, b) => Console.WriteLine("Unloaded");
return new GenericDisposable<T>() { Dispose = () => AppDomain.Unload(appDomain), Object = (T)inst };
}
public class User : MarshalByRefObject
{
public void Sayhello()
{
Console.WriteLine("Hello from User");
}
}
//Usage
static void Main()
{
using (var wrap = CreateDomainWithType<User>())
{
wrap.Object.Sayhello();
}
Console.Read();
}
答案 1 :(得分:1)
不断创新。
一次性操作对我不起作用,因为操作委托不可序列化。
所以这是我的做法:
docker run -d -p 9200:9200 -p 9300:9300 --name elasticsearch -it --rm -v els:/usr/share/elasticsearch/data -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.7.0
秘密正在一个新线程中完成:
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
' TODO: verwalteten Zustand (verwaltete Objekte) entsorgen.
If Me.LoadContext IsNot Nothing Then
Me.LoadContext.Unload()
Me.LoadContext = Nothing
End If
If Me.Stream IsNot Nothing Then
Me.Stream.Dispose()
Me.Stream = Nothing
End If
'If Parameters.m_parameterValues IsNot Nothing Then
' Parameters.m_parameterValues.Clear()
' Parameters.m_parameterValues = Nothing
'End If
If Me.Domain IsNot Nothing Then
' https://stackoverflow.com/questions/7793074/unload-an-appdomain-while-using-idisposable
Dim thread As System.Threading.Thread = New System.Threading.Thread(
Sub(domain As System.AppDomain)
Try
' System.Threading.Thread.Sleep(1000)
System.AppDomain.Unload(domain)
Catch ex As System.Threading.ThreadAbortException
' System.Console.WriteLine(ex.Message)
System.GC.Collect()
System.GC.WaitForPendingFinalizers()
End Try
End Sub
)
thread.IsBackground = True
thread.Start(Me.Domain)
Me.Domain = Nothing
End If
System.GC.Collect()
System.GC.WaitForPendingFinalizers()
End If
' TODO: nicht verwaltete Ressourcen (nicht verwaltete Objekte) freigeben und Finalize() weiter unten überschreiben.
' TODO: grosse Felder auf Null setzen.
End If
disposedValue = True
End Sub
然后线程被杀死,但是代码继续运行。