簡體   English   中英

將 AttributeData 轉換為已知的屬性類型 Roslyn

[英]Converting AttributeData into a known attribute type Roslyn

所以我的代碼中有一個已知的屬性類型,比如:

[AttributeUsage(AttributeTargets.Method)]
public class AliasAttribute : Attribute
{
    public string Alias;
    public AliasAttribute(string alias)
    {
        Alias = alias;
    }
}

我也有一個用這個屬性裝飾的方法,我在 Roslyn 中得到了一個IMethodSymbol

// E.g. code which Roslyn gets to analyze
[Alias("Hello")] public void World() {}

// Actual Roslyn code
var method = (IMethodSymbol) compilation.GetSymbolsWithName("World").Single();

我可以使用與此類似的代碼檢索屬性數據:

var attributeData = method.GetAttributes().Single();

現在我想使用一些簡單的內置方法將此AttributeData轉換為已知類型,例如:

AliasAttribute alias = attributeData.MapToType<AliasAttribute>();

我目前這樣做的方式很復雜:它涉及使用反射來實例化屬性類型。 但是,我並沒有考慮到很多極端情況,比如以通用方式的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;
        }
    }
}

更多輔助方法

如果有人發現上面的代碼有用,我還定義了幾個方便的方法:

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;
}

然后,在程序開始時的某個全局位置,保存您要轉換為的類型:

public static class RelevantSymbols
{
    public static AttributeSymbolWrapper<AliasAttribute> AliasAttribute;

    public static void Init(Compilation compilation)
    {
        AliasAttribute.Init(compilation);
    }
}

最后,您可以這樣使用它,而無需在 type 參數中重復屬性類型:

var method = (IMethodSymbol) compilation.GetSymbolsWithName("World").Single();

if (method.TryGetAttribute(RelevantSymbols.AliasAttribute, out var attribute))
{
    // do something with the attibute data
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM