所以我的代码中有一个已知的属性类型,例如:
[AttributeUsage(AttributeTargets.Method)]
public class AliasAttribute : Attribute
{
public string Alias;
public AliasAttribute(string alias)
{
Alias = alias;
}
}
Run Code Online (Sandbox Code Playgroud)
我还有一个用这个属性装饰的方法,我IMethodSymbol在 Roslyn 中得到了它:
// E.g. code which Roslyn gets to analyze
[Alias("Hello")] public void World() {}
// Actual Roslyn code
var method = (IMethodSymbol) compilation.GetSymbolsWithName("World").Single();
Run Code Online (Sandbox Code Playgroud)
我可以使用与此类似的代码检索属性数据:
var attributeData = method.GetAttributes().Single();
Run Code Online (Sandbox Code Playgroud)
现在我想使用一些简单的内置方法将其转换AttributeData为已知类型,例如如下所示:
AliasAttribute alias = attributeData.MapToType<AliasAttribute>();
Run Code Online (Sandbox Code Playgroud)
我目前的做法很复杂:它涉及使用反射实例化属性类型。然而,我没有考虑到很多边缘情况,比如params通用方式的参数。请参阅下面答案中的代码。但是,它太复杂了,我想知道是否有更简单的方法来做到这一点。
正如问题中所指出的,这是我使用反射的解决方案。
public static T MapToType<T>(this AttributeData attributeData) where T : Attribute
{
T attribute;
if (attributeData.AttributeConstructor != null && attributeData.ConstructorArguments.Length > 0)
{
attribute = (T) Activator.CreateInstance(typeof(T), attributeData.GetActualConstuctorParams().ToArray());
}
else
{
attribute = (T) Activator.CreateInstance(typeof(T));
}
foreach (var p in attributeData.NamedArguments)
{
typeof(T).GetField(p.Key).SetValue(attribute, p.Value.Value);
}
return attribute;
}
public static IEnumerable<object> GetActualConstuctorParams(this AttributeData attributeData)
{
foreach (var arg in attributeData.ConstructorArguments)
{
if (arg.Kind == TypedConstantKind.Array)
{
// Assume they are strings, but the array that we get from this
// should actually be of type of the objects within it, be it strings or ints
// This is definitely possible with reflection, I just don't know how exactly.
yield return arg.Values.Select(a => a.Value).OfType<string>().ToArray();
}
else
{
yield return arg.Value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果有人发现上面的代码有用,我还定义了一些方便的方法:
public static bool TryGetAttributeData(this ISymbol symbol, ISymbol attributeType, out AttributeData attributeData)
{
foreach (var a in symbol.GetAttributes())
{
if (SymbolEqualityComparer.Default.Equals(a.AttributeClass, attributeType))
{
attributeData = a;
return true;
}
}
attributeData = default;
return false;
}
public struct AttributeSymbolWrapper<T>
{
public INamedTypeSymbol symbol;
private static INamedTypeSymbol GetKnownSymbol(Compilation compilation, System.Type t)
{
return (INamedTypeSymbol) compilation.GetTypeByMetadataName(t.FullName);
}
public void Init(Compilation compilation)
{
symbol = GetKnownSymbol(compilation, typeof(T));
}
}
public static bool TryGetAttribute<T>(this ISymbol symbol, AttributeSymbolWrapper<T> attributeSymbolWrapper, out T attribute) where T : System.Attribute
{
if (TryGetAttributeData(symbol, attributeSymbolWrapper.symbol, out var attributeData))
{
attribute = attributeData.MapToType<T>();
return true;
}
attribute = default;
return false;
}
public static bool HasAttribute(this ISymbol symbol, ISymbol attributeType)
{
foreach (var a in symbol.GetAttributes())
{
if (SymbolEqualityComparer.Default.Equals(a.AttributeClass, attributeType))
{
return true;
}
}
return false;
}
Run Code Online (Sandbox Code Playgroud)
然后,在程序开始时的全局某个位置,保存您想要转换的类型:
public static class RelevantSymbols
{
public static AttributeSymbolWrapper<AliasAttribute> AliasAttribute;
public static void Init(Compilation compilation)
{
AliasAttribute.Init(compilation);
}
}
Run Code Online (Sandbox Code Playgroud)
最后,您可以像这样使用它,而无需在类型参数中重复属性类型:
var method = (IMethodSymbol) compilation.GetSymbolsWithName("World").Single();
if (method.TryGetAttribute(RelevantSymbols.AliasAttribute, out var attribute))
{
// do something with the attibute data
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
566 次 |
| 最近记录: |