如何将 Roslyn 语义模型返回的类型符号名称与 Mono.Cecil 返回的类型符号名称相匹配?
How to match type symbol names as returned by Roslyn semantic model to those returned by Mono.Cecil?
我有如下一段代码:
var paramDeclType = m_semanticModel.GetTypeInfo(paramDecl.Type).Type;
其中 paramDeclType.ToString()
returns
System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<int>>
我也有来自 Mono.Cecil - paramDef.ParameterType.FullName
returns
的相同信息
System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<System.Int32>>
我想比较来自这两个不同来源的类型名称。
如果可能,我想避免复杂的正则表达式解析。
欢迎提出想法。
P.S.
与此同时,我将寻找 Mono.Cecil
Microsoft 的替代品 - System.Reflection.Metadata。也许 Microsoft 的两个库能够生成兼容的类型名称。
AFAIK,SRM 比 Mono.Cecil 低级得多。我还没有足够地使用它来 100% 确定,但是 AFICS,在 SRM 中你需要提供你自己的 type 系统,意思是,你会得到 type 令牌、method tokens 等,以及如何在程序中表示它取决于您(这看起来比使用 Cecil 复杂得多)。 (你可以看看 ILSpy 代码 https://github.com/icsharpcode/ILSpy/tree/master/ICSharpCode.Decompiler 来了解一下)
我不认为从 Roslyn 名称转换为 -> Cecil 名称那么难(当然,取决于您的需要)。你可以试试:
查看是否可以要求 Roslyn 始终使用 BLC 类型名称(而不是 C# 原始类型名称)
删除排名信息(1,
2 等),必要时可能添加 <>
将 Cecil (/) 使用的嵌套类型分隔符替换为点 (.)
也许还有一些其他情况...:)
也就是说,另一种选择是尝试将名称映射到其 System.Type 等价物;一旦你有一个 System.Type 你可以在 Cecil 中调用 Module.Import() 并得到一个 TypeRefernce 并比较它的全名(不幸的是不能通过相等比较两个 TypeReferences) .
Obs:试图将其添加为评论,但它太长了:)
您可以使用类似于以下代码的内容来计算完全限定的类型名称:
using System;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var st = CSharpSyntaxTree.ParseText("class Foo { int i; }");
var v = st.GetRoot().DescendantNodesAndSelf().OfType<VariableDeclarationSyntax>().Single().Type;
var pts = (PredefinedTypeSyntax) v;
var Mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var compilation = CSharpCompilation.Create("MyCompilation", syntaxTrees: new[] { st }, references: new[] { Mscorlib });
var model = compilation.GetSemanticModel(st);
var symbol = model.GetTypeInfo(pts);
System.Console.WriteLine(symbol.Type.ContainingNamespace.Name + "." + symbol.Type.Name);
// or even something like...
var intType = model.Compilation.GetTypeByMetadataName(typeof(int).FullName);
Console.WriteLine(v.ToString() + " " + v.GetType().FullName + " " + (symbol.Type.SpecialType == intType.SpecialType));
}
}
}
我想分享一下我目前使用的功能:
private const string GENERIC_ARG_COUNT_PATTERN = @"`\d+";
private static readonly Regex s_genericArgCountRegex = new Regex(GENERIC_ARG_COUNT_PATTERN);
public static bool IsSameType(this TypeReference typeRef, ITypeSymbol typeSymbol, bool isByRef)
{
if (isByRef || typeRef.IsByReference)
{
return isByRef && typeRef.IsByReference && DoIsSameType(((ByReferenceType)typeRef).ElementType, typeSymbol);
}
return DoIsSameType(typeRef, typeSymbol);
static bool DoIsSameType(TypeReference typeRef, ITypeSymbol typeSymbol)
{
var arrayTypeSymbol = typeSymbol as IArrayTypeSymbol;
var arrayTypeRef = typeRef as ArrayType;
if (arrayTypeSymbol != null || arrayTypeRef != null)
{
return arrayTypeSymbol != null
&& arrayTypeRef != null
&& arrayTypeSymbol.Rank == arrayTypeRef.Rank
&& DoIsSameType(arrayTypeRef.ElementType, arrayTypeSymbol.ElementType);
}
// Optimistic approach
var declFullName = typeSymbol.ToString();
var typeRefFullName = typeRef.FullName;
if (typeRefFullName.Contains('`'))
{
typeRefFullName = s_genericArgCountRegex.Replace(typeRef.FullName, "");
}
if (declFullName == typeRefFullName)
{
return true;
}
// Built-in types - either top level or as generic arguments.
if (typeRef is GenericInstanceType genTypeRef)
{
if (!(typeSymbol is INamedTypeSymbol namedTypeSymbol) ||
!namedTypeSymbol.IsGenericType ||
genTypeRef.ElementType.Name != namedTypeSymbol.ConstructedFrom.MetadataName ||
genTypeRef.GenericArguments.Count != namedTypeSymbol.TypeArguments.Length ||
genTypeRef.ElementType.Namespace != namedTypeSymbol.ConstructedFrom.ContainingNamespace.ToString())
{
return false;
}
for (int i = 0; i < genTypeRef.GenericArguments.Count; ++i)
{
if (!DoIsSameType(genTypeRef.GenericArguments[i], namedTypeSymbol.TypeArguments[i]))
{
return false;
}
}
return true;
}
return
typeRef.Name == typeSymbol.Name &&
typeRef.IsBuiltInType() &&
typeSymbol.ContainingNamespace.Name == "System";
}
}
public static bool IsBuiltInType(this TypeReference t)
{
return t.Scope.Name == "mscorlib" && DoIsBuiltInType(t);
static bool DoIsBuiltInType(TypeReference t) => t switch
{
ArrayType arrayType => DoIsBuiltInType(arrayType.ElementType),
ByReferenceType byReferenceType => DoIsBuiltInType(byReferenceType.ElementType),
_ => t.IsPrimitive || t.Name == "Object" || t.Name == "Decimal" || t.Name == "String",
};
}
我有如下一段代码:
var paramDeclType = m_semanticModel.GetTypeInfo(paramDecl.Type).Type;
其中 paramDeclType.ToString()
returns
System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<int>>
我也有来自 Mono.Cecil - paramDef.ParameterType.FullName
returns
System.Collections.Generic.Dictionary`2<System.String,System.Collections.Generic.List`1<System.Int32>>
我想比较来自这两个不同来源的类型名称。
如果可能,我想避免复杂的正则表达式解析。
欢迎提出想法。
P.S.
与此同时,我将寻找 Mono.Cecil
Microsoft 的替代品 - System.Reflection.Metadata。也许 Microsoft 的两个库能够生成兼容的类型名称。
AFAIK,SRM 比 Mono.Cecil 低级得多。我还没有足够地使用它来 100% 确定,但是 AFICS,在 SRM 中你需要提供你自己的 type 系统,意思是,你会得到 type 令牌、method tokens 等,以及如何在程序中表示它取决于您(这看起来比使用 Cecil 复杂得多)。 (你可以看看 ILSpy 代码 https://github.com/icsharpcode/ILSpy/tree/master/ICSharpCode.Decompiler 来了解一下)
我不认为从 Roslyn 名称转换为 -> Cecil 名称那么难(当然,取决于您的需要)。你可以试试:
查看是否可以要求 Roslyn 始终使用 BLC 类型名称(而不是 C# 原始类型名称)
删除排名信息(
1,
2 等),必要时可能添加 <>将 Cecil (/) 使用的嵌套类型分隔符替换为点 (.)
也许还有一些其他情况...:)
也就是说,另一种选择是尝试将名称映射到其 System.Type 等价物;一旦你有一个 System.Type 你可以在 Cecil 中调用 Module.Import() 并得到一个 TypeRefernce 并比较它的全名(不幸的是不能通过相等比较两个 TypeReferences) .
Obs:试图将其添加为评论,但它太长了:)
您可以使用类似于以下代码的内容来计算完全限定的类型名称:
using System;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var st = CSharpSyntaxTree.ParseText("class Foo { int i; }");
var v = st.GetRoot().DescendantNodesAndSelf().OfType<VariableDeclarationSyntax>().Single().Type;
var pts = (PredefinedTypeSyntax) v;
var Mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var compilation = CSharpCompilation.Create("MyCompilation", syntaxTrees: new[] { st }, references: new[] { Mscorlib });
var model = compilation.GetSemanticModel(st);
var symbol = model.GetTypeInfo(pts);
System.Console.WriteLine(symbol.Type.ContainingNamespace.Name + "." + symbol.Type.Name);
// or even something like...
var intType = model.Compilation.GetTypeByMetadataName(typeof(int).FullName);
Console.WriteLine(v.ToString() + " " + v.GetType().FullName + " " + (symbol.Type.SpecialType == intType.SpecialType));
}
}
}
我想分享一下我目前使用的功能:
private const string GENERIC_ARG_COUNT_PATTERN = @"`\d+";
private static readonly Regex s_genericArgCountRegex = new Regex(GENERIC_ARG_COUNT_PATTERN);
public static bool IsSameType(this TypeReference typeRef, ITypeSymbol typeSymbol, bool isByRef)
{
if (isByRef || typeRef.IsByReference)
{
return isByRef && typeRef.IsByReference && DoIsSameType(((ByReferenceType)typeRef).ElementType, typeSymbol);
}
return DoIsSameType(typeRef, typeSymbol);
static bool DoIsSameType(TypeReference typeRef, ITypeSymbol typeSymbol)
{
var arrayTypeSymbol = typeSymbol as IArrayTypeSymbol;
var arrayTypeRef = typeRef as ArrayType;
if (arrayTypeSymbol != null || arrayTypeRef != null)
{
return arrayTypeSymbol != null
&& arrayTypeRef != null
&& arrayTypeSymbol.Rank == arrayTypeRef.Rank
&& DoIsSameType(arrayTypeRef.ElementType, arrayTypeSymbol.ElementType);
}
// Optimistic approach
var declFullName = typeSymbol.ToString();
var typeRefFullName = typeRef.FullName;
if (typeRefFullName.Contains('`'))
{
typeRefFullName = s_genericArgCountRegex.Replace(typeRef.FullName, "");
}
if (declFullName == typeRefFullName)
{
return true;
}
// Built-in types - either top level or as generic arguments.
if (typeRef is GenericInstanceType genTypeRef)
{
if (!(typeSymbol is INamedTypeSymbol namedTypeSymbol) ||
!namedTypeSymbol.IsGenericType ||
genTypeRef.ElementType.Name != namedTypeSymbol.ConstructedFrom.MetadataName ||
genTypeRef.GenericArguments.Count != namedTypeSymbol.TypeArguments.Length ||
genTypeRef.ElementType.Namespace != namedTypeSymbol.ConstructedFrom.ContainingNamespace.ToString())
{
return false;
}
for (int i = 0; i < genTypeRef.GenericArguments.Count; ++i)
{
if (!DoIsSameType(genTypeRef.GenericArguments[i], namedTypeSymbol.TypeArguments[i]))
{
return false;
}
}
return true;
}
return
typeRef.Name == typeSymbol.Name &&
typeRef.IsBuiltInType() &&
typeSymbol.ContainingNamespace.Name == "System";
}
}
public static bool IsBuiltInType(this TypeReference t)
{
return t.Scope.Name == "mscorlib" && DoIsBuiltInType(t);
static bool DoIsBuiltInType(TypeReference t) => t switch
{
ArrayType arrayType => DoIsBuiltInType(arrayType.ElementType),
ByReferenceType byReferenceType => DoIsBuiltInType(byReferenceType.ElementType),
_ => t.IsPrimitive || t.Name == "Object" || t.Name == "Decimal" || t.Name == "String",
};
}