检测源生成器中可为空的枚举类型

问题描述 投票:0回答:1

我正在编写一个源生成器来处理我的模型类并为它们添加一些自定义序列化代码。模型类可以具有各种属性类型。我目前对可为空的枚举属性感兴趣。我无法检测到它们。我看到它们的名称和类型类似于“MyEnum?”但我需要知道这是否是一个枚举。

对于不可为空的枚举类型,检查是:

isEnum = classProperty.Type is INamedTypeSymbol namedType2 &&
    namedType2.EnumUnderlyingType != null;

可空值可以这样检测:

isNullable = classProperty.Type is INamedTypeSymbol namedType &&
    namedType.NullableAnnotation == NullableAnnotation.Annotated;

但我找不到这些的任何组合。这可能吗?如何实现?

更新:

这同样适用于像

int?
这样的属性类型。我希望这是一个
Nullable<int>
之类的东西。事实上,我可以看到该类型是通用的 (
IsGenericType == true
),其第一个类型参数 (
TypeArguments[0]
) 是
int
(在
int?
的情况下)。我只是无法在任何地方取消隐藏
Nullable<>
部分,所以我永远无法确定这是否真的是可为空的情况。

c# .net enums roslyn sourcegenerators
1个回答
0
投票

所有可为 null 的值类型实际上都是

Nullable<T>
类型,反之亦然。 因此,对于某些 ITypeSymbol.IsValueType
,检查 
Nullable<T>
 来确定类型是否属于 
T
类型应该足够了。

首先添加以下扩展方法:

public static partial class CodeAnalysisExtensions
{
    public static bool IsNullable(this ITypeSymbol typeSymbol) =>  
        typeSymbol.NullableAnnotation == NullableAnnotation.Annotated;

    public static bool IsNullableValueType(this ITypeSymbol typeSymbol) =>  
        typeSymbol.IsValueType && typeSymbol.IsNullable();

    public static bool TryGetNullableValueUnderlyingType(this ITypeSymbol typeSymbol, [NotNullWhen(returnValue: true)] out ITypeSymbol? underlyingType)
    {
        if (typeSymbol is INamedTypeSymbol namedType && typeSymbol.IsNullableValueType() && namedType.IsGenericType)
        {
            var typeParameters = namedType.TypeArguments;
            Debug.Assert(typeParameters.Length == 1);
            underlyingType = typeParameters[0];
            // TODO: decide what to return when the underlying type is not declared due to some compilation error.
            // TypeKind.Error indicats a compilation error, specifically a nullable type where the underlying type was not found.
            // I have observed that IsValueType will be true in such cases even though it is actually unknown whether the missing type is a value type
            // I chose to return false but you may prefer something else. 
            return underlyingType.TypeKind == TypeKind.Error ? false : true;
        }
        underlyingType = null;
        return false;
    }
    
    public static bool IsEnum(this ITypeSymbol typeSymbol) => 
        typeSymbol is INamedTypeSymbol namedType && namedType.EnumUnderlyingType != null;

    public static bool IsNullableEnumType(this ITypeSymbol typeSymbol) => 
        typeSymbol.TryGetNullableValueUnderlyingType(out var underlyingType) == true && underlyingType.IsEnum();
}

假设

classProperty
的类型为
IPropertySymbol
,您现在可以执行以下操作:

var isNullableEnum = classProperty.Type.TryGetNullableValueUnderlyingType(out var underlyingType) && underlyingType.IsEnum();

备注:

  • 在实践中,我发现我的声明所有可空值类型实际上都是类型

    Nullable<T>
    ,反之亦然在存在编译错误时不成立,特别是基础类型未定义的可空属性,例如:

    public CompilationError? MyCompilationError { get; set; }
    

    在这种情况下,与

    ITypeSymbol
    相对应的
    CompilationError?
    将具有
    IsValueType == true
    ,尽管实际上并不知道这一点。 发生这种情况时,底层类型
    TypeKind将具有值
    TypeKind.Error
    。 在上面的扩展方法中,我检查这一点并从
    false
    返回
    TryGetNullableValueUnderlyingType()
    。 您可以选择以不同的方式处理这种边界情况。

演示小提琴在这里

© www.soinside.com 2019 - 2024. All rights reserved.