C#的Attribute和Typescript的装饰器之比较
概要
C#的Attribute和TypeScript的装饰器都可以实现AOP,以达到优化代码结构的作用。两者都可以在不修改原有代码的基础让,添加新的功能到已有类中,但是却采用了不同的实现方式。本文以一个字符串最大长度的限定的例子,来比较二者的实现方式。
概念比较
在C#中,通过定义一个类,该类继承自Attribute类。通过Attribute的子类实现对类,类中的属性,方法,方法参数等的装饰。C#的编译器会先实例化该子类,再实例化该类装饰的内容。这就决定了在Attribute子类内部,是无法访问到被装饰的内容,所以Attribute子类往往是和反射结合在一起使用,作为反射的路标。通过反射,定位被装饰的内容,以实现Attribute子类的功能。
在Typescript中,装饰器是一个函数,编译器将被装饰的内容作为参数,注入到该函数的参数中,这就决定了在TypeScript的装饰器中,可以访问到被装饰的内容。也就是说可以通过装饰器,扩展原有类的内容,以实现装饰器定义的功能。
实例比较
通过在C#和Typescript中定义一个限定类中属性长度的修饰器,来比较两者的实现方式。
C#Attribute子类的实例
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class MaxLengthAttribute: Attribute
{
private int MaxLength {get;set;} = 10;
public MaxLengthAttribute(int maxLength = 10){
Console.WriteLine("MaxLengthAttribute");
this.MaxLength = maxLength;
}
public bool checkMaxLength(string prop, string str){
if (str == null){
Console.WriteLine("Property {prop} is null");
return false;
}
if (str.Length > MaxLength){
Console.WriteLine($"Failed to update property {prop}. Its maximum length cannot exceed {this.MaxLength}.");
return false;
}
return true;
}
}
- 限定该类只能修饰类中属性
- 类中属性默认最大长度为10个字符,可以通过参数默认值实现定制。
- checkMaxLength方法定义了检查方式,如果类中的属性长度大于指定值,报错。
Attribute子类的应用
using System;
using System.Reflection;
using System.Linq;
public class Program
{
public static void Main()
{
Engine engine = new Engine();
engine.Vendor = "AVAVAAAA";
engine.Type = "AVAVAAAAAVAVAAAAAVAVAAAAAVAVAAAAAVAVAAAA";
var engineType = typeof(Engine);
var properties = engineType.GetProperties();
foreach (var property in properties){
if (property.IsDefined(typeof(MaxLengthAttribute))){
var maxlength = property.GetCustomAttributes<MaxLengthAttribute>().First();
maxlength.checkMaxLength(property.Name, (string)property.GetValue(engine));
}
}
}
}
public class Engine {
public Engine(){
Console.WriteLine("Engine");
}
[MaxLength(5)]
public string Vendor {get;set;}
[MaxLength]
public string Type {get;set;}
}
- 自定义发动机类,该类有两个属性,供应商和型号。其*应商不能超过5个字符,型号不能超过10个。
- 通过反射找到被MaxLength标识的Engine对象的供应商和型号属性,MaxLength起到了路标的作用。
- 通过反射调用Attribute子类对象的checkMaxLength,检查属性字符串的长度。
- 运行结果如下:
从结果上可以看出如下:
- MaxLengthAttribute子类的实例化先于被装饰的内容Engine类的实例化。
- MaxLengthAttribute子类每次使用,编译器会实例化一个新的对象。
Typescript装饰器的实例
const maxLength = (max: number = 10) => (target: any, name: any) => {
const key = "_temp" + new Date().getTime();
Object.defineProperty(target, name, {
get: function () {
return this[key];
},
set: function (val) {
if (val == null) {
console.log(`Property ${name} is null.`);
return;
}
if (val.length > max) {
console.log(
`Failed to update property ${name}. Its maximum length cannot exceed ${max}.`
);
return;
}
this[key] = val;
},
});
};
- 因为要传递参数来限制最大允许的字符数,所以采用工厂方式定义该装饰器函数maxLength。
- 采用defineProperty实现数据劫持,一旦赋值超过规定长度,则赋值失败。
- 因为要支持多个属性的长度检查,所以通过new Date().getTime()来区分每个属性,以配合defineProperty的使用。
Typescript装饰器的使用
class Engine {
@maxLength(5)
public Vendor: string | undefined;
@maxLength()
public Type: string | undefined;
}
let p: Engine = new Engine();
p.Vendor = "1sssssssssssssssssssssssss";
p.Type = "2ssssssssssssssssssssssssssss";
通过tsc命令编译后,生成js代码并引入到html中,执行结果如下:
结论
Attribute子类在.net的各种框架中已经大量使用,TypeScript的装饰器已经成为ES7的必备内容。虽然收到本身设计的限制,二者的实现方式并不同,但是却可以实现相同的功能,达到基于AOP编程的目标。
本文地址:https://blog.csdn.net/weixin_43263355/article/details/110137016
上一篇: Java8常用Lambda表达式
下一篇: 唐僧四人坐飞机去旅游