3

I created customized attribute named someAttribute.
It has boolean member named Skip:

public class someAttribute : Attribute
{
    public bool Skip { get; set; }
}

In the Main I initialized the member Skip with the value true for the method foo().

Next I am calling the function foo() which has the attribute [someAttribute()] and I want to check if the member Skip was initialized:

[someAttribute()]
private static int foo()
{
    if(Skip)
     {
         return 0;
     }

    return 1;
} 

I received the error "The name 'Skip' does not exist in the current context".
enter image description here

How can I check the members of attribute inside a method that using this attribute ?

My full code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace ConsoleApplication1
{
    class ProgramTest
    {
        [someAttribute()]
        private static int foo()
        {
            if(Skip)
             {
                 return 0;
             }

            return 1;
        }

        public class someAttribute : Attribute
        {
            public bool Skip { get; set; }
        }

        public static void initAttributes()
        {
            var methods = Assembly.GetExecutingAssembly().GetTypes().SelectMany(t => t.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static))
           .Where(method => Attribute.IsDefined(method, typeof(someAttribute)));

            foreach (MethodInfo methodInfo in methods)
            {
                IEnumerable<someAttribute> SomeAttributes = methodInfo.GetCustomAttributes<someAttribute>();
                foreach (var attr in SomeAttributes)
                {
                    attr.Skip = true;
                }
            }
        }
        static void Main(string[] args)
        {
            initAttributes();
            int num = foo();
        }
     }
 }

EDIT:
I added BindingFlags.Static in order for the refelction to get the static function foo().

E235
  • 11,560
  • 24
  • 91
  • 141
  • Possible duplicate of [Reflection - get attribute name and value on property](http://stackoverflow.com/questions/6637679/reflection-get-attribute-name-and-value-on-property) – MakePeaceGreatAgain Feb 01 '17 at 09:41
  • 1
    I suppose you´ll need `BindingFlags.Static | BindingFlags.NonPublic` as your `foo`-method is `private static`. – MakePeaceGreatAgain Feb 01 '17 at 09:43
  • I edited the code and added it, thanks. – E235 Feb 01 '17 at 10:15
  • Why do you want or need to have the attribute at all? The attribute is a couple of lines up? It won't change after the code has been compiled. Are you sure you don't just need a constant? Or even easier, just remove the code altogether? In the second piece of code, `Skip` will always be `false`, why not simply remove the code? – Lasse V. Karlsen Feb 01 '17 at 10:23
  • 1
    Please note that you will need to store the references to the attributes you modify after you have modified them, and then use those references inside the methods in question. The reason for this is that each call to `GetCustomAttributes` will create *new attribute type instances*. In other words, your loop that sets the `Skip` property to `true` has no meaning because the next time you go fishing for the same attributes you will get new ones, that is again back to `false`. – Lasse V. Karlsen Feb 01 '17 at 10:29
  • See [this .NET fiddle](https://dotnetfiddle.net/TU40mL) for an example. – Lasse V. Karlsen Feb 01 '17 at 10:33
  • Basically, the conclusion is that attributes are not variables, they're metadata that is obtained when you need it. If you need to store persistent state that can change at runtime you need to find some other method, like *normal variables*. – Lasse V. Karlsen Feb 01 '17 at 10:34
  • @LasseV.Karlsen, regarding your first comment. I just gave here a small example. In the reality I have a testing application and before running the tests I am creating files that are required for each test. For each test I create the relveant file but if I failed to create the file I wanted to **notify** the relvant test. So I thought to do it by attributes. For example, I am trying to create file `test.txt` for `Test1`. If I failed I wanted to update an attribute for Test1 with `Skip = true` and inside `Test1` I will skip the test. My workaround for now is to create list of the failed files. – E235 Feb 01 '17 at 14:39

2 Answers2

2

You can get the attribute with something like this:

[someAttribute()]
private static int foo()
{
    if (GetCurrentMethodAttribute<someAttribute>().Skip)
    {
        return 0;
    }
    return 1;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static T GetCurrentMethodAttribute<T>() where T : Attribute
{
    return (T)new StackTrace().GetFrame(1).GetMethod().GetCustomAttribute(typeof(T));
}

Anyway, in your initAttributes() method, when you do Assembly.GetExecutingAssembly().GetTypes().SelectMany, you don't get the method foo because it is static, so you should also use the BindingFlags.Static.

Also, you cannot set an attribute value at runtime, so the changes made initAttributes won't be "seen" when you retrieve the attribute (so Skip property will be false)

Community
  • 1
  • 1
Matteo Umili
  • 3,412
  • 1
  • 19
  • 31
  • Upvoted for the problem with setting attribute properties at runtime. But I think the stack trace approach may fail if the compiler inlines a method or due to other optimizations. – René Vogt Feb 01 '17 at 09:41
1

You can use reflection to achieve this:

 [someAttribute()]
 private static int foo()
 {
     if (typeof(ProgramTest).GetMethod(nameof(foo)).
                      GetCustomAttribute<someAttribute>()?.Skip ?? false)
         return 0;

     return 1;
 }

This gets the MethodInfo for foo and checks if that method has the someAttribute. If so, it checks the Skip property of that attribute instance.

In your case, GetMethod() will need some more arguments, because foo is private and static. If you have multiple overloads of foo you will also need to specify the parameter types:

private static int foo()
{
    // some foo code
    return 1;
}

[someAttribute(Skip=true)]
private static int foo(int arg)
{
    if (typeof(ProgramTest).GetMethod(nameof(foo), 
                        BindingFlags.Static | BindingFlags.NonPublic,
                        null, // use default binder
                        new Type[] {typeof(int)}, // list of parameter types
                        null) // array of parameter modifiers
                .GetCustomAttribute<someAttribute>()?.Skip ?? false)
        return 0;
    return arg;
}

(Note: In a previous edit I suggested the ConditionalAttribute instead, but obviously this is not applicable to non-void methods).

René Vogt
  • 43,056
  • 14
  • 77
  • 99