1

I need to create attribute for functions that will create files according to a given name before the call to the function or even if there is no call to the function.
For example, if I have function with atribute [some("C:\\hello.txt")]:

[some("C:\\hello.txt")]
private void foo()
{
    // do something
}

When I will run the application it will create this file ("C:\hello.txt") before calling the function or even if there is no call to the function..

I tried with two techniques:
1. Creating the file in the constructor
2. Creating the file with reflection.

But none of them worked for me.

First try (with constructor):
I tried create the file in the constructor every time there is a new attribute.
In this method I tried to create the file before the enter to the Main function.
While it parsing the functions it will find the attributes and create the files.
Expected:
Two files should be created:
1. C:\hello.txt
2. C:\bye.txt

In reality => nothing happen.

[some("C:\\hello.txt")]
private void foo()
{
    // do something
}

[some("C:\\bye.txt")]
private void foo()
{
    // do something
}

public class someAttribute : Attribute
{
    public someAttribute(string fileToCreate)
    {
        // File.Create(fileToCreate);
        Console.WriteLine("Create file " + fileToCreate);
    }
}

static void Main(string[] args)
{
    // something
}

Second try (with reflection):
Expected:
One file should be created:
1. C:\hello.txt

In reality => "types" variables is empty and nothing is being created.

[some(fileToCreate = "C:\\hello.txt")]
private void foo()
{
    // do something
}

public class someAttribute : Attribute
{
    public string fileToCreate {get; set;}
}

static void Main(string[] args)
{

    var types = from t in Assembly.GetExecutingAssembly().GetTypes()
                where t.GetCustomAttributes<someAttribute>().Count() > 0
                select t;
    foreach(var t in types)  // types is null
    {
        string n = t.Name;
        foreach(var p in t.GetProperties()) 
        {
            // File.Create(fileToCreate)
            Console.WriteLine(p.fileToCreate);
        }
    }

}
E235
  • 11,560
  • 24
  • 91
  • 141
  • Do you have the list of classes that the attributed functions could belong to? If so, then this question might be useful: http://stackoverflow.com/questions/2831809/how-would-i-use-reflection-to-call-all-the-methods-that-has-a-certain-custom-att. To get attributes attached to a method, you'll need to use reflection on the class the method belongs to. – purplecat Jan 23 '17 at 19:48
  • Regarding your first attempt (putting the create-file code in the attribute's constructor), the reason it didn't work is answered here: http://stackoverflow.com/questions/1168535/when-is-a-custom-attributes-constructor-run#1168590. – purplecat Jan 23 '17 at 20:38

1 Answers1

1

Your first attempt didn't work because an attribute's constructor is run when an attribute is examined See When is a custom attribute's constructor run? for additional details. It won't be run just by the fact that a method has that attribute in the code. So reflection will be needed to get a list of methods that have the desired attribute.

Your second attempt came close, but didn't work because you only looked at the attributes attached to the class types. You'll need to go one step further to look at the methods within the classes.

I came up with a solution, but be warned that it could affect performance since it looks at every type and method in the assemblies linked to your project. You may want to limit the assemblies to only the ones that you can expect to have someAttribute. See C#: List All Classes in Assembly for some examples on how to do this.

static void Main()
{
    var methods = AppDomain.CurrentDomain.GetAssemblies()
                    //Get a sequence of all types in the referenced assemblies
                    .SelectMany(assembly => assembly.GetTypes()) 
                    //Get a sequence of all the methods in those types. 
                    //The BindingFlags are needed to make sure both public and non-public instance methods are included. 
                    //Otherwise private methods are skipped.
                    .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
                    //And finally, filter to only those methods that have someAttribute attached to them
                    .Where(method => Attribute.IsDefined(method, typeof(someAttribute)));

    foreach (MethodInfo methodInfo in methods)
    {
        IEnumerable<someAttribute> SomeAttributes = methodInfo.GetCustomAttributes<someAttribute>();
        foreach (var attr in SomeAttributes)
        {
            //Here, you can create the file.
            Console.WriteLine(attr.fileToCreate);
        }
    }
}
Community
  • 1
  • 1
purplecat
  • 252
  • 1
  • 6
  • 10
  • Thanks you ! It works. I just made a little change like you said to go over only one assembly for performance. This is my little change to do it: var methods = Assembly.GetExecutingAssembly().GetTypes() .SelectMany(t => t.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) .Where(method => Attribute.IsDefined(method, typeof(someAttribute))); – E235 Jan 24 '17 at 12:27