Unity单例模式代码

31

我刚接触Unity,正在尝试编写一些Unity逻辑,以初始化、注册/解析Email对象的单例实例,以便在其他多个对象中使用。例如下面的OperationEntity。

因此,在注册时,它会从配置文件中填充Email单例实例的某些值,然后每当创建一个OperationEntity实例(在我的情况下是反序列化)时,它都使用相同的Email单例实例。因此,所有客户端逻辑只需反序列化OperationEntity并调用PerformAction() - 由Unity处理电子邮件实例。

public interface IEmail
{
    string FromName { get; set; }
    string FromEmailAddress { get; set; }
}

public class Email : IEmail
{
    public string FromName { get; set; }
    public string FromEmailAddress { get; set; }

    public Email(string fromName, string fromEmailAddress)
    {
        FromName = fromName;
        FromEmailAddress = fromEmailAddress;
    }
}

public class OperationEntity
{
    private readonly IEmail _email;

    public int OperationId { get; set; }
    public string OperationName { get; set; }
    public string ToAddress { get; set; }

    public OperationEntity(IEmail email)
    {
        _email = email;
    }

    public void PerformAction()
    {
        _email.ToAddress = ToAddress;
        _email.Body = "Some email body";
        _email.Deliver();
    }
}

希望能得到帮助,让这个Unity代码正常工作。

    public static void Register(IUnityContainer container)
    {
        container
            .RegisterType<IEmail, Email>(
            new InjectionFactory(c => new Email(
                "To Name", 
                "to@email.com")));

        var email = container.Resolve<IEmail>();  

        container.RegisterType<OperationEntity>(
            "email", new ContainerControlledLifetimeManager(),
            new InjectionConstructor(email));
    }

我需要一些帮助来处理C# Unity代码以连接它。 - Bern
也许关于Singleton的维基可以提供一些帮助。 - cregox
@cregox 这是一个指向 Unity3d 的链接,它是一个图形库。这是关于 Unity 容器 的问题,它是一个 DI 框架。 - Andy
@Andy,你是在说这个链接完全没有帮助吗?直到今天我还在学习单例和依赖注入,所以如果这确实完全无关紧要,那就好好知道一下。 ;) - cregox
在这种情况下,我认为它不太有用;对于大多数 DI 容器,您可以配置插件类型的生命周期,以便只有一个实例,但是消费者实际上并不知道他们正在使用单例。例如,在 .Net 中,我使用接口来抽象从 .config 文件中读取。需要配置的实例通过 ctor 注入像正常一样引用接口,但是因为实现只是包装了静态、线程安全的方法,所以我将其配置为单例,因此 DI 框架实际上只创建对象的一个实例。 - Andy
@cregox 这有时可能很有用,因为您可以从每次使用中新建的实现实例中获得,只需丢弃它,减少等待进行垃圾回收的对象数量,这可能会提高性能。 - Andy
5个回答

52
首先,您需要一个适当的生命周期管理器ContainerControlledLifetimeManager用于单例。
对于自定义初始化,您可能可以使用{{link2:InjectionFactory}}。
这使您可以编写任何初始化实体的代码。
编辑1:这应该有所帮助。
public static void Register(IUnityContainer container)
{
    container
        .RegisterType<IEmail, Email>(
        new ContainerControlledLifetimeManager(),
        new InjectionFactory(c => new Email(
            "To Name", 
            "to@email.com")));
}

然后

var opEntity = container.Resolve<OperationEntity>();

编辑2:为了支持序列化,您必须在反序列化后重新构建依赖项:

public class OperationEntity
{
   // make it public and mark as dependency   
   [Dependency]
   public IEmail _email { get; set;}

}

然后

OperationEntity entity = somehowdeserializeit;

// let unity rebuild your dependencies
container.BuildUp( entity );

我使用第二个链接中的逻辑来解决电子邮件实例问题,但是我该如何确保 OperationEntity 将在其构造函数中使用那个实例呢?我尝试过 container.RegisterType<OperationEntity>(new InjectionConstructor(email)); 但没有成功。 - Bern
如果您使用ContainerControlledLifetimeManager,那么唯一的单例实体将始终被注入到其他类的构造函数中。 - Wiktor Zychla
我按照您的建议在上面包含了我的代码,但不幸的是,在OperationEntity构造函数中,电子邮件实例仍然为null值,所以我猜我可能错过了什么。能否请您提供一下您的想法? - Bern
到目前为止,Wiktor,非常感谢您提供的更新代码。我遇到的唯一问题是,当我反序列化OperationEntity的实例并调用其构造函数时,电子邮件实例仍然为空。因此,对PerformAction()方法的后续调用具有空_email。非常感谢您的想法。 - Bern
非常好,感谢您的耐心和帮助,Wiktor。在您的协助下,我成功让它工作了。 - Bern

15

你可以使用:

container.RegisterType<IEmail, Email>(new ContainerControlledLifetimeManager());

11

如果IEmail是一个没有依赖项(只有自定义参数)的单例模式,你可以自己创建一个实例:

container.RegisterInstance<IEmail>(new Email("To Name", "to@email.com"));

这将把提供的实例注册为容器中的单例。

然后您只需解析服务:

container.Resolve<OperationEntity>();

由于您正在解析具体类型,因此无需注册。但是,如果您希望该服务也是单例,则可以使用ContainerControlledLifetimeManager进行注册,然后所有解析调用(或在将其作为依赖项注入到另一个类中时)都将返回相同的实例:

container.RegisterType<OperationEntity>(new ContainerControlledLifetimeManager());

1
谢谢。最终,OperationEntity的反序列化是主要问题,我不确定这种方法是否解决了这个问题。反序列化后需要的代码是unityContainer.BuildUp(OperationEntity)。 - Bern
1
实际上,这个是可以工作的,但是在Unity 4.0.1中需要添加using Microsoft.Practices.Unity;。但是当更新到版本5.8.11时,即使添加了这个命名空间也不再起作用了,我找不到这个方法。如果你有任何想法,请提供一下。 - Hakan Fıstık

0

你可以实现自己的单例类并从中扩展任何类。

public class MyClass : MonoBehaviour {

    private static MyClass _instance;

    public static MyClass Instance { get { return _instance; } }

    private void Awake()
    {
        if (_instance != null && _instance != this)
        {
            Destroy(this.gameObject);
        } else {
            _instance = this;
        }
    }
}

0

你可以使用以下代码:

public class example : MonoBehaviour
{
    public static example instance;

    public void Start()
    {
        (!instance)
            instance = this;
    }
}

投入更多时间来回答问题是很好的。例如,我非常确定基类被称为 MonoBehaviour 而不是 monobehaviour - BDL

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接