[Conditional("DEBUG")] not works

// ReSharper disable ConvertToAutoPropertyWhenPossible
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using UnityEngine;
using Object = UnityEngine.Object;

[assembly: InternalsVisibleTo("iCare.VisitorDetection.Tests")]

namespace iCare.VisitorDetection
{
    public sealed class VisitorDetector<TVisitor> : IVisitorDetector
    {
        readonly bool _cacheVisitors;


        readonly ConditionData _conditionData;
        readonly VisitorFindStrategy _visitorFinder;

        internal VisitorDetector(VisitorFindStrategy visitorFindStrategy, bool cacheVisitors,
            ConditionData conditionData)
        {
            _visitorFinder = visitorFindStrategy;
            _conditionData = conditionData;
            _cacheVisitors = cacheVisitors;
        }

        public List<TVisitor> Visitors { get; } = new();
        public bool HasVisitors => Visitors.Count > 0;

#if DEBUG
        internal bool LogsActive { get; set; }
#endif

        public event Action<TVisitor> OnVisitorDetectedEvent;
        public event Action<TVisitor> OnVisitorLeftEvent;


        void AddVisitor(Collider other, TVisitor visitor)
        {
            if (Visitors.Contains(visitor))
                throw new InvalidOperationException("Visitor already exists in the list");

            OnVisitorDetectedEvent?.Invoke(visitor);

            if (_cacheVisitors)
            {
                Visitors.Add(visitor);
                Log($"{visitor.SetBold()} added to CacheList", visitor as Object);
            }

            Log($"{other.name.Highlight()} successfully detected".SetColor(Color.green), other);
        }

        void RemoveVisitor(Collider other, TVisitor visitor)
        {
#if DEBUG
            var otherName = other.name.Highlight();
#endif
            if (_cacheVisitors && Visitors.Contains(visitor))
            {
                Visitors.Remove(visitor);
                Log($"{visitor.SetBold()} removed from CacheList", visitor as Object);
            }

            OnVisitorLeftEvent?.Invoke(visitor);
            Log($"{otherName} successfully left".SetColor(Color.green), other);
        }

        internal sealed class Condition<T>
        {
            readonly Func<T, bool> _condition;

#if DEBUG
            internal readonly string FailureMessage;
#endif

            internal Condition(Func<T, bool> condition, string failureMessage)
            {
                _condition = condition;
                FailureMessage = failureMessage;
            }

            internal bool IsMet(T visitor)
            {
                return _condition(visitor);
            }
        }

        internal sealed class ConditionData
        {
            internal readonly List<Condition<VisitorDetector<TVisitor>>> DetectorConditions;
            internal readonly List<Condition<Collider>> Preconditions;
            internal readonly List<Condition<TVisitor>> VisitorConditions;

            internal ConditionData(List<Condition<VisitorDetector<TVisitor>>> detectorConditions,
                List<Condition<Collider>> preconditions,
                List<Condition<TVisitor>> visitorConditions)
            {
                DetectorConditions = detectorConditions;
                Preconditions = preconditions;
                VisitorConditions = visitorConditions;
            }

            public void AddVisitorCondition(Func<TVisitor, bool> visitorCondition, string errorMessage = null)
            {
                VisitorConditions.Add(new Condition<TVisitor>(visitorCondition, errorMessage));
            }

            public void AddDetectorCondition(Func<VisitorDetector<TVisitor>, bool> detectorCondition,
                string errorMessage = null)
            {
                DetectorConditions.Add(new Condition<VisitorDetector<TVisitor>>(detectorCondition, errorMessage));
            }

            public void AddPrecondition(Func<Collider, bool> precondition, string errorMessage = null)
            {
                Preconditions.Add(new Condition<Collider>(precondition, errorMessage));
            }
        }

        #region OnTrigger

        void IVisitorDetector.OnTriggerEnter(Collider other)
        {
            if (ConditionsAreMet(other, out var visitor)) return;
            AddVisitor(other, visitor);
        }

        void IVisitorDetector.OnTriggerExit(Collider other)
        {
            if (!_visitorFinder.TryGetVisitor<TVisitor>(other, out var visitor))
            {
                Log($"{other.name} does not have {typeof(TVisitor).Name.Highlight()}".SetColor(Color.yellow), other);
                return;
            }

            RemoveVisitor(other, visitor);
        }

        #endregion

        #region Conditions

        bool PreConditionsMet(Collider other)
        {
            foreach (var precondition in _conditionData.Preconditions)
            {
                if (precondition.IsMet(other)) continue;
                Log(precondition.FailureMessage, other);
                return false;
            }

            return true;
        }

        bool VisitorConditionsMet(TVisitor visitor)
        {
            foreach (var visitorCondition in _conditionData.VisitorConditions)
            {
                if (visitorCondition.IsMet(visitor)) continue;
                Log(visitorCondition.FailureMessage, visitor as Object);
                return false;
            }

            return true;
        }

        bool DetectorConditionsMet()
        {
            foreach (var detectorCondition in _conditionData.DetectorConditions)
            {
                if (detectorCondition.IsMet(this)) continue;
                Log(detectorCondition.FailureMessage, null);
                return false;
            }

            return true;
        }

        bool ConditionsAreMet(Collider other, out TVisitor visitor)
        {
#if DEBUG
            var otherName = other.name.Highlight();
#endif
            visitor = default;

            if (!PreConditionsMet(other))
            {
                Log($"{otherName} does not meet preconditions".SetColor(Color.yellow), other);
                return true;
            }

            if (!DetectorConditionsMet())
            {
                Log($"{otherName} does not meet detector conditions".SetColor(Color.yellow), other);
                return true;
            }

            if (!_visitorFinder.TryGetVisitor(other, out visitor))
            {
                Log($"{otherName} does not have {typeof(TVisitor).Name.Highlight()}".SetColor(Color.yellow), other);
                return true;
            }

            if (!VisitorConditionsMet(visitor))
            {
                Log($"{otherName} does not meet visitor conditions".SetColor(Color.yellow), other);
                return true;
            }

            return false;
        }

        #endregion

        // ReSharper disable Unity.PerformanceAnalysis
        [Conditional("DEBUG")]
        void Log([CanBeNull] object message, Object context)
        {
            //Null check for failure messages
            if (LogsActive && message != null)
                UnityEngine.Debug.Log(message, context);
        }
    }
}

{C9F7A223-7D5D-44CC-9B98-64A5A1E8E1A4}

{4E0C6CDD-8D46-45E8-BC41-A4FB20410F65}

                Log($"{otherName} does not meet preconditions".SetColor(Color.yellow), other);

this log calls should be removed from build as i know, but it does not so im having errors because of that. Isnt conditional attribute for this, so i can safely remove method calls without extra #if debug on every line

Be carefull, the [ConditionalAttribute] removes calls to methods, not the method itself. The method still exists and is compiled, but calls to the method are removed

1 Like

but this

Log($"{otherName} does not meet preconditions".SetColor(Color.yellow), other);

calls should be removed or they will stay there as empty?

Every line that calls the method, is removed. The method itself, is not

So yes, those lines would be removed

1 Like

Okey but this builds succesfuly

            if (!VisitorConditionsMet(visitor))
            {
                Log($"{other.name} does not meet visitor conditions".SetColor(Color.yellow), other);
                return true;
            }...rest of code


        // ReSharper disable Unity.PerformanceAnalysis
        [Conditional("DEBUG")]
        void Log([CanBeNull] object message, Object context)
        {
#if DEBUG
            //Null check for failure messages
            if (Debug && message != null)
                UnityEngine.Debug.Log(message, context);
#endif
         }

but this does not build it fails
{CA70F779-7855-41CA-B621-C30CED60651B}

#if DEBUG
            var otherName = other.name;
#endif
            
            if (!VisitorConditionsMet(visitor))
            {
                Log($"{otherName} does not meet visitor conditions".SetColor(Color.yellow), other);
                return true;
            }...rest of code
         }

-otherName variable was deleted because it was not in debug mode
-The log call was deleted because of the conditional attribute, so it will not actually use otherName because it was deleted.

With this logic, the code should work but it doesn’t

This might be, because even if the call is removed, the arguments might still get “checked” for correctness by the compiler.
So one of your arguments (otherName) does not exist (the compiler sees a variable that is not declared, because it was stripped by the #if DEBUG #endif . So it throws

from the docs ( ConditionalAttribute Class (System.Diagnostics) | Microsoft Learn)

Applying ConditionalAttribute to a method indicates to compilers that a call to the method should not be compiled into Microsoft intermediate language (MSIL) unless the conditional compilation symbol that is associated with ConditionalAttribute is defined. You will get a compilation error in Visual Studio if you apply this attribute to a method that does not return void. Applying ConditionalAttribute to an attribute indicates that the attribute should not be emitted to metadata unless the conditional compilation symbol is defined. Any arguments passed to the method or attribute are still type-checked by the compiler.

So im guessing you might want to remove the variable otherName and just directly inline other.name inside the call

1 Like

Ah now I understand thank you very much

1 Like