I am attempting to use Pre-compiler directives to toggle certain features in my application. I am using Pre-compiler directives as opposed to const static variables because these features will not occur in the release version of the application.
Is it true that C# does not allow for all C Pre-compiler commands, ie, #if X > Y & etc.? My below code is throwing compiler errors. Is it possible to use Pre-compiler directives to toggle functionality in my application?
My solution is a very 'C++/C' way of achieving this functionality. What is the C# way of achieving this functionality?
#define DEBUG 1 // Not allowed to assign values to constants?
#define USE_ALTERNATE_METHOD 0
public class MyApplication
{
public MyApplication()
{
#if DBEUG > 0 // Not allowed these kinds of directives?
RegressionTests.Run();
#endif // DEBUG > 0
}
public void myMethod
{
#if USE_ALTERNATE_METHOD > 0
// Do alternate stuff
#else
// Do regular stuff
#endif // USE_ALTERNATE_METHOD > 0
}
}
Specifying just #if DEBUG does the work.
You cannot use pre-processor definitives like #define DEBUG 1 .
However you can just specify #define DEBUG or #define CustomDefinition and use it with the Conditional attribute.
Eg,
You can just do this:
#if DEBUG
Console.WriteLine("This will work in DEBUG mode alone");
#endif
Or you can specify conditional-attributes on top of the method that you wanna execute only in the debug mode.
Eg,
[Conditional("DEBUG")]
void ExecuteOnlyInDebugMode()
{
// do stuff you wanna do.
}
For your example it has to be like this:
#define DEBUG
#define USE_ALTERNATE_METHOD
public class MyApplication
{
public MyApplication()
{
#if DEBUG
RegressionTests.Run();
#endif
}
public void myMethod
{
#if USE_ALTERNATE_METHOD
// Do alternate stuff
//do not execute the following. just return.
#endif
// Do regular stuff
}
}
You can find more info here . Beautifully explained.
Also, read more that Conditional attribute, here.
Related
Is it possible to "gray out" the code if it doesn't exist like we do in Platform Dependent compilation (ex: #If UNITY_EDITOR)?
For example I don't want the compiler to complain if the code doesn't exist in the project. Specifically, I want to "hide" "GoogleMobileAds.Api;" package, which I don't have in the project, but it may be in the future.
Preprocessors are not an invention of Unity but is a c# thing.
If there are no specific defines listed in the Platform dependent compilation first make sure that your packages don't bring their own custom defines. Photon PUN e.g. actually does bring own defines like PHOTON_UNITY_NETWORKING, PUN_2_OR_NEWER, etc. But that's totally up to the providers of such libraries.
You can see/check this in the Edit -> Project Settings -> Player Settings -> Other Settings -> Scripting Define Symbols
Scripting Define Symbols
Set custom compilation flags. For more details, see the documentation on Platform dependent compilation.
Then you can just invent your own ones and add them to your code like e.g. USE_GOOGLE, USE_FIREBASE, etc
#if USE_GOOGLE
// some Google API related stuff
#endif
and later once you actually have according package in your project add these defines to the Scripting Define Symbols mentioned above.
If you know the full qualified assembly name of one type contained in the optional package you are looking for you can use e.g.
[assembly: OptionalDependency("Namespace.SubNameSpace.TypeName", "YOUR_DEFINE_SYMBOL_HERE")]
which is well hidden in Unity.XRTools.Utils!
This will internally on compile time check if the type Namespace.SubNameSpace.TypeName exists and if so define YOUR_DEFINE_SYMBOL_HERE so you can again in your code wrap the optional stuff in
#if YOUR_DEFINE_SYMBOL_HERE
...
#else
...
#endif
Alternative
For packages you own/implemen yourself there is a way to do this kind of automatically I used in the past. The following script as soon as it exists in a project it automatically adds the given define to the PlayerSettings if it doesn't exists yet
#if !USE_GOOGLE
public static class UseGoogleDefineSetter
{
const string DEFINE = "USE_GOOGLE";
[InitializeOnLoadMethod]
private void Init()
{
// Get current defines
var currentDefinesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
// Split at ;
var defines = currentDefinesString.Split(';').ToList();
// check if defines already exist given define
if (!defines.Contains(DEFINE))
{
// if not add it at the end with a leading ; separator
currentDefinesString += $";{DEFINE}";
// write the new defines back to the PlayerSettings
// This will cause a recompilation of your scripts
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, currentDefinesString);
}
}
}
#endif
Update Unity 2020+
The method was semi replaced by a better one operating on a list/array instead of an entire string which is expensive. And in the newest version also the build pipeline slightly changed.
So in newer Unity versions I would rather use something like e.g. (assuming 2020 is the oldest version you want to support/use)
#if !USE_GOOGLE
public static class UseGoogleDefineSetter
{
const string DEFINE = "USE_GOOGLE";
[InitializeOnLoadMethod]
private void Init()
{
EditorUtils.AddScriptingSymbol(DEFINE);
}
}
#endif
and to make it easy and general
public static class EditorUtils
{
#if UNITY_2021_2_OR_NEWER
private static NamedBuildTarget GetActiveNamedBuildTarget()
{
var buildTargetGroup = GetActiveBuildTargetGroup();
var namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup);
return namedBuildTarget;
}
#endif
private static BuildTargetGroup GetActiveBuildTargetGroup()
{
var buildTarget = EditorUserBuildSettings.activeBuildTarget;
var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(buildTarget);
return buildTargetGroup;
}
public static void AddScriptingSymbol(string define)
{
#if UNITY_2021_2_OR_NEWER
var namedBuildTarget = GetActiveNamedBuildTarget();
PlayerSettings.GetScriptingDefineSymbols(namedBuildTarget, out var defines);
#else
var buildTargetGroup = GetActiveBuildTargetGroup();
PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup, out var defines);
#endif
var defineList = defines.ToList();
if (!defineList.Contains(define))
{
defineList.Add(define);
}
#if UNITY_2021_2_OR_NEWER
PlayerSettings.SetScriptingDefineSymbols(namedBuildTarget, defineList.ToArray());
#else
PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, defineList.ToArray());
#endif
}
}
Both scripts go of course either in a folder called Editor and/or in an assembly only compiled for the Unity Editor or need to be wrapped additionally in #if UNITY_EDITOR
I have a C++ code which I need to rewrite to C# and looks like this:
class dppServerError: public dppBaseError
{
public :
dppServerError(DWORD ActionCode, const TCHAR* Desciption)
#ifdef POSTER_VER
: dppBaseError(Desciption)
#else
: dppBaseError(TEXT("Server text response: \"%s\""), Desciption)
#endif
, m_AC(ActionCode), m_ErrorCode(dppERR_SERVER)
{
};
Problem is I am not using #defines in my C# code and instead using public const Enums. Now, how can I duplicate above code in C#? the #ifdefs part?
Can't I normally initialize member variables of base class in the body of the constructor of derived class? (without : syntax). Then I could do (in C#):
dppServerError(uint ActionCode, string Desciption)
{
// Initialize base class member
if(Globals.ConfigEnum == POSTER_VER)
dppBaseError = Desciption; // Can I initialize this base class ivar like this? without : syntax?
else
dppBaseError = "Smth else" + Desciption;
// These are just ivars from This class
m_AC = ActionCode;
m_ErrorCode = dppERR_SERVER;
};
PS. Someone told me this about #defines in C#
"Be aware though: there is no guarantee that the conditional
compilation symbol is the same for all projects in your solution. This
will hinder reuse of your DLLs by other solutions that want different
conditional compilation symbols."
And I decided to move to enums because I didn't really get what this meant. I am a bit new to .NET.
To get the same c++ behaviour in c#, use this:
#if POSTER_VER
dppBaseError = Desciption;
#else
dppBaseError = "Smth else" + Desciption;
#endif
or also:
dppServerError(uint ActionCode, string Desciption)
#if POSTER_VER
:base(Desciption)
#else
:base("Smth else" + Desciption)
#endif
Use a #define POSTER_VER directive, or better, define the symbol in project properties -> build -> Conditional compilation symbols.
Usually a source file is included only in one project (unless you use "add as link " in visual studio to add same file to two or more projects), so the remarks "be aware" does not apply. if it does, use the same care you would use for c++ code.
In you c# code , the variable Global.ConfigEnum is evaulated at runtime, in my c# code, as in your c++, the symbol POSTER_VER is checked at complile time, resulting in different compiled binary files.
see #if, #define and ProjectProperties on MSDN
If dppBaseError is a field, you can initialize it as you have shown in your code.
If it's a base class constructor, you could do this:
dppServerError(uint ActionCode, string Desciption)
: base( (Globals.ConfigEnum == POSTER_VER) ? Desciption : "Smth else" + Desciption)
{
...
I need the following logic
#if (DEV || QA || RELEASE)
//add when dev or qa or release configuration
#endif
Is it possible in c#?
Yes. Quoting the #if documentation on MSDN:
You can use the operators && (and), || (or), and ! (not) to evaluate whether multiple symbols have been defined. You can also group symbols and operators with parentheses.
#define DEBUG
#define MYTEST
using System;
public class MyClass
{
static void Main()
{
#if (DEBUG && !MYTEST)
Console.WriteLine("DEBUG is defined");
#elif (!DEBUG && MYTEST)
Console.WriteLine("MYTEST is defined");
#elif (DEBUG && MYTEST)
Console.WriteLine("DEBUG and MYTEST are defined");
#else
Console.WriteLine("DEBUG and MYTEST are not defined");
#endif
}
}
Here simple code how to do it. You can read full documentation on C# Preprocessor Directives
Yes. These are called "preprocessor directives" or compiler directives.
This question already has answers here:
C# !Conditional attribute?
(8 answers)
Closed 8 years ago.
What is the C# System.Diagnostics.Conditional equivalent of #if (!DEBUG)?
I want to encrypt a section of the app.config file of a console application if it has not been compiled in DEBUG mode. This is achieved like so:
public static void Main(string[] args)
{
#if (!DEBUG)
ConfigEncryption.EncryptAppSettings();
#endif
//...
}
but somehow, I prefer decorating the encrypt method with a conditional attribute:
[Conditional("!DEBUG")]
internal static void EncryptAppSettings()
{
//...
}
however this makes the compiler sad: The argument to the 'System.Diagnostics.ConditionalAttribute' attribute must be a valid identifier...
What is the correct syntax for negating the Conditional argument?
EDIT:
Thanks to #Gusdor, I used this (I preferred to keep the Program.cs file free of if/else debug logic):
#if !DEBUG
#define ENCRYPT_CONFIG
#endif
[Conditional("ENCRYPT_CONFIG")]
internal static void EncryptAppSettings()
{
//...
}
Using the attribute will be a bit of a hack but it can be done.
#if DEBUG
//you have nothing to do here but c# requires it
#else
#define NOT_DEBUG //define a symbol specifying a non debug environment
#endif
[Conditional("NOT_DEBUG")]
internal static void EncryptAppSettings()
{
//...
}
#if DEBUG
// do nothing
#else
//your code here
#endif
I'm using VS2010/2012 and I was wondering if there is a way (perhaps using reflection) to see how an assembly is build.
When I run in Debug, I use the #if DEBUG to write debug information out to the console.
However, when you end up with a bunch of assemblies, is there then a way to see how they where build? Getting the version number is easy, but I haven't been able to find out how to check the build type.
There are 3 ways:
private bool IsAssemblyDebugBuild(string filepath)
{
return IsAssemblyDebugBuild(Assembly.LoadFile(Path.GetFullPath(filepath)));
}
private bool IsAssemblyDebugBuild(Assembly assembly)
{
foreach (var attribute in assembly.GetCustomAttributes(false))
{
var debuggableAttribute = attribute as DebuggableAttribute;
if(debuggableAttribute != null)
{
return debuggableAttribute.IsJITTrackingEnabled;
}
}
return false;
}
Or using assemblyinfo metadata:
#if DEBUG
[assembly: AssemblyConfiguration("Debug")]
#else
[assembly: AssemblyConfiguration("Release")]
#endif
Or using a constant with #if DEBUG in code
#if DEBUG
public const bool IsDebug = true;
#else
public const bool IsDebug = false;
#endif
I prefer the second way so i can read it both by code and with windows explorer
Once they are compiled, you can't, unless you put the metadata yourself.
For example, you could use either AssemblyConfigurationAttribute or .NET 4.5's AssemblyMetadataAttribute
#if DEBUG
[assembly: AssemblyConfiguration("Debug")]
#else
[assembly: AssemblyConfiguration("Release")]
#endif
or
#if DEBUG
[assembly: AssemblyMetadata("DefinedVariable", "DEBUG")]
#endif