2

I have some logic which is like this:

#define MYVAR
...
#if MYVAR
[Attribute1]
#else
[Attribute2]
#endif

I want to be able to switch the attributes applied to a method based on a environment variable Like if the environment variable MYVAR is set, then actually define the symbol 'MYVAR' and vice-versa.

TBH I do not know if it is possible. If not any ideas how to overcome this? I can ofcourse add this in the method itself - the env. variable check, but I need the attributes because of reflection being executed. I can create a custom MyAttrbute(int mode) but I cannot add a logic in its constructor to extract an env. variable, because it must be a constant expression.

Possible solutions:

  • Like the first comment suggests, I can read the env. variable in the custom attribute. Still there will be some nasty reflection left to do.

  • Because I am working on Linux with dotnet commands, I can actually bundle my build command with pre-step sed command and add a dummy placeholder {{define_myvar}} to the top of the .cs file and replace it with either empty string or the full string based on env. variables. No reflection here, but not very elegant.

antanta
  • 618
  • 8
  • 16
  • You can read the environment variable inside the attribute. – Guru Stron Dec 13 '21 at 15:18
  • Ok, this is an option and add a public property to the custom attribute and do some nasty reflection. Still question remains if there is an easier way. – antanta Dec 13 '21 at 15:33
  • 3
    You can pass the environment variable to the compiler using your build script, but the identifier in your C# code will not go looking for an environment variable for you. Using code in the attribute is also wrong, as that will be evaluated at runtime, whereas `#if xxx` will be evaluated at compile time. – Lasse V. Karlsen Dec 13 '21 at 15:42
  • 2
    TBH not following how reflection and so on is involved. Can you explain your use case more, cause ATM it seems to be more x-y problem. – Guru Stron Dec 13 '21 at 15:43
  • You could use a T4 template maybe, to define a global symbol – Charlieface Dec 13 '21 at 21:37

1 Answers1

4

I was able to find a nice solution. It was the comment by Lasse V. Karlsen which gave me the idea and little bit more searching in this area.

C# code:

        public static void Main(string[] args)
        {
#if MYVAR
            Log.Information("MYVAR defined!");
#else
            Log.Information("MYVAR not defined!");
#endif
        }

Project file:

<PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <DefineConstants>$(MyVar)</DefineConstants>
</PropertyGroup>

Commands:

dotnet build MyProject.csproj -p:MyVar=$MYVAR -c Debug
dotnet run --project=MyProject.csproj -c Debug --no-build

These commands also work (docker ready):

dotnet publish MyProject.csproj -c Release -p:MyVar=$MYVAR
dotnet MyProject.dll

Explanation:

If the env. variable $MYVAR is set to MYVAR just like in the C# code, the dotnet build will replace the csproj file with <DefineConstants>MYVAR</DefineConstants> and the preprocessor will evaluate #if MYVAR to true.

If environment variable $MYVAR is not set (aka empty string) or set to any other different value from MYVAR, the preprocessor will evaluate the condition to false.

IMO this is a very nice tweak which is suitable for different scenarios. Thanks for the guidance, guys. Cheers.

Note:

Like stated in the question I do not care about the value of the environment variable, only if it is defined or not, i.e. it is either empty string or not. Also the c# code is just PoC so it was easier to use logging instead of writing the attributes and the refraction.

antanta
  • 618
  • 8
  • 16