前言

  经过前面EF的《第一篇》与《第二篇》,我们的数据层功能已经较为完善了,但有不少代码相似度较高,比如负责实体映射的 EntityConfiguration,负责仓储操作的IEntityRepository与EntityRepository。而且每添加一个实体类型,就要手动去添加一套相应的代码,也是比较累的工作。如果能有一个根据实体类型自动生成这些相似度较高的代码的解决方案,那将会减少大量的无聊的工作。

  VS提供的“文本模板”(俗称T4)功能,就是一个较好的解决方案。要添加一个实体类型,只要把实体类型定义好,然后运行一下定义好的T4模板,就可以自动生成相应的类文件。

工具准备

  为了更好的使用 T4模板 功能,我们需要给VS安装如下两个插件:

  • Devart T4 Editor:为VS提供智能提示功能。
  • T4 Toolbox:在生成多文件时很有用。

T4代码生成预热

单文件生成:HelloWorld.cs

  下面,我们先来体验一个最简单的T4代码生成功能,输出一个最简单的类文件。

  首先,在 GMF.Demo.Core.Data中 添加一个名为 T4 的文件夹,用于存放生成本工程内的代码的T4模板文件。并在其中添加一个名为 HelloWorld.tt的“文本模板”的项。

  HelloWorld.tt定义如下:

 <#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
using System; namespace GMF.Demo.Core.Data.T4
{
public class HelloWorld
{
private string _word; public HelloWorld(string word)
{
_word = word;
}
}
}

  直接保存文件(T4的生成将会在保存模板,模板失去焦点等情况下自动触发生成。),将会在模板的当前位置生成一个同名的类文件:

  HelloWorld.cs的内容如下:

 using System;

 namespace GMF.Demo.Core.Data.T4
{
public class HelloWorld
{
private string _word; public HelloWorld(string word)
{
_word = word;
}
}
}

  这样,我们的HelloWorld之旅就结束了,非常简单。

多文件生成

  当前位置方案的方案只能生成如下所示的代码:

  生成的文件会与T4模板在同一目录中,这里就不详述了,可以参考 蒋金楠 一个简易版的T4代码生成"框架" 。

  本项目的多文件需要生成到指定文件夹中,但又想对T4模板进行统一的管理,T4文件夹里放置T4模板文件,但生成的映射文件EntityConfiguration将放置到文件夹Configurations中,仓储操作的文件IEntityRepository与EntityRepository将放置到Repositories文件夹中。且生成的代码文件应能自动的添加到解决方案中,而不是只是在文件夹中存在。

  要实现此需求,一个简单的办法就是通过 T4 Toolbox 来进行生成。想了解 T4 Toolbox 的细节,可以自己使用 ILSpy 来对 T4Toolbox.dll 文件进行反编译来查看源代码,如果是通过 T4 Toolbox 是通过VS的插件来安装的,将在“C:\Users\Administrator\AppData\Local\Microsoft\VisualStudio\11.0\Extensions\dca4f0lt.jdx”文件夹中(我的机器上)。下面,我们直接进行多文件生成的演示。

  首先,添加一个名为 HelloWorldTemplate.tt 的 T4 Toolbox 的代码模板。

  此模板将继承于 T4 Toolbox 的 CSharpTemplate 类:

 <#+
// <copyright file="HelloWorldTemplate.tt" company="郭明锋@中国">
// Copyright © 郭明锋@中国. All Rights Reserved.
// </copyright> public class HelloWorldTemplate : CSharpTemplate
{
private string _className; public HelloWorldTemplate(string className)
{
_className = className;
} public override string TransformText()
{
#>
using System; namespace GMF.Demo.Core.Data.T4
{
public class <#=_className #>
{
private string _word; public <#=_className #>(string word)
{
_word = word;
}
}
}
<#+
return this.GenerationEnvironment.ToString();
}
}
#>

  模板类中定义了一个 className 参数,用于接收一个表示要生成的类名的值。所有生成类的代码都以字符串的形式写在重写的 TransformText 方法中。

  再定义一个T4模板文件 HelloWorldMulti.tt,用于调用 上面定义的代码模板进行代码文件的生成。

 <#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ include file="T4Toolbox.tt" #>
<#@ include file="HelloWorldTemplate.tt" #>
<#
string curPath = Path.GetDirectoryName(Host.TemplateFile);
string destPath = Path.Combine(curPath, "outPath");
if(!Directory.Exists(destPath))
{
Directory.CreateDirectory(destPath);
}
string[] classNames = new[]{"HelloWorld1", "HelloWorld2", "HelloWorld3"};
foreach(string className in classNames)
{
HelloWorldTemplate template = new HelloWorldTemplate(className);
string fileName = string.Format(@"{0}\{1}.cs", destPath, className);
template.Output.Encoding = Encoding.UTF8;
template.RenderToFile(fileName);
}
#>

  以上是整个T4模板的执行方,在执行方中,要引用所有需要用到的类库文件,命名空间,包含的模板文件等。

  最后,文件的生成是调用 T4 Toolbox 的Template基类中定义的 RenderToFile(string filename)方法来生成各个文件的,输入的参数为生成文件的文件全名。在这里,生成将如下所示:

  outPPath文件夹中生成了 HelloWorld1.cs、HelloWorld2.cs、HelloWorld3.cs 文件,而 HelloWorldMulti.tt 所在文件夹中也会生成一个空的 HelloWorldMulti.cs 类文件。

生成数据层实体相关相似代码

生成准备

  我们的生成代码是完全依赖于业务实体的,所以,需要有一个类来对业务实体的信息进行提取封装。

 1 namespace GMF.Component.Tools.T4
2 {
3 /// <summary>
4 /// T4实体模型信息类
5 /// </summary>
6 public class T4ModelInfo
7 {
8 /// <summary>
9 /// 获取 模型所在模块名称
10 /// </summary>
11 public string ModuleName { get; private set; }
12
13 /// <summary>
14 /// 获取 模型名称
15 /// </summary>
16 public string Name { get; private set; }
17
18 /// <summary>
19 /// 获取 模型描述
20 /// </summary>
21 public string Description { get; private set; }
22
23 public IEnumerable<PropertyInfo> Properties { get; private set; }
24
25 public T4ModelInfo(Type modelType)
26 {
27 var @namespace = modelType.Namespace;
28 if (@namespace == null)
29 {
30 return;
31 }
32 var index = @namespace.LastIndexOf('.') + 1;
33 ModuleName = @namespace.Substring(index, @namespace.Length - index);
34 Name = modelType.Name;
35 var descAttributes = modelType.GetCustomAttributes(typeof(DescriptionAttribute), true);
36 Description = descAttributes.Length == 1 ? ((DescriptionAttribute)descAttributes[0]).Description : Name;
37 Properties = modelType.GetProperties();
38 }
39 }
40 }

  另外,通过模板生成的代码,与我们手写的代码有如下几个区别:

  1. 手写代码可以自由定义,生成代码是根据模板生成的,必然遵守模板定义的规范。
  2. 手写代码的修改可以永久保存,生成代码的修改将会在下次生成后丢失,即生成代码不应该进行修改。

  基于以上几个区别,我提出如下解决方案,来解决生成代码的修改问题

  1. 通过模板生成的类应尽量定义为分部类(partial class),在需要进行修改及扩展的时候可以定义另外的同名分部类来进行修改。
  2. 对于需要外部实现的功能,定义分部方法来对外提供扩展。
  3. 生成代码的类文件命名把扩展名由“.cs”更改为“generated.cs”,以区分生成代码与手写代码文件。

生成实体相关相似代码

生成实体映射配置类

  实体映射配置类模板 EntityConfigurationTemplate.tt 定义:

 <#+
// <copyright file="EntityConfigurationTemplate.tt" company="郭明锋@中国">
// Copyright © 郭明锋@中国. All Rights Reserved.
// </copyright> public class EntityConfigurationTemplate : CSharpTemplate
{
private T4ModelInfo _model; public EntityConfigurationTemplate(T4ModelInfo model)
{
_model = model;
} /// <summary>
/// 获取 生成的文件名,根据模型名定义
/// </summary>
public string FileName
{
get
{
return string.Format("{0}Configuration.generated.cs", _model.Name);
}
} public override string TransformText()
{
#>
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// 如存在本生成代码外的新需求,请在相同命名空间下创建同名分部类实现 <#= _model.Name #>ConfigurationAppend 分部方法。
// </auto-generated>
//
// <copyright file="<#= _model.Name #>Configuration.generated.cs">
// Copyright(c)2013 GMFCN.All rights reserved.
// CLR版本:4.0.30319.239
// 开发组织:郭明锋@中国
// 公司网站:http://www.gmfcn.net
// 所属工程:GMF.Demo.Core.Data
// 生成时间:<#= DateTime.Now.ToString("yyyy-MM-dd HH:mm") #>
// </copyright>
//------------------------------------------------------------------------------ using System;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity.ModelConfiguration.Configuration; using GMF.Component.Data;
using GMF.Demo.Core.Models; namespace GMF.Demo.Core.Data.Configurations
{
/// <summary>
/// 实体类-数据表映射——<#= _model.Description #>
/// </summary>
internal partial class <#= _model.Name #>Configuration : EntityTypeConfiguration<<#= _model.Name #>>, IEntityMapper
{
/// <summary>
/// 实体类-数据表映射构造函数——<#= _model.Description #>
/// </summary>
public <#= _model.Name #>Configuration()
{
<#= _model.Name #>ConfigurationAppend();
} /// <summary>
/// 额外的数据映射
/// </summary>
partial void <#= _model.Name #>ConfigurationAppend(); /// <summary>
/// 将当前实体映射对象注册到当前数据访问上下文实体映射配置注册器中
/// </summary>
/// <param name="configurations">实体映射配置注册器</param>
public void RegistTo(ConfigurationRegistrar configurations)
{
configurations.Add(this);
}
}
}
<#+
return this.GenerationEnvironment.ToString();
}
}
#>

  生成模板调用方 EntityCodeScript.tt 定义

 <#@ template language="C#" debug="True" #>
<#@ output extension="cs" #>
<#@ Assembly Name="System.Core" #>
<#@ Assembly Name="$(SolutionDir)\GMF.Component.Tools\bin\Debug\GMF.Component.Tools.dll" #>
<#@ import namespace="System.IO" #>
<#@ Import Namespace="System.Linq" #>
<#@ Import Namespace="System.Text" #>
<#@ import namespace="System.Reflection" #>
<#@ Import Namespace="System.Collections.Generic" #>
<#@ Import Namespace="GMF.Component.Tools" #>
<#@ Import Namespace="GMF.Component.Tools.T4" #>
<#@ include file="T4Toolbox.tt" #>
<#@ include file="Include\EntityConfigurationTemplate.tt" #>
<#
string currentPath = Path.GetDirectoryName(Host.TemplateFile);
string projectPath =currentPath.Substring(, currentPath.IndexOf(@"\T4"));
string solutionPath = currentPath.Substring(, currentPath.IndexOf(@"\GMF.Demo.Core.Data")); string modelFile= Path.Combine(solutionPath, @"GMF.Demo.Core.Models\bin\Debug\GMF.Demo.Core.Models.dll");
byte[] fileData= File.ReadAllBytes(modelFile);
Assembly assembly = Assembly.Load(fileData);
IEnumerable<Type> modelTypes = assembly.GetTypes().Where(m => typeof(Entity).IsAssignableFrom(m) && !m.IsAbstract);
foreach(Type modelType in modelTypes)
{
T4ModelInfo model = new T4ModelInfo(modelType);
//实体映射类
EntityConfigurationTemplate config = new EntityConfigurationTemplate(model);
string path = string.Format(@"{0}\Configurations", projectPath);
config.Output.Encoding = Encoding.UTF8;
config.RenderToFile(Path.Combine(path, config.FileName));
}
#>

  调用方通过反射从业务实体程序集 GMF.Demo.Core.Models.dll 中获取所有基类为 Entity 的并且不是抽象类的实体类型信息,再调用模板逐个生成实体配置类文件。

  例如,生成的登录记录信息(LoginLog)的映射文件 LoginLogConfiguration.generated.cs 如下:

 //------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// 如存在本生成代码外的新需求,请在相同命名空间下创建同名分部类实现 LoginLogConfigurationAppend 分部方法。
// </auto-generated>
//
// <copyright file="LoginLogConfiguration.generated.cs">
// Copyright(c)2013 GMFCN.All rights reserved.
// CLR版本:4.0.30319.239
// 开发组织:郭明锋@中国
// 公司网站:http://www.gmfcn.net
// 所属工程:GMF.Demo.Core.Data
// 生成时间:2013-06-16 17:45
// </copyright>
//------------------------------------------------------------------------------ using System;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity.ModelConfiguration.Configuration; using GMF.Component.Data;
using GMF.Demo.Core.Models; namespace GMF.Demo.Core.Data.Configurations
{
/// <summary>
/// 实体类-数据表映射——登录记录信息
/// </summary>
internal partial class LoginLogConfiguration : EntityTypeConfiguration<LoginLog>, IEntityMapper
{
/// <summary>
/// 实体类-数据表映射构造函数——登录记录信息
/// </summary>
public LoginLogConfiguration()
{
LoginLogConfigurationAppend();
} /// <summary>
/// 额外的数据映射
/// </summary>
partial void LoginLogConfigurationAppend(); /// <summary>
/// 将当前实体映射对象注册到当前数据访问上下文实体映射配置注册器中
/// </summary>
/// <param name="configurations">实体映射配置注册器</param>
public void RegistTo(ConfigurationRegistrar configurations)
{
configurations.Add(this);
}
}
}

  要配置登录信息与用户信息的 N:1 关系,只需要添加一个分部类 LoginLogConfiguration,并实现分类方法 LoginLogConfigurationAppend 即可。

 namespace GMF.Demo.Core.Data.Configurations
{
partial class LoginLogConfiguration
{
partial void LoginLogConfigurationAppend()
{
HasRequired(m => m.Member).WithMany(n => n.LoginLogs);
}
}
}

生成实体仓储接口

  实体映射配置类模板 EntityConfigurationTemplate.tt 定义:

 <#+
// <copyright file="IEntityRepositoryTemplate.tt" company="郭明锋@中国">
// Copyright © 郭明锋@中国. All Rights Reserved.
// </copyright> public class IEntityRepositoryTemplate : CSharpTemplate
{
private T4ModelInfo _model; public IEntityRepositoryTemplate(T4ModelInfo model)
{
_model = model;
} /// <summary>
/// 获取 生成的文件名,根据模型名定义
/// </summary>
public string FileName
{
get
{
return string.Format("I{0}Repository.generated.cs", _model.Name);
}
} public override string TransformText()
{
#>
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// 如存在本生成代码外的新需求,请在相同命名空间下创建同名分部类进行实现。
// </auto-generated>
//
// <copyright file="I<#= _model.Name #>Repository.generated.cs">
// Copyright(c)2013 GMFCN.All rights reserved.
// CLR版本:4.0.30319.239
// 开发组织:郭明锋@中国
// 公司网站:http://www.gmfcn.net
// 所属工程:GMF.Demo.Core.Data
// 生成时间:<#= DateTime.Now.ToString("yyyy-MM-dd HH:mm") #>
// </copyright>
//------------------------------------------------------------------------------ using System; using GMF.Component.Data;
using GMF.Demo.Core.Models; namespace GMF.Demo.Core.Data.Repositories
{
/// <summary>
/// 数据访问层接口——<#= _model.Description #>
/// </summary>
public partial interface I<#= _model.Name #>Repository : IRepository<<#= _model.Name #>>
{ }
} <#+
return this.GenerationEnvironment.ToString();
}
}
#>

  相应的,在调用方 EntityCodeScript.tt 中添加模板调用代码(如下 11-15 行所示):

     foreach(Type modelType in modelTypes)
{
T4ModelInfo model = new T4ModelInfo(modelType); //实体映射类
EntityConfigurationTemplate config = new EntityConfigurationTemplate(model);
string path = string.Format(@"{0}\Configurations", projectPath);
config.Output.Encoding = Encoding.UTF8;
config.RenderToFile(Path.Combine(path, config.FileName)); //实体仓储操作接口
IEntityRepositoryTemplate irep= new IEntityRepositoryTemplate(model);
path = string.Format(@"{0}\Repositories", projectPath);
irep.Output.Encoding = Encoding.UTF8;
irep.RenderToFile(Path.Combine(path, irep.FileName));
}

  生成的登录记录信息仓储操作接口 ILoginLogRepository.generated.cs:

 //------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// 如存在本生成代码外的新需求,请在相同命名空间下创建同名分部类进行实现。
// </auto-generated>
//
// <copyright file="ILoginLogRepository.generated.cs">
// Copyright(c)2013 GMFCN.All rights reserved.
// CLR版本:4.0.30319.239
// 开发组织:郭明锋@中国
// 公司网站:http://www.gmfcn.net
// 所属工程:GMF.Demo.Core.Data
// 生成时间:2013-06-16 17:56
// </copyright>
//------------------------------------------------------------------------------ using System; using GMF.Component.Data;
using GMF.Demo.Core.Models; namespace GMF.Demo.Core.Data.Repositories
{
/// <summary>
/// 数据访问层接口——登录记录信息
/// </summary>
public partial interface ILoginLogRepository : IRepository<LoginLog>
{ }
}

  如果 IRepository<T> 中定义的仓储操作不满足登录记录仓储操作的需求,只需要添加一个相应的分部接口,在其中进行需求的定义即可。这里当前没有额外的需求,就不需要额外定义了。

生成实体仓储实现

  实体仓储操作的实现与仓储操作接口类似,这里略过。

  完成生成后,代码结构如下所示:

  如果添加了或者删除了一个实体,只需要重新运行一下调用模板 EntityCodeScript.tt 即可重新生成新的代码。

源码获取

  为了让大家能第一时间获取到本架构的最新代码,也为了方便我对代码的管理,本系列的源码已加入微软的开源项目网站 http://www.codeplex.com,地址为:

  https://gmframework.codeplex.com/

  可以通过下列途径获取到最新代码:

  • 如果你是本项目的参与者,可以通过VS自带的团队TFS直接连接到 https://tfs.codeplex.com:443/tfs/TFS17 获取最新代码
  • 如果你安装有SVN客户端(亲测TortoiseSVN 1.6.7可用),可以连接到 https://gmframework.svn.codeplex.com/svn 获取最新代码
  • 如果以上条件都不满足,你可以进入页面 https://gmframework.codeplex.com/SourceControl/latest 查看最新代码,也可以点击页面上的 Download 链接进行压缩包的下载,你还可以点击页面上的 History 链接获取到历史版本的源代码
  • 如果你想和大家一起学习MVC,学习EF,欢迎加入Q群:5008599(群发言仅限技术讨论,拒绝闲聊,拒绝酱油,拒绝广告)
  • 如果你想与我共同来完成这个开源项目,可以随时联系我。

系列导航

  1. MVC实用架构设计(〇)——总体设计
  2. MVC实用架构设计(一)——项目结构搭建
  3. MVC实用架构设计(二)——使用MEF应用IOC
  4. MVC实用架构设计(三)——EF-Code First(1):Repository,UnitOfWork,DbContext
  5. MVC实用架构设计(三)——EF-Code First(2):实体映射、数据迁移,重构
  6. MVC实用架构设计(三)——EF-Code First(3):使用T4模板生成相似代码
  7. MVC实用架构设计(三)——EF-Code First(4):数据查询
  8. MVC实用架构设计(三)——EF-Code First(5):二级缓存
  9. MVC实体架构设计(三)——EF-Code First(6):数据更新
  10. 未完待续。。。

MVC实用架构设计(三)——EF-Code First(3):使用T4模板生成相似代码的更多相关文章

  1. EF Code First:使用T4模板生成相似代码

    http://developer.51cto.com/art/201309/409948.htm

  2. MVC实用架构设计(三)——EF-Code First(5):二级缓存

    前言 今天我们来谈谈EF的缓存问题. 缓存对于一个系统来说至关重要,但是是EF到版本6了仍然没有见到有支持查询结果缓存机制的迹象.EF4开始会把查询语句编译成存储过程缓存在Sql Server中,据说 ...

  3. MVC实用架构设计(三)——EF-Code First(1):Repository,UnitOfWork,DbContext

    前言 终于到EF了,实在不好意思,最近有点忙,本篇离上一篇发布已经一个多星期了,工作中的小迭代告一段落,终于有点时间来继续我们的架构设计了,在这里先对大家表示歉意. 其实这段时间我并不是把这个系列给忘 ...

  4. MVC实用架构设计(三)——EF-Code First(4):数据查询

    前言 首先对大家表示抱歉,这个系列已经将近一个月没有更新了,相信大家等本篇更新都等得快失望了.实在没办法,由于本人水平有限,写篇博客基本上要大半天的时间,最近实在是抽不出这么长段的空闲时间来写.另外也 ...

  5. [转]MVC实用架构设计(三)——EF-Code First(3):使用T4模板生成相似代码

    本文转自:http://www.cnblogs.com/guomingfeng/p/mvc-ef-t4.html 〇.目录 一.前言 二.工具准备 三.T4代码生成预热 (一) 单文件生成:Hello ...

  6. MVC 实用架构设计(三)——EF-Code First(5):二级缓存

    一.前言 今天我们来谈谈EF的缓存问题. 缓存对于一个系统来说至关重要,但是是EF到版本6了仍然没有见到有支持查询结果缓存机制的迹象.EF4开始会把查询语句编译成存储过程缓存在Sql Server中, ...

  7. MVC 实用架构设计(〇)——总体设计

    〇.目录 一.前言 二.结构图 三.结构说明 一.前言 一直以来都想写这个系列,但基于各种理由(主要是懒惰),迟迟没有动手.今天,趁着周末的空档,终于把系列的目录公布出来了,算是开个头,也给自己一个坚 ...

  8. MVC实用架构设计:总体设计

    http://developer.51cto.com/art/201309/410166.htm

  9. MVC实用构架设计(三)——EF-Code First(6):数据更新最佳实践

    前言 最近在整理EntityFramework数据更新的代码,颇有体会,觉得有分享的价值,于是记录下来,让需要的人少走些弯路也是好的. 为方便起见,先创建一个控制台工程,使用using(var db ...

随机推荐

  1. git flow工作流实际项目实践

    公司项目的开发流程主要是这样 代码分为 develop分支 master分支 平时我开发的时候,主要在develop分支上改动 一般来讲,有以下几种改动方式 1.直接在develop上修改代码 这种一 ...

  2. javascript基础知识

    1.javascript 表单验证,减轻服务器压力 制作网页特效 动态改变页面内容 基于对象和事件驱动的,具有安全性能的脚本语言 交互,脚本语言.解释性语言,边执行边解释 2.script标签 添加位 ...

  3. iOS中 将 颜色转化成图片

    定义一个类方法: 声明: + (UIImage *)imageFromColor:(UIColor *)color; 实现: + (UIImage *)imageFromColor:(UIColor ...

  4. 谈BFC和haslayout

    今天提到BFC和haslayout,就顺带在网上查查资料,总结一下它们. CSS2我们再熟悉不过,当然它里面我们需要掌握的,就是CSS2的选择器和布局,选择器总共31种.避开这个不说,我们说布局. 布 ...

  5. java并发编程(十七)Executor框架和线程池

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17465497   Executor框架简介 在Java 5之后,并发编程引入了一堆新的启动 ...

  6. python中实现定时器Timer

    实现定时器最简单的办法是就是循环中间嵌time.sleep(seconds), 这里我就不赘述了 # encoding: UTF-8 import threading #Timer(定时器)是Thre ...

  7. SDOI 2016 排列计数

    题目大意:一个数列A,n个元素,其中m个元素不动,其他元素均不在相应位置,问有多少种排列 保证m个元素不动,组合数学直接计算,剩余元素错位排列一下即可 #include<bits/stdc++. ...

  8. ajax优点与缺点

    ajax的优点 Ajax的给我们带来的好处大家基本上都深有体会,在这里我只简单的讲几点: 1.最大的一点是页面无刷新,在页面内与服务器通信,给用户的体验非常好. 2.使用异步方式与服务器通信,不需要打 ...

  9. Delphi自己隐藏自定义弹出列表

    先上代码 procedure TForm3.Timer1Timer(Sender: TObject); var Point: TPoint; Name: array[0..255] of Char; ...

  10. 皮裤原理和运营微信公众号dotNET跨平台

    经常碰到有同学对.NET跨平台存在各种疑惑和误解,原因是什么呢?当然我是知道.NET的跨平台不是问题,而且微软2014年的努力可圈可点,而且还有很多人对.NET的前景感到困惑.春节期间突然明白了,这就 ...