您的位置:首页 > 移动开发 > Unity3D

Unity 学习笔记(1) -- Unity简介及简单使用

2009-04-23 21:03 751 查看
Unity是微软Patterns&Practices团队所开发的一个轻量级的,并且可扩展的依赖注入(DependencyInjection)容器,它支持常用的三种依赖注入方式:构造器注入(ConstructorInjection)、属性注入(PropertyInjection),以及方法调用注入(MethodCallInjection).现在Unity最新的版本的1.2版,可以在微软的开源站点http://unity.codeplex.com下载最新的发布版本和文档。通过使用Unity,我们能轻松构建松耦合结构的程序,从而让整个程序框架变得清晰和易于维护。

在平常的软件编码过程中,程序的逻辑往往很复杂,尤其是大型项目的开发中,一个模块常常会去引用其他的模块,假设我们有一个监控器类,用来监控CPU的温度,当温度达到预警的范围时,监控器有一个报警的方法,方法里面通过短信提醒器,以发送短信的方式通知维护人员。于是就出现了下面这样一段最为常见的代码:

publicclassMonitor
{
publicvoidAlarm()
{
SMSNotifynotify=newSMSNotify();
notify.Send();
}
}
.csharpcode{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{margin:0em}.csharpcode.rem{color:#008000}.csharpcode.kwrd{color:#0000ff}.csharpcode.str{color:#006080}.csharpcode.op{color:#0000c0}.csharpcode.preproc{color:#cc6633}.csharpcode.asp{background-color:#ffff00}.csharpcode.html{color:#800000}.csharpcode.attr{color:#ff0000}.csharpcode.alt{background-color:#f4f4f4;margin:0em;width:100%}.csharpcode.lnum{color:#606060}.csharpcode{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{margin:0em}.csharpcode.rem{color:#008000}.csharpcode.kwrd{color:#0000ff}.csharpcode.str{color:#006080}.csharpcode.op{color:#0000c0}.csharpcode.preproc{color:#cc6633}.csharpcode.asp{background-color:#ffff00}.csharpcode.html{color:#800000}.csharpcode.attr{color:#ff0000}.csharpcode.alt{background-color:#f4f4f4;margin:0em;width:100%}.csharpcode.lnum{color:#606060}

在Monitor类中,直接引用到了一个短信提醒器的类,这是最为不灵活和最不易于扩展的一种方式。或许我们想到了面向接口编程,利用多态的好处,可以提供灵活的不同子类的实现,增加代码扩展性等等。但是说到底,接口一定是需要实现的,也就是如下语句迟早要执行:

publicvoidAlarm()
{
INotifynotify=newSMSNotify();
notify.Send();
}
.csharpcode{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{margin:0em}.csharpcode.rem{color:#008000}.csharpcode.kwrd{color:#0000ff}.csharpcode.str{color:#006080}.csharpcode.op{color:#0000c0}.csharpcode.preproc{color:#cc6633}.csharpcode.asp{background-color:#ffff00}.csharpcode.html{color:#800000}.csharpcode.attr{color:#ff0000}.csharpcode.alt{background-color:#f4f4f4;margin:0em;width:100%}.csharpcode.lnum{color:#606060}

这样看来,在实现INotify这个接口的时候,仍然需要具体的类来实现,而这样的代码在程序编译的时候就已经固定下来,如果以后需要使用新的提醒器,仍旧需要修改源代码并重新编译。并且在我们的Monitor类中,明显依赖SMSNotify类,二者之间的耦合度非常紧密。因此Ioc(控制反转)模式被提出用来解决这种问题,也即把接口的具体实现延缓到运行时,接口的实现类是在运行时被装载的。这样,就算有了新的实现类,也不需要更改调用者的代码(可以在Unity中使用配置文件的方式实现)。这种Ioc模式可以被形象的比喻为:接口就像一个空壳,而在具体实现时,向这个空壳注入内容,而让它成为一个真正的实体。这种模式也被形象的称为:依赖注入。通过使用Unity,我们能构建松耦合的软件,并且对象之间相互关联的细节,我们也不必关心,可以交由依赖注入容器全权负责。

前面也提到了依赖注入常用的三种形式:构造器注入、属性注入和方法调用注入,我们可以通过例子来实现这三种形式的注入。还是以上面的场景为例,我们的几个类和接口如下图:





1.ConstructorInjection

IMonitor接口定义:

publicinterfaceIMonitor
{
voidAlarm();
}
Monitor类:

publicclassMonitor:IMonitor
{
privateINotifynotify;
publicMonitor(INotifyn)
{
notify=n;
}
publicvoidAlarm()
{
notify.Send();
}
}
.csharpcode{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{margin:0em}.csharpcode.rem{color:#008000}.csharpcode.kwrd{color:#0000ff}.csharpcode.str{color:#006080}.csharpcode.op{color:#0000c0}.csharpcode.preproc{color:#cc6633}.csharpcode.asp{background-color:#ffff00}.csharpcode.html{color:#800000}.csharpcode.attr{color:#ff0000}.csharpcode.alt{background-color:#f4f4f4;margin:0em;width:100%}.csharpcode.lnum{color:#606060}.csharpcode{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{margin:0em}.csharpcode.rem{color:#008000}.csharpcode.kwrd{color:#0000ff}.csharpcode.str{color:#006080}.csharpcode.op{color:#0000c0}.csharpcode.preproc{color:#cc6633}.csharpcode.asp{background-color:#ffff00}.csharpcode.html{color:#800000}.csharpcode.attr{color:#ff0000}.csharpcode.alt{background-color:#f4f4f4;margin:0em;width:100%}.csharpcode.lnum{color:#606060}

INotify接口定义:

.csharpcode{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{margin:0em}.csharpcode.rem{color:#008000}.csharpcode.kwrd{color:#0000ff}.csharpcode.str{color:#006080}.csharpcode.op{color:#0000c0}.csharpcode.preproc{color:#cc6633}.csharpcode.asp{background-color:#ffff00}.csharpcode.html{color:#800000}.csharpcode.attr{color:#ff0000}.csharpcode.alt{background-color:#f4f4f4;margin:0em;width:100%}.csharpcode.lnum{color:#606060}

publicinterfaceINotify
{
voidSend();
}
.csharpcode{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{margin:0em}.csharpcode.rem{color:#008000}.csharpcode.kwrd{color:#0000ff}.csharpcode.str{color:#006080}.csharpcode.op{color:#0000c0}.csharpcode.preproc{color:#cc6633}.csharpcode.asp{background-color:#ffff00}.csharpcode.html{color:#800000}.csharpcode.attr{color:#ff0000}.csharpcode.alt{background-color:#f4f4f4;margin:0em;width:100%}.csharpcode.lnum{color:#606060}

EmailNotify类:

publicclassEmailNotify:INotify
{
publicvoidSend()
{
Console.WriteLine("SendEmailNotify...");
}
}
.csharpcode{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{margin:0em}.csharpcode.rem{color:#008000}.csharpcode.kwrd{color:#0000ff}.csharpcode.str{color:#006080}.csharpcode.op{color:#0000c0}.csharpcode.preproc{color:#cc6633}.csharpcode.asp{background-color:#ffff00}.csharpcode.html{color:#800000}.csharpcode.attr{color:#ff0000}.csharpcode.alt{background-color:#f4f4f4;margin:0em;width:100%}.csharpcode.lnum{color:#606060}

SMSNotify类:

publicclassSMSNotify:INotify
{
publicvoidSend()
{
Console.WriteLine("SendSMSNotify...");
}
}
.csharpcode{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{margin:0em}.csharpcode.rem{color:#008000}.csharpcode.kwrd{color:#0000ff}.csharpcode.str{color:#006080}.csharpcode.op{color:#0000c0}.csharpcode.preproc{color:#cc6633}.csharpcode.asp{background-color:#ffff00}.csharpcode.html{color:#800000}.csharpcode.attr{color:#ff0000}.csharpcode.alt{background-color:#f4f4f4;margin:0em;width:100%}.csharpcode.lnum{color:#606060}

可以看到,在Monitor类的构造函数里面,传入的参数是一个INotify接口类型,Alarm方法,调用了实现类的Send方法,但具体调用哪一个实现类的Send方法,只有在注入实体后才知道。Unity容器中,通常使用RegisterType和Resolve方法来分别注册和获取实例,并且这两个方法有很多泛型和非泛型的重载,具体的类型和参数,可以参考Unity的官方帮助文档。

现在我们向Monitor的构造函数注入实现INotify接口的实例:

staticvoidMain(string[]args)
{
IUnityContainercontainer=newUnityContainer();
container.RegisterType<IMonitor,Monitor>().RegisterType<INotify,SMSNotify>();
IMonitormonitor=container.Resolve<IMonitor>();
monitor.Alarm();
Console.ReadLine();
}
.csharpcode,.csharpcodepre{font-size:small;color:black;font-family:consolas,"CourierNew",courier,monospace;background-color:#ffffff;/*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{background-color:#f4f4f4;width:100%;margin:0em;}.csharpcode.lnum{color:#606060;}代码中我们注入的INotify实例是SMSNotify类的实例,然后调用monitor.Alrarm(),里面会调用notify.Send().
运行查看结果:



上面是针对单个构造函数的情况,如果有多个构造函数,需要指明哪个构造函数是需要被注入的,也即需要在指定被注入的构造函数加上attribute:InjectionConstructor

publicMonitor(INotifyn,stringname)
{
notify=n;
}
[InjectionConstructor]
publicMonitor(INotifyn)
{
notify=n;
}
.csharpcode,.csharpcodepre{font-size:small;color:black;font-family:consolas,"CourierNew",courier,monospace;background-color:#ffffff;/*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{background-color:#f4f4f4;width:100%;margin:0em;}.csharpcode.lnum{color:#606060;}

运行后可得到一样的结果.

2.PropertyInjection

通过属性注入,我们需要加上attribute:Dependency,使得Unity容器在获取类对象实例时,自动实例化该属性所依赖的对象,并注入到属性中。

修改Monitor类,实现下面的代码:

publicclassMonitor:IMonitor
{
[Dependency]
publicINotifyNotify{get;set;}
publicvoidAlarm()
{
Notify.Send();
}
}
.csharpcode,.csharpcodepre{font-size:small;color:black;font-family:consolas,"CourierNew",courier,monospace;background-color:#ffffff;/*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{background-color:#f4f4f4;width:100%;margin:0em;}.csharpcode.lnum{color:#606060;}.csharpcode{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{background-color:#ffffff;font-family:consolas,"CourierNew",courier,monospace;color:black;font-size:small}.csharpcodepre{margin:0em}.csharpcode.rem{color:#008000}.csharpcode.kwrd{color:#0000ff}.csharpcode.str{color:#006080}.csharpcode.op{color:#0000c0}.csharpcode.preproc{color:#cc6633}.csharpcode.asp{background-color:#ffff00}.csharpcode.html{color:#800000}.csharpcode.attr{color:#ff0000}.csharpcode.alt{background-color:#f4f4f4;margin:0em;width:100%}.csharpcode.lnum{color:#606060}

再在Main函数里面,修改原有的代码,这次我们让容器注入EmailNotify实例:

container.RegisterType<INotify,EmailNotify>();
.csharpcode,.csharpcodepre{font-size:small;color:black;font-family:consolas,"CourierNew",courier,monospace;background-color:#ffffff;/*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{background-color:#f4f4f4;width:100%;margin:0em;}.csharpcode.lnum{color:#606060;}

运行查看结果:


还有一个比较方便的地方,可以为Dependency特性指定名称,这样,在注入时,会将RegisterType所指定的对应名称的实体进行注入,例如:

publicclassMonitor:IMonitor
{
[Dependency("SMS")]
publicINotifyNotify{get;set;}
publicvoidAlarm()
{
Notify.Send();
}
}
.csharpcode,.csharpcodepre{font-size:small;color:black;font-family:consolas,"CourierNew",courier,monospace;background-color:#ffffff;/*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{background-color:#f4f4f4;width:100%;margin:0em;}.csharpcode.lnum{color:#606060;}
修改Main函数,在RegisterType函数中指定注入名称:

container.RegisterType<INotify,EmailNotify>("Email");
container.RegisterType<INotify,SMSNotify>("SMS");
.csharpcode,.csharpcodepre{font-size:small;color:black;font-family:consolas,"CourierNew",courier,monospace;background-color:#ffffff;/*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{background-color:#f4f4f4;width:100%;margin:0em;}.csharpcode.lnum{color:#606060;}运行查看结果:




3.MethodCallInjection

MethodCallInjection注入的时机和ConstructorInjection有一定的区别,构造函数注入,是在容器创建实例的时候,而方法调用注入,是在方法被调用的时候。实现方法调用注入,需要在指定注入的方法前加上attribute:InjectionMethod

修改Monitor类的代码如下:

publicclassMonitor:IMonitor
{
privateINotifynotify;
[InjectionMethod]
publicvoidGetNotify(INotifyn)
{
notify=n;
}
publicvoidAlarm()
{
notify.Send();
}
}
.csharpcode,.csharpcodepre{font-size:small;color:black;font-family:consolas,"CourierNew",courier,monospace;background-color:#ffffff;/*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{background-color:#f4f4f4;width:100%;margin:0em;}.csharpcode.lnum{color:#606060;}

在程序运行时,容器会自动实例化GetNotify方法所依赖的对象,并自动调用该方法,将其注入到方法中。

Main函数如下:

staticvoidMain(string[]args)
{
IUnityContainercontainer=newUnityContainer();
container.RegisterType<IMonitor,Monitor>();
container.RegisterType<INotify,EmailNotify>();
IMonitormonitor=container.Resolve<IMonitor>();
monitor.Alarm();
Console.ReadLine();
}
.csharpcode,.csharpcodepre{font-size:small;color:black;font-family:consolas,"CourierNew",courier,monospace;background-color:#ffffff;/*white-space:pre;*/}.csharpcodepre{margin:0em;}.csharpcode.rem{color:#008000;}.csharpcode.kwrd{color:#0000ff;}.csharpcode.str{color:#006080;}.csharpcode.op{color:#0000c0;}.csharpcode.preproc{color:#cc6633;}.csharpcode.asp{background-color:#ffff00;}.csharpcode.html{color:#800000;}.csharpcode.attr{color:#ff0000;}.csharpcode.alt{background-color:#f4f4f4;width:100%;margin:0em;}.csharpcode.lnum{color:#606060;}

运行查看结果:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: