Как использовать атрибуты, чтобы влиять на функции в C #? - PullRequest
0 голосов
/ 11 марта 2019

В соответствии с документацией в атрибуте C # , кажется, что в C # атрибут может быть только хранилище метаданных времени компиляции. Вы должны использовать отражение, чтобы манипулировать им.

...

public class AnimalTypeAttribute : Attribute {
    // The constructor is called when the attribute is set.
    public AnimalTypeAttribute(Animal pet) {
        thePet = pet;
    }

    // Keep a variable internally ...
    protected Animal thePet;

    // .. and show a copy to the outside world.
    public Animal Pet {
        get { return thePet; }
        set { thePet = value; }
    }
}

...

class DemoClass {
    static void Main(string[] args) {
        AnimalTypeTestClass testClass = new AnimalTypeTestClass();
        Type type = testClass.GetType();
        // Iterate through all the methods of the class.
        foreach(MethodInfo mInfo in type.GetMethods()) {
            // Iterate through all the Attributes for each method.
            foreach (Attribute attr in
                Attribute.GetCustomAttributes(mInfo)) {
                // Check for the AnimalType attribute.
                if (attr.GetType() == typeof(AnimalTypeAttribute))
                    Console.WriteLine(
                        "Method {0} has a pet {1} attribute.",
                        mInfo.Name, ((AnimalTypeAttribute)attr).Pet);
            }

        }
    }
}

Однако я заметил, что FlagsAttribute , не добавил ни одной переменной. Хотя, он манипулировал / перехватывал вывод ToString () (я думаю). Как FlagsAttribute делает это? Как я подражаю поведению или влияю на некоторые функции в моих пользовательских атрибутах?

[FlagsAttribute] 
enum MultiHue : short {
   None = 0,
   Black = 1,
   Red = 2,
   Green = 4,
   Blue = 8
};

...

Console.WriteLine( "{0,3} - {1:G}", 3, (MultiHue)3); // output 3 - Black, Red

1 Ответ

3 голосов
/ 11 марта 2019

Считывая код для Enum.cs, мы видим, что метод ToString вызывает InternalFormat:

public override string ToString()
{
    // Returns the value in a human readable format.  For PASCAL style enums who's value maps directly the name of the field is returned.
    // For PASCAL style enums who's values do not map directly the decimal value of the field is returned.
    // For BitFlags (indicated by the Flags custom attribute): If for each bit that is set in the value there is a corresponding constant
    // (a pure power of 2), then the OR string (ie "Red, Yellow") is returned. Otherwise, if the value is zero or if you can't create a string that consists of
    // pure powers of 2 OR-ed together, you return a hex value

    // Try to see if its one of the enum values, then we return a String back else the value
    return InternalFormat((RuntimeType)GetType(), ToUInt64()) ?? ValueToString();
}

private static string InternalFormat(RuntimeType eT, ulong value)
{
    Debug.Assert(eT != null);

    // These values are sorted by value. Don't change this
    TypeValuesAndNames entry = GetCachedValuesAndNames(eT, true);

    if (!entry.IsFlag) // Not marked with Flags attribute
    {
        return Enum.GetEnumName(eT, value);
    }
    else // These are flags OR'ed together (We treat everything as unsigned types)
    {
        return InternalFlagsFormat(eT, entry, value);
    }
}

И InternalFormat вызывает GetCachedValuesAndNames для доступа к информационному кешу.

В этом методе GetCachedValuesAndNames мы видим, что он проверяет, определен ли FlagsAttribute (bool isFlags = enumType.IsDefined(typeof(FlagsAttribute), inherit: false);):

private static TypeValuesAndNames GetCachedValuesAndNames(RuntimeType enumType, bool getNames)
{
    TypeValuesAndNames entry = enumType.GenericCache as TypeValuesAndNames;

    if (entry == null || (getNames && entry.Names == null))
    {
        ulong[] values = null;
        string[] names = null;
        GetEnumValuesAndNames(
            enumType.GetTypeHandleInternal(),
            JitHelpers.GetObjectHandleOnStack(ref values),
            JitHelpers.GetObjectHandleOnStack(ref names),
            getNames);
        bool isFlags = enumType.IsDefined(typeof(FlagsAttribute), inherit: false);

        entry = new TypeValuesAndNames(isFlags, values, names);
        enumType.GenericCache = entry;
    }

    return entry;
}

Таким образом, он действительно использует отражение, чтобы определить, существует ли FlagsAttribute, и соответствующим образом корректирует результат ToString.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...