C# 11 新特性:泛型 Attribute

之前

使用JsonConverterAttribute,我们可以为任意类型自定义 Json 序列化。例如:.

[JsonConverter(typeof(UserJsonConverter))]
public class User
{
    public string Name { get; set; }
    public override string ToString()
    {
        return Name;
    }
}

public class UserJsonConverter : JsonConverter<User>
{
    public override User? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        //演示代码,不实现具体序列化逻辑
        reader.Read();
        return new User { Name = "MyIO" };
    }
}

反序列时将返回UserConverter生成的 User 实例:

var user = JsonSerializer.Deserialize<User>("{}");

C# 11 新特性:泛型 Attribute

但这不是本文的重点。重点是UserJsonConverter如何被实例化的。

查看JsonConverterAttribute的源码,JsonConverter的类型在构造函数中赋值给了ConverterType属性:

public class JsonConverterAttribute : JsonAttribute
{
    public JsonConverterAttribute(Type converterType)
    {
        ConverterType = converterType;
    } 

    public Type? ConverterType { get; private set; }
}

ConverterType的使用代码如下:

JsonConverter? converter;
 
Type? type = converterAttribute.ConverterType;
if (type == null)
{
    converter = converterAttribute.CreateConverter(typeToConvert);
}
else
{
    converter = (JsonConverter)Activator.CreateInstance(type)!;
}

也就是说,JsonConverte实例是通过反射(Activator.CreateInstance(type))生成的。

一般来说,反射的执行效率较低,这时我们注意到if分支,可以用CreateConverter方法创建实例。

修改实现代码如下:

public class UserJsonConverterAttribute : JsonConverterAttribute
{
    public override JsonConverter? CreateConverter(Type typeToConvert)
    {
        return new UserJsonConverter();
    }
}

[UserJsonConverterAttribute()]
public class User

同样可以反序列成功,这时使用的是强类型的JsonConverter。

但是,这种方式也有一个缺点,每当我们实现了一个xxJsonConverter,就需要再定义一个xxJsonConverterAttribute

而我们的目的,只是为了创建 JsonConverter 实例,代码逻辑相同,完全可以用范型方式实现。

但是在 C# 11 之前,是不支持这样定义的。

C# 11

在 C# 11 中,可以声明基类为System.Attribute的泛型类。

这样,上面的代码可以改造为如下形式:

public class GenericJsonConverterAttribute<T> : JsonConverterAttribute where T : JsonConverter, new()
{
    public override JsonConverter? CreateConverter(Type typeToConvert)
    {
        return new T();
    }
}

[GenericJsonConverter<UserJsonConverter>]
public class User

[GenericJsonConverter<OrderJsonConverter>]
public class Order