使用 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 序列化规则的事情
- 看类型是不是public
- 查看是否应用了 [SerializeField] 属性
- 看类型是int、float、string、double等
- 查看类型,如果它的class具有[Serializable]属性
我想就是这样。目前,我正试图弄清楚该字段的类型,并迷失在一个兔子沃伦的选项中,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) 查看类型是否为int
、float
、string
、double
等
幸运的是,Type
也是 IFieldSymbol
接口的 属性,所以我们可以只检查字段符号的 属性。如果你只关心简单类型,你可以引用结果 ITypeSymbol
的 Name
属性。
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:
顺便说一句,我完全同意,好的 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 序列化规则的事情
- 看类型是不是public
- 查看是否应用了 [SerializeField] 属性
- 看类型是int、float、string、double等
- 查看类型,如果它的class具有[Serializable]属性
我想就是这样。目前,我正试图弄清楚该字段的类型,并迷失在一个兔子沃伦的选项中,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) 查看类型是否为int
、float
、string
、double
等
幸运的是,Type
也是 IFieldSymbol
接口的 属性,所以我们可以只检查字段符号的 属性。如果你只关心简单类型,你可以引用结果 ITypeSymbol
的 Name
属性。
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:
顺便说一句,我完全同意,好的 Roslyn 文档似乎非常罕见。