- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
据我了解,LLBLGen Pro 无法在其自己的实体上生成 POCO(请参见此处:http://www.llblgen.com/Pages/featuresLLBLGenPro.aspx)。
有没有人编写过一个 T4 来生成与 LLBLGen Pro 实体相对应的 POCO 类,并生成适当的转换逻辑来往返于实体和 POCO?有没有其他人想出一个不涉及手动编写大量转换代码的解决方案?
最佳答案
我们从 LLBLGen 生成 DTO,而不是使用 T4。
我们需要从实体创建 DTO。这些在技术上不是 POCO,因为它们有 ToEntity()
和 FromEntity()
方法,但也许这对你有用。
我们创建了 IDTO<T>
然后由 DTO 类实现的接口(interface)(每个实体一个)。我们没有修改实体模板,而是添加了 DTOExtension
将实体转换为 DTO 的方法(以及许多其他辅助转换)。
以下是您可以在 LLBLGen v2.6 中使用的模板文件。版本 3 中的模板设计器更易于使用,如果需要,您可以将大部分代码转换为版本 3。
文件:entityDTOInterface.template
using System;
using System.ComponentModel;
using System.Collections;
using System.Runtime.Serialization;
using <[RootNamespace]>.HelperClasses;
using <[RootNamespace]>.EntityClasses;
using SD.LLBLGen.Pro.ORMSupportClasses;
namespace <[RootNamespace]>.DTOClasses
{
/// <summary>
/// DTO interface.
/// </summary>
public interface IDTO<T>
{
T ToEntity(T toFill);
IDTO<T> FromEntity(T entityInstance, Hashtable seenObjects, Hashtable parents);
}
}
using System;
using System.ComponentModel;
using System.Collections;
using System.Runtime.Serialization;
using <[RootNamespace]>.HelperClasses;
using <[RootNamespace]>.EntityClasses;
using SD.LLBLGen.Pro.ORMSupportClasses;
namespace <[RootNamespace]>.DTOClasses
{
<[ UserCodeRegion "AdditionalNamespaces" ]>
// __LLBLGENPRO_USER_CODE_REGION_START AdditionalNamespaces
// __LLBLGENPRO_USER_CODE_REGION_END
<[ EndUserCodeRegion ]>
/// <summary>
/// DTO class for the entity '<[CurrentEntityName]>'.
/// </summary>
[Serializable]
public <[If UsePartialClasses]>partial <[EndIf]>class <[CurrentEntityName]>DTO : <[ If IsSubType ]><[ SuperTypeName ]>DTO, <[ EndIf]>IDTO<<[CurrentEntityName]>Entity><[ UserCodeRegion "AdditionalInterfaces" ]>
// __LLBLGENPRO_USER_CODE_REGION_START AdditionalInterfaces
// __LLBLGENPRO_USER_CODE_REGION_END
<[ EndUserCodeRegion ]>
{
#region Entity Field Public Properties
<[Foreach EntityField CrLf]> /// <summary>Get or set the <[EntityFieldName]> property that maps to the Entity <[CurrentEntityName]></summary>
public virtual <[If GenerateAsNullableType]><[TypeOfField]>?<[Else]><[TypeOfField]><[EndIf]> <[EntityFieldName]> { get; set; }
<[NextForeach]>
#endregion
#region Related Field Public Properties
<[ Foreach RelatedEntityField CrLf]> /// <summary>Get or set the <[MappedFieldNameRelatedField]> property that maps to the Entity <[CurrentEntityName]>'s <[ MappedFieldNameRelation ]>.<[ RelatedEntityFieldName ]></summary>
public virtual <[If GenerateAsNullableType]><[TypeOfField]>?<[Else]><[TypeOfField]><[EndIf]> <[ MappedFieldNameRelatedField ]> { get; private set; }<[NextForeach]>
#endregion
#region Custom Fields
<[ UserCodeRegion "CustomFieldCode" ]>
// __LLBLGENPRO_USER_CODE_REGION_START CustomFieldCode
// __LLBLGENPRO_USER_CODE_REGION_END
<[ EndUserCodeRegion ]>
#endregion
#region Ctors
/// <summary>
/// CTor
/// </summary>
public <[CurrentEntityName]>DTO()
{
}
/// <summary>
/// CTor which initializes the DTO with values from its corresponding entity
/// </summary>
/// <param name="entityInstance">The entity instance which holds the values for this DTO</param>
public <[CurrentEntityName]>DTO(<[CurrentEntityName]>Entity entityInstance) : this(entityInstance, new Hashtable(), new Hashtable()) { }
internal <[CurrentEntityName]>DTO(<[CurrentEntityName]>Entity entityInstance, Hashtable seenObjects, Hashtable parents)<[ If IsSubType ]> : base(entityInstance, seenObjects, parents)<[ EndIf]>
{
FromEntity(entityInstance, seenObjects, parents);
}
#endregion
/// <summary>
/// Creates a <[CurrentEntityName]>DTO object from the given entity.
/// </summary>
public virtual IDTO<<[CurrentEntityName]>Entity> FromEntity(<[CurrentEntityName]>Entity entityInstance, Hashtable seenObjects, Hashtable parents)
{
<[ If IsSubType ]>base.FromEntity(entityInstance, seenObjects, parents);
<[ EndIf]>seenObjects[entityInstance] = this;
parents = new Hashtable(parents);
parents.Add(entityInstance, null);
<[Foreach EntityField CrLf]> this.<[EntityFieldName]> = entityInstance.<[EntityFieldName]>;<[NextForeach]>
<[Foreach RelatedEntityField CrLf]> if (entityInstance.AlreadyFetched<[MappedFieldNameRelation]>)
this.<[MappedFieldNameRelatedField]> = entityInstance.<[MappedFieldNameRelatedField]>;<[NextForeach]>
<[Foreach RelatedEntity OneToMany CrLf]><[If Not MappedFieldRelationIsHidden]> if (entityInstance.AlreadyFetched<[MappedFieldNameRelation]>)
<[MappedFieldNameRelation]> = RelatedArray<<[RelatedEntityName]>DTO, <[RelatedEntityName]>Entity>(entityInstance.<[MappedFieldNameRelation]>, seenObjects, parents);<[EndIf]><[NextForeach]>
<[Foreach RelatedEntity ManyToMany CrLf]><[If Not MappedFieldRelationIsHidden]> if (entityInstance.AlreadyFetched<[MappedFieldNameRelation]>)
<[MappedFieldNameRelation]> = RelatedArray<<[RelatedEntityName]>DTO, <[RelatedEntityName]>Entity>(entityInstance.<[MappedFieldNameRelation]>, seenObjects, parents);<[EndIf]><[NextForeach]>
<[Foreach RelatedEntity ManyToOne CrLf]><[If Not MappedFieldRelationIsHidden]> if (entityInstance.AlreadyFetched<[MappedFieldNameRelation]>)
<[MappedFieldNameRelation]> = RelatedObject<<[RelatedEntityName]>DTO, <[RelatedEntityName]>Entity>(entityInstance.<[MappedFieldNameRelation]>, seenObjects, parents);//(new <[RelatedEntityName]>DTO(entityInstance.<[MappedFieldNameRelation]>, seenObjects);<[EndIf]><[NextForeach]>
<[Foreach RelatedEntity OneToOne CrLf]><[If Not MappedFieldRelationIsHidden]> if (entityInstance.AlreadyFetched<[MappedFieldNameRelation]>)
<[MappedFieldNameRelation]> = RelatedObject<<[RelatedEntityName]>DTO, <[RelatedEntityName]>Entity>(entityInstance.<[MappedFieldNameRelation]>, seenObjects, parents);<[EndIf]><[NextForeach]>
return this;
}
<[ If Not IsSubType ]>
/// <summary>
/// Get a collection of DTO objects created from entities related to <[CurrentEntityName]>Entity.
/// It keeps track of entities previously seen to prevent an infinite loop.
/// <summary>
protected virtual T[] RelatedArray<T,U>(EntityCollectionBase<U> entities, Hashtable seenObjects, Hashtable parents) where T : class, IDTO<U>, new() where U : EntityBase
{
if (null == entities)
{
return null;
}
T[] arr = new T[entities.Count];
int i = 0;
foreach (U entity in entities)
{
if (parents.Contains(entity))
{
return null;
}
}
foreach (U entity in entities)
{
if (seenObjects.Contains(entity))
{
arr[i++] = seenObjects[entity] as T;
}
else
{
arr[i++] = new T().FromEntity(entity, seenObjects, parents) as T;
}
}
return arr;
}
/// <summary>
/// Creates a DTO object from the given related <[CurrentEntityName]>Entity entity.
/// This is used to populate a single DTO from a single relation of the <[CurrentEntityName]>Entity.
/// <summary>
protected virtual T RelatedObject<T,U>(U entityInstance, Hashtable seenObjects, Hashtable parents) where T : class, IDTO<U>, new() where U : EntityBase
{
if (null == entityInstance)
{
return null;
}
if (seenObjects.Contains(entityInstance))
{
if (parents.Contains(entityInstance))
{
return null;
}
else
{
return seenObjects[entityInstance] as T;
}
}
return new T().FromEntity(entityInstance, seenObjects, parents) as T;
}
<[ EndIf]>
/// <summary>
/// Get a collection of individual DTO objects from the EntityCollectionBase<<[CurrentEntityName]>Entity> collection.
/// <summary>
public static <[CurrentEntityName]>DTO[] ToDTOArray(EntityCollectionBase<<[CurrentEntityName]>Entity> entities)
{
Hashtable seenObjects = new Hashtable();
<[CurrentEntityName]>DTO[] arr = new <[CurrentEntityName]>DTO[entities.Count];
for (int i = 0; i < entities.Count; i++)
{
arr[i] = new <[CurrentEntityName]>DTO().FromEntity(entities[i], seenObjects, new Hashtable()) as <[CurrentEntityName]>DTO;
}
return arr;
}
/// <summary>
/// Creates a new entity instance and copies over the values of this DTO
/// </summary>
public <[ If IsSubType ]>override <[SuperTypeName]><[ Else]>virtual <[CurrentEntityName]><[ EndIf]>Entity ToEntity()
{
return ToEntity(new <[CurrentEntityName]>Entity());
}
/// <summary>
/// Copies over the values of this DTO into the entity passed in.
/// Readonly fields on the entity are NOT copied to the entity from this DTO.
/// </summary>
public virtual <[CurrentEntityName]>Entity ToEntity(<[CurrentEntityName]>Entity toFill)
{
<[Foreach EntityField CrLf]><[If IsReadOnly ]><[ Else ]> toFill.<[EntityFieldName]> = this.<[EntityFieldName]>;<[ EndIf ]><[NextForeach]>
<[ If IsSubType ]> base.ToEntity(toFill);<[ EndIf]>
return toFill;
}
#region Relation Fields
<[Foreach RelatedEntity OneToMany CrLf]><[If Not MappedFieldRelationIsHidden]>
/// <summary> Gets the EntityCollectionBase with the related entities of type '<[RelatedEntityName]>Entity' which are related to this entity via a relation of type '1:n'.
/// If the EntityCollectionBase hasn't been fetched yet, the collection returned will be empty.</summary>
public virtual <[RelatedEntityName]>DTO[] <[MappedFieldNameRelation]> { get; set; }<[EndIf]><[NextForeach]>
<[Foreach RelatedEntity ManyToMany CrLf]><[If Not MappedFieldRelationIsHidden]>
/// <summary> Gets the EntityCollectionBase with the related entities of type '<[RelatedEntityName]>Entity' which are related to this entity via a relation of type 'm:n'.
/// If the EntityCollectionBase hasn't been fetched yet, the collection returned will be empty.</summary>
public virtual <[RelatedEntityName]>DTO[] <[MappedFieldNameRelation]> { get; set; }<[EndIf]><[NextForeach]>
<[Foreach RelatedEntity ManyToOne CrLf]><[If Not MappedFieldRelationIsHidden]>
/// <summary> Gets / sets related entity of type '<[RelatedEntityName]>Entity' which has to be set using a fetch action earlier. If no related entity
/// is set for this property, null is returned. This property is not visible in databound grids.</summary>
[Browsable(false)]
public virtual <[RelatedEntityName]>DTO <[MappedFieldNameRelation]> { get; set; }<[EndIf]><[NextForeach]>
<[Foreach RelatedEntity OneToOne CrLf]><[If Not MappedFieldRelationIsHidden]>
/// <summary> Gets / sets related entity of type '<[RelatedEntityName]>Entity' which has to be set using a fetch action earlier. If no related entity
/// is set for this property, null is returned. This property is not visible in databound grids.</summary>
[Browsable(false)]
public virtual <[RelatedEntityName]>DTO <[MappedFieldNameRelation]> { get; set; }<[EndIf]><[NextForeach]>
#endregion
#region Custom DTO code
<[ UserCodeRegion "CustomDTOCode" ]>
// __LLBLGENPRO_USER_CODE_REGION_START CustomDTOCode
// __LLBLGENPRO_USER_CODE_REGION_END
<[ EndUserCodeRegion ]>
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using <[RootNamespace]>;
using <[RootNamespace]>.EntityClasses;
using <[RootNamespace]>.CollectionClasses;
using <[RootNamespace]>.DTOClasses;
using <[RootNamespace]>.HelperClasses;
namespace <[RootNamespace]>.DTOClasses
{
/// <summary>
/// Generates extension methods for converting Entities to DTOs
/// This class is generated. Do not modify.
/// </summary>
public static <[If UsePartialClasses]>partial <[EndIf]>class Extensions
{
<[If HasEntity]><[Foreach Entity]>
/// <summary>Create a <[CurrentEntityName]> DTO from a <[CurrentEntityName]> entity</summary>
/// <returns>The DTO created</returns>
public static <[CurrentEntityName]>DTO ToDTO(this <[CurrentEntityName]>Entity entity)
{
<[CurrentEntityName]>DTO dto = null;
if (entity != null)
dto = new <[CurrentEntityName]>DTO(entity);
return dto;
}
/// <summary>Create a list of <[CurrentEntityName]>DTO from a <[CurrentEntityName]>Collection</summary>
/// <returns>The DTO list created</returns>
public static List<<[CurrentEntityName]>DTO> ToDTOs(this <[CurrentEntityName]>Collection collection)
{
List<<[CurrentEntityName]>DTO> dtoList = new List<<[CurrentEntityName]>DTO>(collection.Count);
foreach(<[CurrentEntityName]>Entity entity in collection)
dtoList.Add(new <[CurrentEntityName]>DTO(entity));
return dtoList;
}
/// <summary>Create a list of <[CurrentEntityName]>DTO from a List of <[CurrentEntityName]> entities</summary>
/// <returns>The DTO list created</returns>
public static List<<[CurrentEntityName]>DTO> ToDTOs(this List<<[CurrentEntityName]>Entity> entities)
{
return entities.ConvertAll<<[CurrentEntityName]>DTO>(e => new <[CurrentEntityName]>DTO(e));
}
/// <summary>From the queryable object, get a list of <[CurrentEntityName]>DTO</summary>
/// <returns>The DTO list created</returns>
public static List<<[CurrentEntityName]>DTO> ToDTOs(this IQueryable<<[CurrentEntityName]>Entity> queryableEntities)
{
return queryableEntities.ToList().ConvertAll<<[CurrentEntityName]>DTO>(e => new <[CurrentEntityName]>DTO(e));
}
/// <summary>From the queryable object, get a list of <[CurrentEntityName]>DTO</summary>
/// <returns>The DTO list created</returns>
public static <[CurrentEntityName]>DTO FirstDTO(this IQueryable<<[CurrentEntityName]>Entity> queryableEntities)
{
<[CurrentEntityName]>DTO dto = null;
<[CurrentEntityName]>Entity firstEntity = queryableEntities.First();
if (firstEntity != null)
dto = new <[CurrentEntityName]>DTO(firstEntity);
return dto;
}
<[NextForeach]><[EndIf]>
#region Custom code
<[ UserCodeRegion "CustomDTOCode" ]>
// __LLBLGENPRO_USER_CODE_REGION_START CustomDTOCode
// __LLBLGENPRO_USER_CODE_REGION_END
<[ EndUserCodeRegion ]>
#endregion
}
}
<?xml version="1.0" ?>
<taskGroup xmlns="http://sd/llblgen/pro/taskGroupElementDefinitions.xsd" name="DTO Templates" isOptional="false" description="General group of tasks which are used DTO templates.">
<supportedPlatforms>
<platform name=".NET 3.5" />
</supportedPlatforms>
<supportedTemplateGroups>
<templateGroup name="SelfServicing" />
</supportedTemplateGroups>
<taskGroup name="Create Directories">
<task name="DTODirectoryCreator" assemblyFilename="SD.LLBLGen.Pro.TaskPerformers.dll" taskPerformerClass="SD.LLBLGen.Pro.TaskPerformers.DirectoryCreator">
<parameters>
<parameter name="folderToCreate" defaultValue="DTOClasses" isOptional="false" description="The folder to create" />
<parameter name="failWhenExistent" defaultValue="false" isOptional="true" description="Flag to signal what to do when the folder already exists. Overrules clearWhenExistent" valueType="boolean" />
<parameter name="clearWhenExistent" defaultValue="false" isOptional="true" description="Flag to signal if an existing folder has to be cleared first. Overruled by failWhenExistent" valueType="boolean" />
</parameters>
</task>
</taskGroup>
<taskGroup name="Create DTO Classes" description="Create DTO Classes">
<task name="DTOInterfaceCreator" assemblyFilename="SD.LLBLGen.Pro.TaskPerformers.dll" taskPerformerClass="SD.LLBLGen.Pro.TaskPerformers.CodeEmitter">
<parameters>
<parameter isOptional="false" name="destinationFolder" defaultValue="DTOClasses" />
<parameter isOptional="false" name="failWhenExistent" defaultValue="false" />
<parameter isOptional="false" name="filenameFormat" defaultValue="IDTO.cs" />
<parameter isOptional="false" name="templateID" defaultValue="SD_DTOInterfaceTemplate" />
<parameter isOptional="false" name="emitType" defaultValue="generic" />
</parameters>
</task>
<task name="DTOClassCreator" assemblyFilename="SD.LLBLGen.Pro.TaskPerformers.dll" taskPerformerClass="SD.LLBLGen.Pro.TaskPerformers.CodeEmitter">
<parameters>
<parameter isOptional="false" name="destinationFolder" defaultValue="DTOClasses" />
<parameter isOptional="false" name="failWhenExistent" defaultValue="false" />
<parameter isOptional="false" name="filenameFormat" defaultValue="[elementName]DTO.[extension]" />
<parameter isOptional="false" name="templateID" defaultValue="SD_DTOTemplate" />
<parameter isOptional="false" name="emitType" defaultValue="allEntities" />
</parameters>
</task>
<task name="DTOExtentionsCreator" assemblyFilename="SD.LLBLGen.Pro.TaskPerformers.dll" taskPerformerClass="SD.LLBLGen.Pro.TaskPerformers.CodeEmitter">
<parameters>
<parameter isOptional="false" name="destinationFolder" defaultValue="DTOClasses" />
<parameter isOptional="false" name="failWhenExistent" defaultValue="false" />
<parameter isOptional="false" name="filenameFormat" defaultValue="DTOExtensions.cs" />
<parameter isOptional="false" name="templateID" defaultValue="SD_DTOExtentionsTemplate" />
<parameter isOptional="false" name="emitType" defaultValue="generic" />
</parameters>
</task>
</taskGroup>
</taskGroup>
<taskGroupPreset name="DTO Templates">
<taskGroupPreset name="Create Directories">
<taskPreset name="DTODirectoryCreator" />
</taskGroupPreset>
<taskGroupPreset name="Create DTO Classes">
<taskPreset name="DTOInterfaceCreator" />
<taskPreset name="DTOEntityInterfaceCreator" />
<taskPreset name="DTOBaseClassCreator" />
<taskPreset name="DTOClassCreator" />
<taskPreset name="DTOExtentionsCreator" />
</taskGroupPreset>
</taskGroupPreset>
关于.net - T4 用于在 LLBLGen Pro 实体上生成 POCO?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7913266/
你好, 我有一个 .pro 文件,看起来像: TEMPLATE = subdirs SUBDIRS = foo bar 我想在我的 subdirs .pro 文件中设置一个变量,或定义,或一些可以在
我正在尝试使用 QtCreator 构建一些项目。 这两个 .pro 文件之间有很多共同的代码。 有没有办法让这两个 .pro 文件调用另一个包含共同信息的唯一 .pro 文件? 最佳答案 是的,参见
好的,我正在为我构建的网络购物车使用 PHP/SQL。我也有所需的 Paypal 帐户(企业),所以我有一个 API 身份验证。我还创建了一个沙箱帐户并在那里也获得了 API 身份验证。 我已经通读了
我已经使用 Unity 4 Pro 试用版几个星期了。我决定升级到实际的专业版,这样我就可以发布游戏了。 我遇到的问题是,即使我已经升级了试用版本,(是的,我已经把新的序列号放在了 Unity 给我的
我目前正在开发一款完全用 Java 编写的游戏。截至目前,我的主循环正在 Action Performed 方法中运行,该方法由计时器每 100 秒更新一次。在这个循环中,代码会重新绘制,并对玩家位置
我想与组织内的用户共享 Power BI 报告。我有一个 Power BI Pro 帐户。其他用户没有。这可能吗? 我与之共享的用户无法打开报告。它说他们需要在 Pro 帐户中。 最佳答案 Shari
有没有什么办法可以将Ant Design Pro表格的默认操作文本翻译成英文。我正在使用 Umi 默认组件 最佳答案 Ant Design Pro Table 的操作有默认的翻译。您将需要导入它们并将
我有一个来 self 们供应商的 Java 应用程序。以管理员身份安装应用程序后,我尝试以管理员身份打开应用程序,但没有任何反应。我已经转到程序图标指向的位置,它转到 Javaw.exe 文件: "C
我无法使用 PayPal Payments Pro 选项创建 Paypal 沙盒测试帐户(使用 Pro 表示您自己是商家)。我在创建测试帐户时选择了该选项,但当我查看该帐户的详细信息时,它显示了 Ac
这是代码,请帮助我在第19行中使元组索引超出范围错误 import math n = 7 x = 0.5,1.2,2.1,2.9,3.6,4.5,5.7 y = 3.2,5.2,9.3,14.6,20
有人知道怎么做吗? Using File>加载C头文件失败错误代码太多。 最佳答案 C++ 标准库中有太多 IDA 无法理解的编译器相关宏。由于许多原始数据类型,例如 uint32_t 等,已经被 I
我有一个基于 Java SWING 的桌面应用程序,有人问我它是否可以在 Microsoft Surface Pro 或 Pro 2 上运行。据我所知,这些应用程序使用的是 Windows 8/8.1
我已经在 Android 中创建了一个模块以在我的主应用程序中使用,并且似乎有两个文件Consumer-rules.pro 和 proguard-rules.pro。 我想了解以下内容 所有模块代码都
好的,事情是这样的。我的一个客户声称他在使用 Safari 浏览器的 Macbook Pro 上查看网站时看不到社交图标(在屏幕的左侧)。这些图标在我测试过的所有设备上都清晰可见。是否有任何 Macb
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的
将应用程序提交到应用程序商店进行审核后,我在 iTunes Connect 中收到以下警告。我在 iTunes Connect 中没有看到上传 iPad Pro 屏幕截图的选项。 请告诉我这里可以做什
我在 Visual Studio 2010 中创建了一个愚蠢的小“游戏”来尝试使用 XNA 并自学游戏开发。它所做的只是循环颜色并在一些小球周围弹跳。我有为 .NET 4.0 客户端配置文件构建的项目
使用最新的TensorFlow(2.13)时遇到与GPU相关的错误。请注意TensorFlow-Metals页面上提供的测试模型培训,以验证我的设置工作正常。。请指点一下。。以下是我使用的命令-该脚本
我正在学习Pro*c语言。我正在尝试创建一个序列。但是当我们创建游标时我没有得到要使用哪个关键字,然后我们将其声明为 EXEC SQL DECLARE CUR_NAME CURSOR FOR
为了响应nodejs的大红大紫,最近应用其实现了一些server端的功能。数据库方面选择了老少咸宜的MySQL。今后肯定还会有相关应用需求。特此记录Mac系统下如何安装、配置MySQL及其管理工具S
我是一名优秀的程序员,十分优秀!