AutoConstructor
Automatically generate constructor for class
快速入门
常见的N层架构中,层与层之间往往有强烈的依赖关系,我们通常使用依赖注入,通过构造函数,将下一层的依赖对象注入到当前层的实现中。但是大部分情况下,这些构造函数都长的很相似,因为它们都做了很类似的事情:赋值和为空检查,例如下面的代码片段。
[ApiController]
[Route("[controller]")]
public class MyController : ControllerBase
{
public MyController(IMyService myservice, ILogger<MyController> logger)
{
_myservice = myservice ?? throw new ArgumentNullException(nameof(myservice));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
private readonly IMyService _myservice;
private readonly ILogger<MyController> _logger;
[HttpPost]
public async Task Run(string arg)
{
_logger.LogInformation("run invoked");
await _myservice.Run(arg);
}
}
大多数情况下,书写这些构造函数都是重复性的工作。比如我们如果想在当前的MyController
中再增加一个依赖的服务IMyService2
,就必须额外定义一个字段,然后在构造函数中也定义一个相同类型的局部参数,并且需要在构造函数体中将此字段赋值并做为空检查。
使用了FlyTiger
后,上面的示例代码就可以被简化。
[AutoConstructor(NullCheck = true)]
[ApiController]
[Route("[controller]")]
public partial class MyController : ControllerBase
{
private readonly IMyService myService;
private readonly ILogger<MyController> logger;
[HttpPost]
public async Task Run(string arg)
{
logger.LogInformation("run invoked");
await myservice.Run(arg);
}
}
简化后的代码片段和之前的代码片段功能上是一模一样的,仅仅在类的定义上加了[AutoConstructor(NullCheck = true)]
特性, 这样FlyTiger
会在编译期间自动生成以下代码
partial class MyController
{
public MyController(IMyService myService, ILogger<MyController> logger)
{
this.myService = myService ?? throw new System.ArgumentNullException(nameof(myService));
this.logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
}
}
因为用了partial关键字,所以dotnet在编译期间会将生成的代码和原有的代码片段合并到一起编译,这样就和手写的构造函数起到了相同的作用。如果在此情况下我们想给MyController
中再增加一个依赖的服务IMyService2
,那么只需要在MyController
类中额外再定义一个额外的字段即可。
[AutoConstructor(NullCheck = true)]
[ApiController]
[Route("[controller]")]
public partial class MyController : ControllerBase
{
private readonly IMyService myService;
private readonly IMyService2 myService2;
private readonly ILogger<MyController> logger;
[HttpPost]
public async Task Run(string arg)
{
logger.LogInformation("run invoked");
await myservice.Run(arg);
}
}
注意事项
- 确保加了
AutoConstructor
特性的类加了partial关键字的修饰。 - 确保没有再定义其它的构造函数,如果你也手动定义了相同参数的构造函数,这个构造函数将会和生成的构造函数冲突导致编译失败。
控制为空检查的行为
AutoConstructor
提供了一个属性NullCheck
,用于控制对于引用类型的对象是否要做为空检查,默认为false
表示不做为空检查。上面示例的代码,如果不加NullCheck
,则会生成以下代码片段。
partial class MyController
{
public MyController(IMyService myService, ILogger<MyController> logger)
{
this.myService = myService;
this.logger = logger;
}
}
忽略的字段或属性
默认情况下 AutoConstructor
会遍历当前类中所有的字段和可写的属性,生成构造函数。在某些情况下,你可能不需要将某些字段或属性通过构造函数赋值。那么你只需要给忽略的字段或属性加上AutoConstructorIgnore
特性即可。
[AutoConstructor(NullCheck = true)]
[ApiController]
[Route("[controller]")]
public partial class MyController : ControllerBase
{
[AutoConstructorIgnore]
private readonly int myValue = 10;
private readonly IMyService myService;
private readonly ILogger<MyController> logger;
[HttpPost]
public async Task Run(string arg)
{
logger.LogInformation("run invoked");
await myservice.Run(arg);
}
}
上面的代码片段仍旧只会生成两个参数的构造函数,因为myValue
的字段上面加了AutoConstructorIgnore
特性。如果去掉AutoConstructorIgnore
,则会生成三个参数的构造函数。
内部嵌套类
如果你需要在内部嵌套类上面使用AutoConstructor
,那么也需要确保外层的类上面加了partial关键字,例如下面的代码段。
using FlyTiger;
namespace ClassLibrary1
{
public partial class Class1
{
[AutoConstructor]
public partial class User
{
private readonly string name;
private readonly int age;
}
}
}
对应生成的构造函数代码片段如下
namespace ClassLibrary1
{
partial class Class1
{
partial class User
{
public User(string name, int age)
{
this.name = name;
this.age = age;
}
}
}
有继承关系的构造函数
如果定义了AutoConstructor
特性的类有继承的父类,默认会继承父类中最少参数的构造函数,例如下面的代码片段。
using FlyTiger;
namespace ClassLibrary1
{
public partial class Person
{
public Person(Guid id)
{
this.Id = id;
}
public Guid Id { get; set; }
}
[AutoConstructor]
public partial class User : Person
{
private readonly string name;
private readonly int age;
}
}
对应生成的构造函数代码片段如下
namespace ClassLibrary1
{
partial class User
{
public User(global::System.Guid id, string name, int age)
: base(id: id)
{
this.name = name;
this.age = age;
}
}
}