02-Unity深入浅出(二)
一. Unity声明周期
Unity容器为我们提供了6种生命周期,便于我们根据项目需求来选择使用。
(1). 瞬时。默认省略即为瞬时,无论单线程还是多线程,每次都重新创建对象。new TransientLifetimeManager()
(2). 容器单例。只要是同一个Unity容器创建的同一个类型的对象,无论是线程之间还是单线程内都是单例的。new ContainerControlledLifetimeManager()
(3). 线程单例。同一个线程内创建的同一个类型的对象,都是单例的。但线程之间不是单例的。new PerThreadLifetimeManager()
(4). 分级容器单例。unity容器可以创建很多子容器,每个子容器无论怎么创建对象,都是单例的,但是子容器之间不是单例的。new HierarchicalLifetimeManager()
(5). 外部可释放单例。在不销毁的情况下,每次Resolve都会返回同一个对象,即是单例的;销毁后,重新创建一个新的对象,销毁后创建的新对象又是单例的。new ExternallyControlledLifetimeManager()
(6).循环引用。new PerResolveLifetimeManager(),不推荐使用,暂不测试。
下面同样先通过代码的形式来测试以上几种情况。
(1). 瞬时
{
Console.WriteLine("------------------- 05-Unity的生命周期-瞬时(1) -------------------");
IUnityContainer container = new UnityContainer();
container.RegisterType<IPhone, AndroidPhone>(new TransientLifetimeManager()); //默认省略就是瞬时的 //下面测试多线程中创建的对象是否是单例的(iphone1是一个线程 iphone2和iphone3是同一个线程)
IPhone iphone1 = null;
Action act1 = () =>
{
iphone1 = container.Resolve<IPhone>();
Console.WriteLine("iphone1由线程id={0}创建", Thread.CurrentThread.ManagedThreadId);
};
var result1 = act1.BeginInvoke(null, null);
IPhone iphone2 = null;
Action act2 = () =>
{
iphone2 = container.Resolve<IPhone>();
Console.WriteLine("iphone2由线程id={0}创建", Thread.CurrentThread.ManagedThreadId);
};
//在act2的异步回调中创建iphone3(iphone2和iphone3是一个线程创建的)
IPhone iphone3 = null;
var result2 = act2.BeginInvoke(t =>
{
iphone3 = container.Resolve<IPhone>();
Console.WriteLine("iphone3由线程id={0}创建", Thread.CurrentThread.ManagedThreadId);
//代表两个不同线程创建的对象
Console.WriteLine("iphone1和iphone2是否相等?{0}", object.ReferenceEquals(iphone1, iphone2));
//代表同一个线程创建的两个对象
Console.WriteLine("iphone2和iphone3是否相等?{0}", object.ReferenceEquals(iphone2, iphone3));
}, null); //线程等待
act1.EndInvoke(result1);
act2.EndInvoke(result2); //总结:瞬时创建无论单个线程内还是多个线程之间,都不是单例的,每次调用都要重新创建对象 }
分析:iphone2和iphone3是同一个线程创建的,iphone1是单独一个线程创建的。经过结果分析:iphone1和iphone2不相等,iphone2和iphone3不相等,证明:瞬时容器无论是线程内,还是线程与线程之间每次都是重新创建的,都不是单例。
(2). 容器单例
{
Console.WriteLine("------------------- 05-Unity的生命周期-容器单例(2) -------------------");
IUnityContainer container = new UnityContainer();
container.RegisterType<IPhone, AndroidPhone>(new ContainerControlledLifetimeManager()); //下面测试多线程中创建的对象是否是单例的(iphone1是一个线程 iphone2和iphone3是同一个线程)
IPhone iphone1 = null;
Action act1 = () =>
{
iphone1 = container.Resolve<IPhone>();
Console.WriteLine("iphone1由线程id={0}创建", Thread.CurrentThread.ManagedThreadId);
};
var result1 = act1.BeginInvoke(null, null);
IPhone iphone2 = null;
Action act2 = () =>
{
iphone2 = container.Resolve<IPhone>();
Console.WriteLine("iphone2由线程id={0}创建", Thread.CurrentThread.ManagedThreadId);
};
//在act2的异步回调中创建iphone3(iphone2和iphone3是一个线程创建的)
IPhone iphone3 = null;
var result2 = act2.BeginInvoke(t =>
{
iphone3 = container.Resolve<IPhone>();
Console.WriteLine("iphone3由线程id={0}创建", Thread.CurrentThread.ManagedThreadId);
//代表两个不同线程创建的对象
Console.WriteLine("iphone1和iphone2是否相等?{0}", object.ReferenceEquals(iphone1, iphone2));
//代表同一个线程创建的两个对象
Console.WriteLine("iphone2和iphone3是否相等?{0}", object.ReferenceEquals(iphone2, iphone3));
}, null); //线程等待
act1.EndInvoke(result1);
act2.EndInvoke(result2); //总结:容器单例:只要是同一个Unity容器创建的一个类,无论是线程之间还是单线程内都是单例的
}
分析:iphone2和iphone3是同一个线程创建的,iphone1是单独一个线程创建的。经过结果分析:iphone1和iphone2相等,iphone2和iphone3相等,证明:容器单例无论是线程内,还是线程与线程之间都是单例的。
(3). 线程单例
{
Console.WriteLine("------------------- 05-Unity的生命周期-线程单例(3) -------------------");
IUnityContainer container = new UnityContainer();
container.RegisterType<IPhone, AndroidPhone>(new PerThreadLifetimeManager()); //下面测试多线程中创建的对象是否是单例的(iphone1是一个线程 iphone2和iphone3是同一个线程)
IPhone iphone1 = null;
Action act1 = () =>
{
iphone1 = container.Resolve<IPhone>();
Console.WriteLine("iphone1由线程id={0}创建", Thread.CurrentThread.ManagedThreadId);
};
var result1 = act1.BeginInvoke(null, null);
IPhone iphone2 = null;
Action act2 = () =>
{
iphone2 = container.Resolve<IPhone>();
Console.WriteLine("iphone2由线程id={0}创建", Thread.CurrentThread.ManagedThreadId);
};
//在act2的异步回调中创建iphone3(iphone2和iphone3是一个线程创建的)
IPhone iphone3 = null;
var result2 = act2.BeginInvoke(t =>
{
iphone3 = container.Resolve<IPhone>();
Console.WriteLine("iphone3由线程id={0}创建", Thread.CurrentThread.ManagedThreadId);
//代表两个不同线程创建的对象
Console.WriteLine("iphone1和iphone2是否相等?{0}", object.ReferenceEquals(iphone1, iphone2));
//代表同一个线程创建的两个对象
Console.WriteLine("iphone2和iphone3是否相等?{0}", object.ReferenceEquals(iphone2, iphone3));
}, null); //线程等待
act1.EndInvoke(result1);
act2.EndInvoke(result2); /*
* 总结:线程单例:同一个线程内,eg:iphone2和iphone3,都是AndroidPhone类型,他是单例的,不重复创建,但是线程与线程之间创建的对象就不是单例的了。
* 与框架中EF上下文 利用CallContext保存的原理一致
一般来说不建议在使用RegisterInstance对已存在的对象注册关系时使用PerThreadLifetimeManager,因为此时的对象已经在一个线程内创建了,如果再使用这个生命周期管理器,将无法保证其正确调用
*/
}
分析:iphone2和iphone3是同一个线程创建的,iphone1是单独一个线程创建的。经过结果分析:iphone1和iphone2不相等,iphone2和iphone3相等,证明:线程单例在线程内是单例的,但线程与线程之间不是单例的。
(4). 分级容器单例
{
Console.WriteLine("------------------- 05-Unity的生命周期-分级容器单例(4) -------------------");
//父Unity容器
IUnityContainer container = new UnityContainer();
container.RegisterType<IPhone, AndroidPhone>(new HierarchicalLifetimeManager());
//子Unity容器1
IUnityContainer childContainer1 = container.CreateChildContainer();
childContainer1.RegisterType<IPhone, AndroidPhone>(new HierarchicalLifetimeManager());
//子Unity容器2
IUnityContainer childContainer2 = container.CreateChildContainer();
childContainer2.RegisterType<IPhone, AndroidPhone>(new HierarchicalLifetimeManager()); IPhone iphone1 = container.Resolve<IPhone>();
IPhone iphone2 = container.Resolve<IPhone>();
IPhone iphone3 = childContainer1.Resolve<IPhone>();
IPhone iphone4 = childContainer1.Resolve<IPhone>();
IPhone iphone5 = childContainer2.Resolve<IPhone>();
IPhone iphone6 = childContainer2.Resolve<IPhone>(); Console.WriteLine("父容器container第一次创建的对象的hashCode值:{0}", iphone1.GetHashCode());
Console.WriteLine("父容器container第二次创建的对象的hashCode值:{0}", iphone2.GetHashCode()); Console.WriteLine("子容器childContainer1第一次创建的对象的hashCode值:{0}", iphone3.GetHashCode());
Console.WriteLine("子容器childContainer1第二次创建的对象的hashCode值:{0}", iphone4.GetHashCode()); Console.WriteLine("子容器childContainer2第一次创建的对象的hashCode值:{0}", iphone5.GetHashCode());
Console.WriteLine("子容器childContainer2第二次创建的对象的hashCode值:{0}", iphone6.GetHashCode()); //总结:unity容器可以创建很多子容器,每个子容器无论怎么创建对象,都是单例的,但是子容器之间不是单例的。
//好处:我们可以对于不同生命周期的对象放在不同容器中,如果一个子容器释放,不会影响其它子容器的对象,
//但是如果根节点处的父容器被释放,所有的子容器都将被释放
}
分析:每个子容器创建的对象的值是相同的,子容器之间创建的对象是不同的
(5). 外部可释放单例
{
Console.WriteLine("------------------- 05-Unity的生命周期-外部可释放单例(5) -------------------");
IUnityContainer container = new UnityContainer();
container.RegisterType<IPhone, AndroidPhone>(new ExternallyControlledLifetimeManager()); IPhone iphone1 = container.Resolve<IPhone>();
IPhone iphone2 = container.Resolve<IPhone>();
Console.WriteLine("第一次调用:{0}", iphone1.GetHashCode());
Console.WriteLine("第二次调用:{0}", iphone2.GetHashCode()); Console.WriteLine("------------------ GC回收过后 ------------------------");
iphone1 = iphone2 = null;
GC.Collect();
Console.WriteLine("回收后第一次调用:{0}", container.Resolve<IPhone>().GetHashCode());
Console.WriteLine("回收后第二次调用:{0}", container.Resolve<IPhone>().GetHashCode()); //总结:在不销毁的情况下,每次Resolve都会返回同一个对象,即是单例的;销毁后,重新创建一个新的对象
//销毁后创建的新对象又是单例的。 }
分析:回收前创建的对象都是单例的,回收后重新创建的对象还是单例的。
(6). 循环引用
不推荐使用。
二. 配置文件的形式实现
Unity在实际开发环境中, 注册类型(包括注入对象、声明生命周期)的那一步,都是在配置文件中声明的,这才是Unity的真谛所在,才能实现真正意义上的解耦,只需将Service层的DLL文件复制到主程序bin文件夹中即可,不需要直接添加对Service层的引用。
该实现形式主要分为以下几步:
(1). 编写配置文件的内容。(需要将该配置文件的属性改为“始终复制”,使其可以生成到主程序的bin文件中)。
(2). 固定4行代码读取配置文件。
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\UnityConfig.xml");//找配置文件的路径
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
(3). 声明Unity容器,并与配置文件关联。
IUnityContainer container = new UnityContainer();
section.Configure(container, "testContainer");
(4). Unity解析对象。
(一).配置文件的书写形式
下面整个Xml均为Untiy的配置文件,声明Unity容器有两种方式:①先定义别名,类型名称和程序集名称均写在别名中,然后在容器中与别名进行关联。 ②直接在容器中的register节点写类型名称和程序集名称。
另外可以在register节点中添加<lifetime>节点,可以声明Unity的声明周期。
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity>
<!-- unity容器支持AOP扩展 -->
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration"/> <!--定义类型别名-->
<aliases>
<!--type的两个参数分别是:类型名称和DLL程序集的名称-->
<!--alias中的别名供container容器中使用-->
<add alias="IPhone" type="Ypf.Interface.IPhone,Ypf.Interface" />
<add alias="AndroidPhone" type="Ypf.Service.AndroidPhone, Ypf.Service" />
<add alias="ApplePhone" type="Ypf.Service.ApplePhone, Ypf.Service" /> <add alias="IMicrophone" type="Ypf.Interface.IMicrophone, Ypf.Interface" />
<add alias="IHeadphone" type="Ypf.Interface.IHeadphone, Ypf.Interface" />
<add alias="IPower" type="Ypf.Interface.IPower, Ypf.Interface" />
<add alias="Microphone" type="Ypf.Service.Microphone, Ypf.Service" />
<add alias="Headphone" type="Ypf.Service.Headphone, Ypf.Service" />
<add alias="Power" type="Ypf.Service.Power, Ypf.Service" />
</aliases> <!-- unity容器配置注册节点-->
<containers>
<!--容器配置方式一:类型名称和程序集名称全部写在容器中-->
<container name="testContainer">
<!-- type和mapTo中的两个参数分别是:类型名称和DLL程序集的名称 -->
<!-- type和mapTo分别对应RegisterType<A,B> 中的A和B两个值-->
<register type="Ypf.Interface.IPhone,Ypf.Interface" mapTo="Ypf.Service.AndroidPhone, Ypf.Service"/>
<register type="Ypf.Interface.IPhone,Ypf.Interface" mapTo="Ypf.Service.AndroidPhone, Ypf.Service" name="android"/>
<register type="Ypf.Interface.IPhone,Ypf.Interface" mapTo="Ypf.Service.ApplePhone, Ypf.Service" name="apple"/>
<!--以下三个属于依赖注入的内容了-->
<register type="Ypf.Interface.IMicrophone, Ypf.Interface" mapTo="Ypf.Service.Microphone, Ypf.Service"/>
<register type="Ypf.Interface.IHeadphone, Ypf.Interface" mapTo="Ypf.Service.Headphone, Ypf.Service"/>
<register type="Ypf.Interface.IPower, Ypf.Interface" mapTo="Ypf.Service.Power, Ypf.Service"/>
</container> <!--容器配置方式二:配合aliases类型别名进行使用-->
<container name="testContainer2">
<!-- type和mapTo中的两个参数分别是:类型名称和DLL程序集的名称 -->
<!-- type和mapTo分别对应RegisterType<A,B> 中的A和B两个值-->
<register type="IPhone" mapTo="AndroidPhone"/>
<register type="IPhone" mapTo="AndroidPhone" name="android"/>
<register type="IPhone" mapTo="ApplePhone" name="apple"/>
<!--以下三个属于依赖注入的内容了-->
<register type="IMicrophone" mapTo="Microphone"/>
<register type="IHeadphone" mapTo="Headphone"/>
<register type="IPower" mapTo="Power"/>
</container> <!--在配置文件中配置Unity的生命周期 以AndroidPhone为例-->
<container name="testContainer3">
<!-- 下面是用别名的形式, type和mapto分别对应上面的别名 -->
<!-- 声明周期放到该节点里面用lifetime进行包裹,不用别名的形式道理一样,也是这种方式进行包裹-->
<register type="IPhone" mapTo="AndroidPhone">
<!--1. 瞬时的 默认即为瞬时的-->
<!--<lifetime type="TransientLifetimeManager" />-->
<!--2. 容器单例的-->
<!--<lifetime type="ContainerControlledLifetimeManager" />-->
<!--3. 线程单例-->
<!--<lifetime type="PerThreadLifetimeManager" />-->
<!--4. 分级容器单例-->
<lifetime type="hierarchical" />
<!--其它两种不做测试-->
</register>
</container>
</containers> </unity>
</configuration>
(二). 容器配置方式一:类型名称和程序集名称全部写在容器中
{
//Console.WriteLine("---------------------------容器配置方式一:类型名称和程序集名称全部写在容器中--------------------------------------");
///*(一). 该配置文件包含依赖注入、指定命名注册*/ ////1. 编写配置文件中的内容(需要将UnityConfig.xml的属性改为始终赋值,使其能生成到bin下)
////2. 固定的4行代码读取配置文件
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\UnityConfig.xml");//找配置文件的路径
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
////3. Unity层次的步骤
IUnityContainer container = new UnityContainer();
section.Configure(container, "testContainer"); IPhone iphone1 = container.Resolve<IPhone>();
iphone1.Call();
IPhone iphone2 = container.Resolve<IPhone>("apple");
iphone2.Call();
IPhone iphone3 = container.Resolve<IPhone>("android");
iphone3.Call(); }
(三). 容器配置方式二:配合aliases类型别名进行使用
{
//Console.WriteLine("---------------------------容器配置方式二:配合aliases类型别名进行使用--------------------------------------");
///*(一). 该配置文件包含依赖注入、指定命名注册*/ //1. 编写配置文件中的内容(需要将UnityConfig.xml的属性改为始终赋值,使其能生成到bin下)
//2. 固定的4行代码读取配置文件
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\UnityConfig.xml");//找配置文件的路径
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
//3. Unity层次的步骤
IUnityContainer container = new UnityContainer();
section.Configure(container, "testContainer2"); IPhone iphone1 = container.Resolve<IPhone>();
iphone1.Call();
IPhone iphone2 = container.Resolve<IPhone>("apple");
iphone2.Call();
IPhone iphone3 = container.Resolve<IPhone>("android");
iphone3.Call();
}
(四). Unity的生命周期-容器单例--配置文件的方式 (可以注释配置文件中的节点,来测试其他情况:瞬时、线程单例)
Console.WriteLine("------------------- Unity的生命周期-容器单例--配置文件的方式 -------------------");
//1. 编写配置文件中的内容(需要将UnityConfig.xml的属性改为始终赋值,使其能生成到bin下)
//2. 固定的4行代码读取配置文件
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\UnityConfig.xml");//找配置文件的路径
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
//3. Unity层次的步骤
IUnityContainer container = new UnityContainer();
section.Configure(container, "testContainer3"); //下面测试多线程中创建的对象是否是单例的(iphone1是一个线程 iphone2和iphone3是同一个线程)
IPhone iphone1 = null;
Action act1 = () =>
{
iphone1 = container.Resolve<IPhone>();
Console.WriteLine("iphone1由线程id={0}创建", Thread.CurrentThread.ManagedThreadId);
};
var result1 = act1.BeginInvoke(null, null);
IPhone iphone2 = null;
Action act2 = () =>
{
iphone2 = container.Resolve<IPhone>();
Console.WriteLine("iphone2由线程id={0}创建", Thread.CurrentThread.ManagedThreadId);
};
//在act2的异步回调中创建iphone3(iphone2和iphone3是一个线程创建的)
IPhone iphone3 = null;
var result2 = act2.BeginInvoke(t =>
{
iphone3 = container.Resolve<IPhone>();
Console.WriteLine("iphone3由线程id={0}创建", Thread.CurrentThread.ManagedThreadId);
//代表两个不同线程创建的对象
Console.WriteLine("iphone1和iphone2是否相等?{0}", object.ReferenceEquals(iphone1, iphone2));
//代表同一个线程创建的两个对象
Console.WriteLine("iphone2和iphone3是否相等?{0}", object.ReferenceEquals(iphone2, iphone3));
}, null); //线程等待
act1.EndInvoke(result1);
act2.EndInvoke(result2);
(五). Unity的生命周期-分级容器-配置文件
Console.WriteLine("------------------- Unity的生命周期-分级容器--配置文件的方式 -------------------");
//1. 编写配置文件中的内容(需要将UnityConfig.xml的属性改为始终赋值,使其能生成到bin下)
//2. 固定的4行代码读取配置文件
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\UnityConfig.xml");//找配置文件的路径
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
//3. Unity层次的步骤
IUnityContainer container = new UnityContainer();
section.Configure(container, "testContainer3"); //子Unity容器1
IUnityContainer childContainer1 = container.CreateChildContainer(); //子Unity容器2
IUnityContainer childContainer2 = container.CreateChildContainer(); IPhone iphone1 = container.Resolve<IPhone>();
IPhone iphone2 = container.Resolve<IPhone>();
IPhone iphone3 = childContainer1.Resolve<IPhone>();
IPhone iphone4 = childContainer1.Resolve<IPhone>();
IPhone iphone5 = childContainer2.Resolve<IPhone>();
IPhone iphone6 = childContainer2.Resolve<IPhone>(); Console.WriteLine("父容器container第一次创建的对象的hashCode值:{0}", iphone1.GetHashCode());
Console.WriteLine("父容器container第二次创建的对象的hashCode值:{0}", iphone2.GetHashCode()); Console.WriteLine("子容器childContainer1第一次创建的对象的hashCode值:{0}", iphone3.GetHashCode());
Console.WriteLine("子容器childContainer1第二次创建的对象的hashCode值:{0}", iphone4.GetHashCode()); Console.WriteLine("子容器childContainer2第一次创建的对象的hashCode值:{0}", iphone5.GetHashCode());
Console.WriteLine("子容器childContainer2第二次创建的对象的hashCode值:{0}", iphone6.GetHashCode());
三. Unity实现AOP
待定
02-Unity深入浅出(二)的更多相关文章
- Java 之 I/O 系列 02 ——序列化(二)
Java 之 I/O 系列 目录 Java 之 I/O 系列 01 ——基础 Java 之 I/O 系列 02 ——序列化(一) Java 之 I/O 系列 02 ——序列化(二) 继续上篇的第二个问 ...
- java代理的深入浅出(二)-CGLIB
java代理的深入浅出(二)-CGLIB 1.基本原理 CGLIB的原理就是生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法.在子类中拦截所有父类方法的调用,拦截下来交给设置的Me ...
- LeetCode初级算法--树02:验证二叉搜索树
LeetCode初级算法--树02:验证二叉搜索树 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn.ne ...
- 依賴注入入門——Unity(二)
參考博客文章http://www.cnblogs.com/kebixisimba/category/130432.html http://www.cnblogs.com/qqlin/tag/Unity ...
- 漫话Unity(二)
三.Unity编辑器介绍 Unity是一个商业级的3d游戏引擎.一个引擎的专业程度事实上并非体如今它多么牛b 的次世代效果,说实话那些效果即便你会用也不敢用.由于没有哪个手机是次世代的. 游戏引擎的专 ...
- Gvr SDK for Unity 分析(二)
前言 关于google vr sdk的具体使用,传送门 Gvr SDK for Unity 分析(一) Google Daydream平台已经整合进Google VR SDK 本文环境:Unity5. ...
- Unity(二)生命周期LifetimeManager
描述:Unity的生命周期是注册的类型对象的生命周期,而Unity默认情况下会自动帮我们维护好这些对象的生命周期,我们也可以显示配置对象的生命周期,Unity将按照配置自动管理. //创建一个Unit ...
- unity基本操作二
一:error1,先断网再启动点击Manual Activation点击Save License生成相应的alf文件2,联网打开https://license.unity3d.com/manual上传 ...
- 02 mysql 基础二 (进阶)
mysql 基础二 阶段一 表约束 1.not null 非空约束 例子: create table tb1( id int, name varchar(20) not null ); 注意 空字符不 ...
随机推荐
- WC2019 tree
WC2019唯一一道正常的题,考场上没什么想法,也只拿到了暴力分.搞了一天终于做完了. 前置知识:purfer序,多项式exp或分治FTT. 对于\(type=0\)的,随便维护下,算下联通块即可. ...
- [luogu3246][bzoj4540][HNOI2016]序列【莫队+单调栈】
题目描述 给定长度为n的序列:a1,a2,...,an,记为a[1:n].类似地,a[l:r](1<=l<=r<=N)是指序列:al,al+1,...,ar-1,ar.若1<= ...
- 每天一个Linux命令(05):tail命令
tail命令用于输入文件中的尾部内容.tail命令默认在屏幕上显示指定文件的末尾10行.如果给定的文件不止一个,则在显示的每个文件前面加一个文件名标题.如果没有指定文件或者文件名为"-&qu ...
- 单片机的编程语言和开发环境 LET′S TRY“嵌入式编程”: 3 of 6
单片机的编程语言和开发环境 LET′S TRY“嵌入式编程”: 3 of 6 本连载讲解作为嵌入式系统开发技术人员所必需具备的基础知识.这些基础知识是硬件和软件技术人员都应该掌握的共通技术知识. 在“ ...
- CAN协议,系统结构和帧结构
CAN:Controller Area Network,控制器局域网 是一种能有效支持分布式控制和实时控制的串行通讯网络. CAN-bus: Controller Area Network-bus,控 ...
- 工厂方法模式(Factory Method)和抽象工厂模式(Abstact Factory)
分类 工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的.工厂模式在<Java 与模式>中分为三类:1)简单工厂模式(Simple Facto ...
- MacBook上使用ssh localhost被拒绝
最开始以为没有装sshd呢,实际上不是.又查了些方法,有些人居然把Linux上访问失败的方法直接照搬,让我建立公私钥.都没有成功.最后找到了解决方法,原来是由于苹果的安全限制,限制了这个功能. sud ...
- [HEOI2014]平衡
[HEOI2014]平衡 转化为求选择k个数,和为(n+1)*k的方案数 保证,每个数[1,2*n+1]且最多选择一次. 限制k个很小,所以用整数划分的第二种方法 f[i][j],用了i个,和为j 整 ...
- 【CH6802】车的放置
题目大意:给定一个 N*M 的棋盘,棋盘上有些点不能放置任何东西,现在在棋盘上放置一些车,问最多可以放置多少个车而不会互相攻击. 题解:将放置一个车看作连接一条无向边,因为每一行和每一列之间只能放置一 ...
- AOP实践--利用MVC5 Filter实现登录状态判断
AOP有的翻译"面向切面编程",有的是"面向方面编程".其实名字不重要,思想才是核心,mvc的Filter让我们很 方便达到这种面向方面编程,就是在现有代码的基 ...