1

I am working on a library that involves retrieving the methods of a given type. I have been using Type.GetMethods, but I've noticed an issue. Let's say that a method in the given type uses a ConditionalAttribute, and the value for this condition is false. GetMethods will still include this method, but I would like to ignore it.

Here is a simple example of what I am attempting. This program is ran in Debug mode, so I want to find a way where only foo() and fooDebug() are called while fooBar() is ignored.

using System;
using System.Diagnostics;
using System.Reflection;

namespace ConsoleApplication
{
    class ClassA
    {
        public static void foo()
        {
            Console.WriteLine("foo");
        }

        [Conditional("DEBUG")]
        public static void fooDebug()
        {
            Console.WriteLine("fooDebug");
        }

        [Conditional("BAR")]
        public static void fooBar()
        {
            Console.WriteLine("fooBar");
        }
    }

    class Program
    {
    //In this example, I want to find a way where only foo() and fooDebug() are called and fooBar() is ignored, when reflected.
        static void Main(string[] args)
        {
            //Call methods directly.
            //Methods are called/ignored as expected.
            ClassA.foo();//not ignored
            ClassA.fooDebug();//not ignored
            ClassA.fooBar();//ignored

            //Call methods with reflection
            MethodInfo[] methods = typeof(ClassA).GetMethods(BindingFlags.Static | BindingFlags.Public);
            foreach (MethodInfo method in methods)
            {
                //All methods are called, regardless of the ConditionalAttribute.
                method.Invoke(null, null);

                //I figured there would be some workaround like this:
                ConditionalAttribute conditional = 
                Attribute.GetCustomAttribute(method, typeof(ConditionalAttribute)) as ConditionalAttribute;

                if (conditional == null)
                {
                    //The method calls if it has no ConditionalAttribute
                    method.Invoke(null, null);
                }
                else
                {
                    //I can get the string of the condition; but I have no idea how, at runtime, to check if it's defined.
                    string conditionString = conditional.ConditionString;

                    //I also cannnot do a hardcoded (conditionString == "BAR") because the library would not know about BAR
                    bool conditionIsTrue = true;
                    //conditionIsTrue = ??

                    //If the method has a ConditionalAttribute, only call it if the condition is true
                    if (conditionIsTrue)
                    {
                        method.Invoke(null, null);
                    }
                }
            }
        }
    }
}

Ultimately, I would like to know which methods do not include false ConditionalAttributes.

EDIT

This idea is for a library that others would use, so assume that ClassA is a type that they define and pass into my library.

Rubixus
  • 757
  • 4
  • 11
  • You could get the method body via reflection and check if it's empty. – Tobias Brandt Jul 29 '13 at 13:32
  • @TobiasBrandt: A false ConditionalAttribute does not make the method empty. The method is fully compiled, but direct calls to it are ignored. – Rubixus Jul 29 '13 at 13:57
  • Ignored in what sense? Do the methods contain some kind of marker to tell the runtime not to execute them? – Tobias Brandt Jul 29 '13 at 14:07
  • @TobiasBrandt A ConditionalAttribute "Indicates to compilers that a method call or attribute should be ignored unless a specified conditional compilation symbol is defined." From MSDN at http://msdn.microsoft.com/en-us/library/system.diagnostics.conditionalattribute.aspx – Rubixus Jul 29 '13 at 14:32
  • So, if a DLL has been compiled with DEBUG and has a method with attribute `Conditional("DEBUG")`, I can still call that method when linking against the DLL? – Tobias Brandt Jul 29 '13 at 14:42
  • @TobiasBrandt: The method will still be included whether or not the dll is compiled with DEBUG. But if the caller is using the dll, the method will only be called when the caller defines DEBUG. Just think of how Debug.Assert works: it is only ever called when DEBUG is defined, even though Assert is defined in another library. – Rubixus Jul 29 '13 at 14:50
  • Ok, got it. I was under the (wrong) impression that the one compiling the library get's to decide whether a method is ignored or not. – Tobias Brandt Jul 29 '13 at 14:59

1 Answers1

1

What this basically boils down to is: "can I determine, via reflection, the arbitrary conditional compilation symbols that were used to compile a given piece of code" - AFAIK, the answer to that is "no".


Note: everything under here no longer applies since the edit that makes it clear that the context here is library / caller.

If you have the luxury of being able to move the methods around (and make them non-public), then maybe:

partial class Program
{
    static partial void foo();
    static partial void fooDebug();
    static partial void fooBar();

    static partial void foo()
    {
        Console.WriteLine("foo");
    }

#if DEBUG
    static partial void fooDebug()
    {
        Console.WriteLine("fooDebug");
    }
#endif
#if BAR
    static partial void fooBar()
    {
        Console.WriteLine("fooBar");
    }
#endif
    //In this example, I want to find a way where only foo() and fooDebug() are called and fooBar() is ignored, when reflected.
    static void Main(string[] args)
    {
        //Call methods directly.
        //Methods are called/ignored as expected.
        foo();//not ignored
        fooDebug();//not ignored
        fooBar();//ignored

Now, the methods that aren't implemented do not exist after compile, so will not show in the reflection code. Note that you'd need use BindingFlags.Static | BindingFlags.NonPublic to find them now, since they are non-public:

MethodInfo[] methods = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.NonPublic);
foreach (MethodInfo method in methods)
{
    if(method.Name == "Main") continue; // yes, that is lazy
    method.Invoke(null, null);
}
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • `Rubixus` this link can help in regards to giving more information to Marc answer [MSDN C# Conditional](http://msdn.microsoft.com/en-us/library/4xssyw96%28VS.80%29.aspx) – MethodMan Jul 29 '13 at 13:25
  • I've considered this, but this idea is for a library that others would use, and I would like to limit the requirements for them to use it. So if they define a type with a ConditionalAttribute, they should assume my library will ignore it when it's false. – Rubixus Jul 29 '13 at 13:28
  • @Rubixus if it is for a library, note that you can't use `partial` anyway - that evaporates and doesn't exist in the IL. I don't think your code can determine what conditional compilation symbols are used in arbitrary code; I suspect you might want to add a **separate** attribute that they can use to indicate methods that should be included/excluded – Marc Gravell Jul 29 '13 at 13:30
  • @Rubixus with your edit, you can ignore most of this answer - I added a 2 line explanation at the top, instead – Marc Gravell Jul 29 '13 at 13:32