当前位置: 首页 > news >正文

深圳住房和建设局网站认租申请网站开发包括哪些技术

深圳住房和建设局网站认租申请,网站开发包括哪些技术,网站开发和大数据开发区别,江门网站制作华企立方接口隔离原则、反射、特性、依赖注入 接口隔离原则 客户端不应该依赖它不需要的接口#xff1b;一个类对另一个类的依赖应该建立在最小的接口上。 五种原则当中的i 上一章中的接口#xff0c;即契约。 契约就是在说两件事#xff0c;甲方说自己不会多要#xff0c;乙方会在…接口隔离原则、反射、特性、依赖注入 接口隔离原则 客户端不应该依赖它不需要的接口一个类对另一个类的依赖应该建立在最小的接口上。 五种原则当中的i 上一章中的接口即契约。 契约就是在说两件事甲方说自己不会多要乙方会在契约里说自己不会少给。 乙方的不会少给是比较容易做到的作为服务的提供者实现一个接口的时候必须要实现接口里面的所有的方法如果没有实现所有的方法那么就会留下抽象方法自己就变成了一个抽象类那么仍然不能够实例化不是一个完整的服务提供者不是一个具体类。 实现了一个接口的完整服务提供者接口里面的方法是一定必须实现的。乙方不能少给这是强制性的硬性的规定。如果有方法没有实现完全编译器会检查到报错。 但是甲方不会多要却是一个软性的规定。所以说这是一个设计方面的问题需要使用设计原则去约束。 编译器没法去检查甲方是否多要了。 怎么才能知道甲方有没有多要呢 就看传给这个调用者的接口类型里有没有一直没被调用到的方法成员。如果有那就说明传进来的接口类型太大了太胖了。换句话来说当一个接口太胖了那么这个接口就是由两个或两个以上的本质不同的小接口合并起来的。所以当把大接口传进来之后只有一部分被调用到了而其中一部分就多余出来了。 这种大接口产生的原因的不同违反接口隔离原则所带来的不好的后果有两种。 第一种情况 胖接口设计的时候有问题把太多的功能包含到这个接口里了。把这种含有太多功能的接口传给调用者这样其中必然有一些功能调用不到。 这样子的话实现这个接口的类同时也违反了单一职责原则。 单一职责原则和接口隔离原则就是一个硬币的两面实际上就是一回事。 只是接口隔离原则是从服务的调用者的角度上来看。单一职责原则是站在服务提供者的角度上来看这个接口。 针对这种情况我们的解决方法就是将胖接口拆分。拆分小接口每个小接口都描述单一的功能。将本质不同的功能隔离开来然后再使用接口封装起来。 接口隔离原则就是服务的调用者不会多要 using System;namespace IspExample {/* 背景故事一个女生开车把车撞了* 她男朋友就哄她说下次给她买个坦克开*/internal class Program {static void Main(string[] args) {/* 我们想要给driver传入的是交通工具* 而不是想要传入能开炮的东西* 所以这里是ITank接口设计的不合理* 应该让ITank继承IVehicle*/Driver driver new Driver(new Car());driver.Drive();Console.ReadKey();}}/* 此时我们想让Driver类* 也能开Tank无论怎么改都要改动Driver类* 假如直接将IVehicle接口改为ITanke接口* 那么的确可以使用这个接口开坦克了但是这样* 就传进来了一个胖接口因为那个女生开坦克是当做车开的* Fire方法永远都不会被调用到了* 那么这个设计就违反了接口隔离原则* 解决办法就是将这个胖接口分成两个小接口*/class Driver {private IVehicle _vehicle;public Driver(IVehicle vehicle){_vehicle vehicle;}public void Drive() {_vehicle.Run();}}interface IVehicle {void Run();}class Car : IVehicle {public void Run() {Console.WriteLine(Car is running...);}}class Truck : IVehicle {public void Run() {Console.WriteLine(Truck is running...);}}interface IWeapon{void Fire();}/* 继承多个接口* 让ITank既继承自IVehicle又继承自IWeapon*/interface ITank : IVehicle, IWeapon{}//原本的ITank//interface ITank {// void Fire();// void Run();//}class LightTank : ITank {public void Fire() {Console.WriteLine(Boom!);}public void Run() {Console.WriteLine(Ka ka ka...);}}class MediumTank : ITank {public void Fire() {Console.WriteLine(Boom!!);}public void Run() {Console.WriteLine(Ka! ka! ka!...);}}class HeavyTank : ITank {public void Fire() {Console.WriteLine(Boom!!!);}public void Run() {Console.WriteLine(Ka!! ka!! ka!!...);}} }注在使用接口隔离原则和单一职责原则的时候不要过犹不及。如果玩得过火了的话就会产生很多很细碎的里面只有一个方法的接口和类 第二种情况 传入的接口有问题 本应该传一个小接口结果却传了一个将几个小接口合并起来的大接口。 这可能导致的问题就是把一些原本合格的服务的提供者当在门外了 就比如上一个例子中将Driver中的代码改成这种 那么就服务的提供者就没有了Car和Truck这两个了 class Driver {private ITank _tank;public Driver(ITank tank){_tank tank;}public void Drive() {_tank.Run();}}using System; using System.Collections;namespace IspExample2 {/* 之前有个例子中* 要求计算一组整数的和* 接口对服务调用者的约束就是这组整数能够被迭代* 也就是要求组整数的类型实现了IEnumerable接口*/internal class Program {static void Main(string[] args) {int[] nums1 { 1, 2, 3, 4, 5};ArrayList nums2 new ArrayList { 1, 2, 3, 4, 5 };var nums3 new ReadOnlyCollection(nums1);Console.WriteLine(Sum(nums1));Console.WriteLine(Sum(nums2));Console.WriteLine(Sum(nums3));/* 此时Sum这个方法是没办法处理nums3的* 因为设置的传入接口太胖了* 我们自己定义的集合只继承了IEnumerable接口* 而没有继承ICollection接口* 我们实际上只需要有迭代器的数据类型就能够传入* 所以不应该将传入的接口设置的这么胖*/}//static int Sum(ICollection nums)//原本设置的接口//下面是更改后的接口static int Sum(IEnumerable nums) {int sum 0;foreach (int i in nums) {sum i;}return sum;}}//有可能我们会设计出一个只实现了IEnumerable接口//而没有实现ICollection接口的类//自己写一个只读的集合class ReadOnlyCollection : IEnumerable {private int[] _array;public ReadOnlyCollection(int[] array){_array array;}//当外界迭代的时候需要给一个迭代器public IEnumerator GetEnumerator() {return new Enumerator(this);}public class Enumerator : IEnumerator {private ReadOnlyCollection _collection;private int _head;public Enumerator(ReadOnlyCollection collection){_collection collection;_head -1;/* 为什么要初始化为-1是有原因的* 迭代器是先调用判断是否越界的方法的* 而这个时候_head然后读取数组的值的时候就正好是1了*/}//只读属性public object Current {get {/* 这个属性需要拿到传进集合的数组* 所以是必须传进来一个collection的* 因为要求返回为Object所以还不能直接返回整数类型需要装箱*/Object o _collection._array[_head];//_head;return o;}}public bool MoveNext() {if (_head _collection._array.Length)return true;else return false;}public void Reset() {_head -1;}}} }第三种情况 专门用来展示显式接口实现 c#语言在接口隔离方面做得比其他语言都要好都要彻底。不但能做到接口隔离甚至还能做到把隔离出来的接口隐藏起来。 直到显式的使用这种接口类型的变量去引用一个实现了这个接口的具体类的实例的时候这个接口内的方法才能被看见才能被使用。 杀手不太冷的主角一面是暖男一面是冷酷无情的杀手 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace IspExample3 {/* 爱憎分明的杀手* 一面是暖男* 另一面是冷酷无情的*/internal class Program {static void Main(string[] args) {/* 从设计的角度来讲* 杀手在大街上不应该被人随便就能认出来* 即Kill()方法不应该能显示出来。* 如果一个接口的方法我们不想被人轻易地调用的话* 就不应该被人轻易地看到*/IKiller killer new WarmKiller();killer.Kill();//如果想要调用Love方法有以下几种方式var wk (IGentleman)killer;//var wk (WarmKiller)killer;//var wk killer as WarmKiller;wk.Love();}}interface IGentleman {void Love();}interface IKiller {void Kill();}class WarmKiller : IGentleman, IKiller {public void Love() {Console.WriteLine(I will love you for ever...); }/* 这个方法只有* IKiller类型引用的实例才能够调用这个方法*/void IKiller.Kill() {throw new NotImplementedException();}} }反射 反射不是C#语言的功能而是.Net框架的功能。 给一个对象反射能在不适用new操作符并且也不知道这个对象是什么静态类型的情况下创建出同类型的对象。 还能访问这个对象所带有的各个成员。 这就相当于进一步的解耦。因为在有new操作符的地方一定会跟类型一旦跟了类型就有了依赖。而且这种依赖还是紧耦合的。现在创建对象可以不适用new操作符可以不出现静态类型那么很多时候这个耦合甚至可弱到忽略不计。 Java开发体系也有这个机制。 可以说这是C#和Java这些托管类型语言与c/c这些原生类型语言最大的区别之一。 之前学的单元测试下面要学的依赖注入还有以后要学的泛型编程都是基于反射机制的。处于.Net和Java很底层的东西 这么底层的东西原理一定是很深奥很复杂。 但是呢.Net和C#在设计方面十分精妙。一般情况下我们在使用反射但是却感觉不到反射的存在。也就是说一般不会直接接触到反射大部分情况下都是使用一些已经封装好了的反射。 很多时候 程序的逻辑不是我们在写程序的时候就能够确定的。有时候这个逻辑是到了用户跟程序进行交互的时候才能确定。这个时候程序已经处在运行状态了。 也就是已经处在动态期了已经离开开发和编译环境了。如果我们在开发程序的时候就枚举用户所有能够进行的操作那么这个程序就会变得非常难维护。 或者我们不可能考虑到用户所能进行的所有情况。所以这个时候程序需要一种 以不变应万变的能力这个能力就是反射。 接下来用两个例子演示反射的神奇功能 反射原理 .Net平台有两个大的版本运行在windows上的.Net FramWork和可以扩平台的.NET Core。两个平台都有反射机制但是类库不太一样。现在使用的是.Net Core程序以后使用.Net FramWork去做反射的时候可以去查一下对应的API。 注意反射是动态的在内存中进行操作不要过多的使用反射机制不然会对程序的性能有所影响。 反射的原理 直接使用反射 using System.Reflection;namespace IspExample4 {/* 背景故事一个女生开车把车撞了* 她男朋友就哄她说下次给她买个坦克开*/internal class Program {static void Main(string[] args) {ITank tank new HeavyTank();//分割线/* new 后面的HeavyTank就是静态类型* GetType()方法可以获得静态类型的一些信息* 比如这个类型包含哪些方法哪些属性*/var t tank.GetType();/* Activator 是激活器* CreateInstance创建t类型的实例不知道具体的类型* 所以创建的是object类型的*/object o Activator.CreateInstance(t);//接下来使用反射//获得t类型的名叫Fire的方法MethodInfo fireMi t.GetMethod(Fire);MethodInfo runMi t.GetMethod(run);/* public object? Invoke(object? obj, object?[]? parameters);* 第二个参数指的是该方法的是否需要传入一些参数* 即该方法的参数列表*/fireMi.Invoke(o, null);runMi.Invoke(o, null);//这里是直接用的反射但是大部分情况下不会这么用}}class Driver {private IVehicle _vehicle;public Driver(IVehicle vehicle) {_vehicle vehicle;}public void Drive() {_vehicle.Run();}}interface IVehicle {void Run();}class Car : IVehicle {public void Run() {Console.WriteLine(Car is running...);}}class Truck : IVehicle {public void Run() {Console.WriteLine(Truck is running...);}}interface IWeapon {void Fire();}/* 继承多个接口* 让ITank既继承自IVehicle又继承自IWeapon*/interface ITank : IVehicle, IWeapon {}class LightTank : ITank {public void Fire() {Console.WriteLine(Boom!);}public void Run() {Console.WriteLine(Ka ka ka...);}}class MediumTank : ITank {public void Fire() {Console.WriteLine(Boom!!);}public void Run() {Console.WriteLine(Ka! ka! ka!...);}}class HeavyTank : ITank {public void Fire() {Console.WriteLine(Boom!!!);}public void Run() {Console.WriteLine(Ka!! ka!! ka!!...);}} }依赖注入 DI – Dependency Injection缩写是DI。 依赖反转原则的缩写也是DIDependency Inversion。 所以此DI非彼DI但是如果没有依赖反转也就没有依赖注入。 依赖反转是一个概念依赖注入是在这个概念的基础之上结合接口和反射机制所形成的一种应用。 依赖注入最重要的就是有一个容器Container现在使用的容器是 Microsoft.Extensions.DependencyInjection: 这是.NET Core内置的依赖注入容器提供了基本的DI功能1。也就是ServiceProvider把各种各样的类型和这些类型对应的接口放到容器里面。要实例的时候就向容器要。注册类型的时候还可以指定以后创建对象的时候是每次创建一个新对象还是创建一个单例模式每次都传同一个实例。这个容器怎么使用不在这里讲这里主要是看DependencyInjection怎么用。 这篇文章有助于理解依赖注入 [C#]理解和入门依赖注入 - 知乎 (zhihu.com) 依赖注入需要借助依赖注入的框架 引入名称空间 using System.Reflection; using Microsoft.Extensions.DependencyInjection;namespace IspExample4 {internal class Program {static void Main(string[] args) {/* 反射有一种很重要的容器* 叫做container* 这种容器就不在这里展开了*///服务的提供者var sc new ServiceCollection();/* typeof()方法用于获取类型的动态信息* AddKeyedScoped()方法有很多重载方法* 我们现在使用的这个* 第一个参数是基接口* 第二个是谁实现了这个接口* 假如我们没有使用反射程序中new 了很多的HeavyTank对象* 当有一天我们需要改成MediumTank的时候就需要改很多个地方* 而使用了反射就只需要改成* sc.AddKeyedScoped(typeof(ITank), typeof(MediumTank));* 现在这是基础用法*/sc.AddScoped(typeof(ITank), typeof(HeavyTank));var sp sc.BuildServiceProvider();//分割线ITank tank sp.GetServiceITank();tank.Fire();tank.Run();//更强大的功能sc.AddScoped(typeof(IVehicle), typeof(MediumTank));sc.AddScopedDriver();var ss sc.BuildServiceProvider();//分割线var driver ss.GetServiceDriver();driver.Drive();/* 当typeof()里面是Car的时候就是调用的Car的Run方法* 当typeof()里面是MediumTank的时候就是调用的MediumTank的Run方法*/}}class Driver {private IVehicle _vehicle;public Driver(IVehicle vehicle) {_vehicle vehicle;}public void Drive() {_vehicle.Run();}}interface IVehicle {void Run();}class Car : IVehicle {public void Run() {Console.WriteLine(Car is running...);}}class Truck : IVehicle {public void Run() {Console.WriteLine(Truck is running...);}}interface IWeapon {void Fire();}/* 继承多个接口* 让ITank既继承自IVehicle又继承自IWeapon*/interface ITank : IVehicle, IWeapon {}class LightTank : ITank {public void Fire() {Console.WriteLine(Boom!);}public void Run() {Console.WriteLine(Ka ka ka...);}}class MediumTank : ITank {public void Fire() {Console.WriteLine(Boom!!);}public void Run() {Console.WriteLine(Ka! ka! ka!...);}}class HeavyTank : ITank {public void Fire() {Console.WriteLine(Boom!!!);}public void Run() {Console.WriteLine(Ka!! ka!! ka!!...);}} }反射实现更松的耦合 这种更松的耦合一般使用在插件式编程中。好处是以主体程序为中心生成一个生态圈在这个生态圈中不断的更新主体程序于是就有人不断用插件往上面添加新功能并且从中获利。微软的很多东西都是带有这种生态圈的。 插件不与主体程序一起编译但是与主体程序一起工作往往由第三方提供。 主体程序和插件的关系就是不变和万变的关系。 主体程序会发布包含有程序开发接口、APIApplication Programming Interface程序开发包就是SDKSoftware Development Kit 反射开发会太自由了发布API可以约束开发者开发第三方插件同时也减轻开发者的一些劳动。 背景作为婴儿车的厂商要发明一种带面板的婴儿车。 这种面板有两排按钮第一排按钮是一些小动物的头像第二排按钮是一些数字按下小动物再按下数字就会发出对应次数的叫声。 然后我们设计一个接口可以让第三方自己定义动物的种类自己定义次数只需要一个USB接口就可以传进去。 纯反射 这是指不依赖于任何预先定义的接口或基类而是完全通过反射来检查和操作程序集、类型、成员等。这种方式可以在运行时动态地创建对象、调用方法、访问字段和属性甚至修改类型的行为。 主体程序 using System.IO; using System.Reflection; using System.Runtime.Loader; namespace BabyStroller.App {internal class Program {/* 主体程序会将Animals文件夹中* 第三方提供的插件加载进来* 去调用里面所有小动物的Voice(int num)方法* * 在主体程序中主要写* 利用反射加载插件拿到这些动物类然后创建实例* 并且调用实例的Voice方法* 这样的功能*/static void Main(string[] args) {Console.WriteLine(Environment.CurrentDirectory);//查看当前项目所处文件夹创建Animals文件夹var folder Path.Combine(Environment.CurrentDirectory, Animals);var files Directory.GetFiles(folder);var animalTypes new ListType();foreach ( var file in files) {var assembly AssemblyLoadContext.Default.LoadFromAssemblyPath(file);var types assembly.GetTypes();//得到加载进来的类型foreach ( var type in types) {//将含有Voice方法的类型加入到List中if (type.GetMethod(Voice) ! null){animalTypes.Add(type);}}}/* 开始运行游戏* 很多这种面板里面实际上就是个死循环* 除非强制关机、或者电池没电* 不然会一直转下去或者程序崩溃*/while (true) {//先打印有多少种动物for ( int i 0; i animalTypes.Count; i ) {Console.WriteLine(${i 1}.{animalTypes[i].Name});}Console.WriteLine();//选择动物Console.WriteLine(Please choose animal:);int index int.Parse( Console.ReadLine() );if( index animalTypes.Count || index 1 ) {Console.WriteLine(No such an animal.Try again!);continue;}//选择次数Console.WriteLine(How many times?);int times int.Parse( Console.ReadLine() );var t animalTypes[index - 1];var m t.GetMethod(Voice);//调用无参构造器实例化的对象var o Activator.CreateInstance(t);//调用该方法并传入参数列表数组m.Invoke(o, new object[] { times });}}} }第三方提供的类 分别有两个类库一共四个类为了演示效果没有做太多的类。 然后将第三方写的类库生成 再将两个类库生成的.dll文件添加到主体程序所在文件夹中的Animals文件夹中 于是就可以成功运行主体程序了。 使用接口的反射 创建一个接口或抽象类来定义操作的契约然后通过反射动态地加载和实例化实现了这些接口或抽象类的具体类。 使用纯反射很容易犯错误比如把方法名字写错了那么这个类就会被看不到了。 为了避免第三方开发犯错误以及减少开发成本。 首先制作接口文件 Attribute类用于保护那些未写完的动物类 然后将这个接口项目生成.dll文件。 接着在第三方中添加并使用主体程序发布的接口 然后修改部分代码 接着让这两个类库生成.dll文件 接把新build的.dll文件再次放到主程序所在的Animals文件夹中将原来的覆盖掉 然后修改主程序 using BabyStroller.SDK; using System.IO; using System.Reflection; using System.Runtime.Loader; namespace BabyStroller.App {internal class Program {/* 主体程序会将Animals文件夹中* 第三方提供的插件加载进来* 去调用里面所有小动物的Voice(int num)方法* * 在主体程序中主要写* 利用反射加载插件拿到这些动物类然后创建实例* 并且调用实例的Voice方法* 这样的功能*/static void Main(string[] args) {Console.WriteLine(Environment.CurrentDirectory);//查看当前项目所处文件夹创建Animals文件夹var folder Path.Combine(Environment.CurrentDirectory, Animals);var files Directory.GetFiles(folder);var animalTypes new ListType();foreach ( var file in files) {//遍历文件//得到一个程序集var assembly AssemblyLoadContext.Default.LoadFromAssemblyPath(file);//得到程序集中的类型集合var types assembly.GetTypes();//得到加载进来的类型foreach ( var t in types) {//遍历这个类型集合//将含有Voice方法的类型加入到List中//if (t.GetMethod(Voice) ! null){// animalTypes.Add(t);//}//有了SDK后就不用这种笨的办法了/* 现在只需要判断一下* 这个类型是不是IAnimal接口的实现类型* 并且没有被[UnFinishedAttribute]这个特性所修饰*/if(t.GetInterfaces().Contains(typeof(IAnimal))){var isUnfinished t.GetCustomAttributes(false).Any(a a.GetType() typeof(UnfinishedAttribute));animalTypes.Add(t);}}}/* 开始运行游戏* 很多这种面板里面实际上就是个死循环* 除非强制关机、或者电池没电* 不然会一直转下去或者程序崩溃*/while (true) {//先打印有多少种动物for ( int i 0; i animalTypes.Count; i ) {Console.WriteLine(${i 1}.{animalTypes[i].Name});} Console.WriteLine();//选择动物Console.WriteLine(Please choose animal:);int index int.Parse( Console.ReadLine() );if( index animalTypes.Count || index 1 ) {Console.WriteLine(No such an animal.Try again!);continue;}//选择次数Console.WriteLine(How many times?);int times int.Parse( Console.ReadLine() );var t animalTypes[index - 1];var m t.GetMethod(Voice);//调用无参构造器实例化的对象var o Activator.CreateInstance(t);//调用该方法并传入参数列表数组//m.Invoke(o, new object[] { times });var a o as IAnimal;a.Voice(times);}}} }特性 其作用就是在使用反射的时候通过反射拿到一个方法或一个类看有没有被某个Attribute所修饰然后再做决定是否调用是否保留等等。这就是Attribute的用处。 之前单元测试中的带框的英文就是Attribute 可以看看这篇文章 【Unity】Unity C#基础十二特性Attribute、反射Reflection_unity attributeusage-CSDN博客 补充 Activator是一个用于快速实例化对象的类。比如用于将Type信息类对象快速实例化为对象。通过反射来实例化。 Type的一些用法 using System.Reflection; using System.Runtime.CompilerServices;namespace AllReflectionExample {class Test {private int i 1;public int j 0;public string str Hello;public Test(){}public Test(int i) {this.i i;}//对构造器的重载public Test(int i, string str): this(i){this.str str;}public void Speak() {Console.WriteLine(i);}}internal class Program {static void Main(string[] args) {//Type 是类的信息类#region 获取Type的方式// 1.object中的GetType()方法可以获得Typeint num 5;Type t num.GetType();Console.WriteLine(t);// 2.通过typeof关键字 传入类名Type t2 typeof(int);Console.WriteLine(t2);// 3.通过类的名字 不过必须得是包含名称空间的全称不然找不到Type t3 Type.GetType(System.Int32);Console.WriteLine(t3);#endregion#region 获取类中的所有公共成员//先得到信息类Type t4 typeof(Test);//得到公共成员需要引用命名空间using System.Reflection;MemberInfo[] infos t4.GetMembers();for (int i 0; i infos.Length; i) {Console.WriteLine(infos[i]);}#endregion#region 获取类的公共构造函数并调用//1.获取所有构造函数ConstructorInfo[] ctors t.GetConstructors();for (int i 0; i ctors.Length; i) { Console.WriteLine(ctors[i]);}//2.获取其中一个构造函数 并执行//得构造函数传入 Type数组 数组中内容按顺序是参数类型//执行构造函数传入 object数组 表示按顺序传入的参数//2 - 1得到无参构造ConstructorInfo info t.GetConstructor(new Type[0]);//执行无参构造函数 没有参数 传nullTest obj info.Invoke(null) as Test;Console.WriteLine(obj.j);//2 - 2得到有参构造ConstructorInfo info2 t.GetConstructor(new Type[] {typeof(int)});obj info2.Invoke(new object[] {2}) as Test;Console.WriteLine(obj.str);ConstructorInfo info3 t.GetConstructor(new Type[] { typeof(int), typeof(string) });obj info3.Invoke(new object[] { 4, shaoi}) as Test;Console.WriteLine(obj.str);#endregion//还可以通过这种方式获取类的公共其他成员并调用}} }Assembly 程序集类主要用来加载其他程序集加载后才能用type来使用其他程序集中的信息。 如果想要使用不是自己程序集中的内容就需要先加载该程序集比如dll库文件。 如果想要详细了解可以去看其他文章这里仅做介绍。 在这里我想说一下知识是学不完的但是当我们学到某种程度就足够我们使用了。 如果以后再遇到需要再深入学习到那个时候可以再学。
http://www.hkea.cn/news/14477179/

相关文章:

  • 南宁老牌网站建设公司小游戏开发
  • 广州市城乡建设部网站首页软件技术介绍
  • 深圳极速网站建设推荐购买网站广告位
  • 河南安阳网站建设电气网站设计
  • 重庆网站建设 制作 设计 优惠价合作做网站
  • 福建省建设干部网站公司法人查询系统
  • 班级网站 程序大型网站开发什么书籍好
  • 三门峡住房和建设局网站泰安做网站公司哪家好
  • 开发建设网站需要什么人才做标签这个网站刷单安全吗
  • 杭州自助建站软件企业为什么建立企业网站
  • 自己做产品网站wordpress企业网站制作
  • 电子报 网站开发如何做公司网站制作
  • 灵芝住房和城乡建设局局网站智能制造工程
  • 大气黑色机械企业网站源码石家庄建设厅官方网站
  • 乡村文化建设网站栏目设置wordpress update_post_meta
  • 网站加上视频对seo影响创新的武进网站建设
  • 宁波手机网站制作媚娘直播
  • 网站建设竣工验收报告动漫制作专业简历
  • google 网站突然一条收录也没有wordpress下载弹窗插件
  • 什么东西可以做网站沈阳建信建设工程有限公司
  • 莱州市招聘网站建设网站的要求吗
  • 仿网站被封怎么办建设音乐网站
  • 哈尔滨网站建设方案开发上海网站建设管理系统
  • 网站开发_超速云河北邢台新河网
  • seo优化网站词绵阳住房和城乡建设部网站
  • 网站登录页面模板下载企业网站开发综合实训
  • asp商业网站源码华为手机开发者模式怎么关闭
  • 做网站是学什么编程语言为网站添加统计
  • 做网站技术要求怎么写石排镇网站建设
  • 简单好看个人主页网站模板wordpress管理地址