使用 Roslyn 确定字段是否可序列化

Determining if a field is serializable with Roslyn

我找不到关于 Roslyn 的好文档,所以如果我遗漏了什么,请原谅我,这应该是显而易见的。 我正在解析一些代码,我的目标是从 class.

中提取每个可序列化的字段

目前我只有这些。首先我解析代码文件

            SyntaxTree tree = CSharpSyntaxTree.ParseText(programText);
            CSharpCompilation compilation = CSharpCompilation.Create("HelloWorld")
                                                             .AddReferences(MetadataReference
                                                                                .CreateFromFile(typeof(object)
                                                                                                .Assembly
                                                                                                .Location))
                                                             .AddSyntaxTrees(tree);

            CompilationUnitSyntax root = tree.GetCompilationUnitRoot();

接下来,我从每个 class

中获取所有字段
        foreach (MemberDeclarationSyntax member in root.Members)
        {
            if (member is ClassDeclarationSyntax classDeclarationSyntax)
            {

                foreach (MemberDeclarationSyntax rootGameMember in classDeclarationSyntax.Members)
                {
                    if (IsFieldSerializable(rootGameMember))
                    {
                    }
                }
            }
        }

aaaa 现在我卡住了。我想对这个字段做的是一些基于 Unity 3D 序列化规则的事情

我想就是这样。目前,我正试图弄清楚该字段的类型,并迷失在一个兔子沃伦的选项中,none 似乎给了我我想要的东西。

您需要做的第一件事是获取代表您的字段的符号,您可以使用 SemanticModel class 来完成。我认为这将有助于解释如何根据您的示例获得语义模型:

https://github.com/dotnet/roslyn/wiki/Getting-Started-C%23-Semantic-Analysis

TLDR:使用 compilation.GetSemanticModel()

这个问题比我原来的答案更好地回答了如何做到这一点,所以请看这里:

现在您已经掌握了有关该字段的语义信息,让我们一次一个地查看每个问题。

1)看类型是不是public

这应该很简单,因为 IFieldSymbol 界面有一个 DeclaredAccessibility 属性 会告诉你你需要知道的!

if(fieldSymbol.DeclaredAccessibility != Accessibility.Public)
{
    // not interested in no-public members
}

2) 查看是否应用了属性[SerializeField]

使用IFieldSymbol接口的GetAttributes()方法。然后你可以像查询任何其他 INamedTypeSymbol.

一样查询属性的 AttributeClass
var attributes = fieldSymbol.GetAttributes();
if(!attributes.Any(attribute => attribute.AttributeClass.Name == "SerializeField"))
{
    // only interested in fields with the SerializeField attribute
}

3) 查看类型是否为intfloatstringdouble

幸运的是,Type 也是 IFieldSymbol 接口的 属性,所以我们可以只检查字段符号的 属性。如果你只关心简单类型,你可以引用结果 ITypeSymbolName 属性。

var fieldTypes = new List<string> { "int", "float", "string", "double" };

if(!fieldTypes.Contains(fieldSymbol.Type.Name))
{
    // only interested in certain types
}

(您可能需要在调试器中仔细检查类型名称字符串)

4) 查看类型,如果它的 class 具有 [Serializable] 属性

据此我猜您的意思是 class 具有 [Serializable] 属性。为此,您可以使用与 2 相同的方法,但您需要 class 符号,因此这必须在您的字段检查功能之外完成。

var classSymbol = model.GetSymbolInfo(classDeclarationSyntax).Symbol;
// remember to null check this
var attributes = classSymbol.GetAttributes();
if(attributes.Any(attribute => attribute.AttributeClass.Name == "Serializable"))
{
   // class is serializable
}

编辑: 如果您认为按名称检查属性有点老套,这个答案也可能对您有所帮助!

IFieldSymbol:

https://docs.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.ifieldsymbol?view=roslyn-dotnet

INamedTypeSymbol:

https://docs.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.inamedtypesymbol?view=roslyn-dotnet

顺便说一句,我完全同意,好的 Roslyn 文档似乎非常罕见。