【发布时间】:2017-09-23 03:37:26
【问题描述】:
我下面有一个已完成(但已损坏)的 C# 应用程序,它会生成堆栈溢出异常。如果您查看源代码,您会明白为什么会出现堆栈溢出异常,所以我并没有真正在诊断它发生的原因,我想知道处理它的最佳方法是什么。
1) 所有对统一的引用都封装在一个名为 Registry 的类中,因此我可以毫无困难地升级。我不希望 unitycontainer 在可能的情况下乱扔其他类。从理论上讲,如果/当它出现时,我应该能够升级到 5,或者如果我的性格发生剧烈变化,甚至可以用 ninject 或其他 DI 框架替换它。
2) 我希望注册表由统一容器控制,以便它可以在容器控制类的构造函数中使用。 (例如 FirstSingleInstance)
3) IRegistry 和 Registry 都继承自 IDisposable,因为我认为处置统一容器是一种很好的做法。
4) Registry 在它自己的构造函数中构造 Unity Container,所以我假设我也应该在 registry.dispose 被调用时处置 unity 容器。
5) 由 Registry 控制的所有其他类都应为单实例类,因此我将它们注册到 ContainerControlledLifetimeManager。我希望这些实例会在容器被释放时被释放。
处理这种情况的最佳做法是什么?
a) 不要在 Registry 上调用 dispose -- 让它在进程线程的生命周期内一直存在?
b) 不要试图让 Registry(以及扩展的 UnityContainer)由统一容器控制。这样在 Registry 上调用 dispose 不会导致 stackoverflow 异常。我将如何统一构造 FirstSingleInstance 类是我必须审查的内容。
d) 其他?
这是我编写的包含所有相关部分的应用程序
using System;
using Microsoft.Practices.Unity;
namespace DIProblem.Console
{
class Program
{
static void Main(string[] args)
{
IRegistry registry = CreateRegistry();
IFirstSingleInstance theInstance = registry.Resolve<IFirstSingleInstance>();
theInstance.DoThis();
registry.Dispose(); // stack overflow here because of infinite dispose loop
}
static IRegistry CreateRegistry() => new Registry();
}
public class FirstSingleInstance : IFirstSingleInstance
{
private IRegistry _registry;
public FirstSingleInstance(IRegistry reg)
{
_registry = reg;
}
public void DoThis()
{
System.Console.WriteLine("This Was Done.");
_registry.Resolve<ISecondSingleInstance>().DoThisToo();
}
}
public class SecondSingleInstance : ISecondSingleInstance
{
private IRegistry _registry;
public SecondSingleInstance(IRegistry reg)
{
_registry = reg;
}
public void DoThisToo()
{
System.Console.WriteLine("This Was Done too.");
}
}
public interface ISecondSingleInstance
{
void DoThisToo();
}
public interface IFirstSingleInstance
{
void DoThis();
}
public class Registry : IRegistry, IDisposable
{
public Registry()
{
_container = new UnityContainer();
RegisterInstance<IFirstSingleInstance, FirstSingleInstance>();
RegisterInstance<ISecondSingleInstance, SecondSingleInstance>();
_container.RegisterInstance<IRegistry>(this);
}
private UnityContainer _container;
public void RegisterInstance<T1, T2>() where T2 : class, T1 => _container.RegisterType<T1, T2>(new ContainerControlledLifetimeManager());
public T Resolve<T>() => _container.Resolve<T>();
public void Dispose()
{
Dispose(true);
System.GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
_container?.Dispose();
_container = null;
}
}
public interface IRegistry : IDisposable
{
T Resolve<T>();
void RegisterInstance<T1, T2>() where T2 : class, T1;
}
}
感谢您以合理的方式提供帮助。
【问题讨论】:
-
那么你到底为什么要在容器中注册
IRegistry? -
因为 FirstSingleInstance 类在其公共构造函数中接受它作为参数。
-
为什么它需要它作为依赖项?我可以想象它需要容器,而不是注册表,它基本上是您的组合根。
-
我的原始代码 sn-p 缺少那么多细节。方法 DoThis() 将依赖 _registry 来解析其他接口,这些接口也将在其公共构造函数中接受 IRegistry。根据我的经验,如果 IRegistry 不在容器中,当在 IFirstSingleInstance 或 ISecondSingleInstance 上调用 Resolve 时,unity 将不知道将什么传递给这些构造函数。我更新了代码 sn-p 以更好地代表这种用途。感谢您的关注!
-
在您的代码中应用Service Locator anti-pattern。避免应用此反模式。相反,让
FirstSingleInstance有一个构造函数参数ISecondSingleInstance而不是IRegistry。这称为 Constructor Injection 并且应该始终是您向类提供依赖项的首选方法。 Unity 将能够检查已解析类型的构造函数(一种称为自动装配的做法),并将代表您自动解析这些依赖项。
标签: c# dependency-injection unity-container