@@ -24,6 +24,10 @@ public sealed class PostHogClient : IPostHogClient
2424 readonly MemoryCache _featureFlagCalledEventCache ;
2525 static readonly ApiResult NoOpApiResult = new ( 0 ) ;
2626 static readonly IReadOnlyDictionary < string , FeatureFlag > EmptyFeatureFlags = new Dictionary < string , FeatureFlag > ( 0 ) ;
27+ static readonly Func < FeatureFlag , object > FeatureFlagResponseSelector = flag => flag . ToResponseObject ( ) ;
28+ static readonly Func < FeatureFlag , bool > FeatureFlagEnabledSelector = flag => ( bool ) flag ;
29+ static readonly Func < EvaluatedFlagRecord , object > EvaluatedFlagResponseSelector = record => record . Flag . ToResponseObject ( ) ;
30+ static readonly Func < EvaluatedFlagRecord , bool > EvaluatedFlagEnabledSelector = record => record . Enabled ;
2731
2832 // Strict allowlist for minimal $feature_flag_called events, shared across PostHog SDKs.
2933 // Everything else — including super properties, context properties, the $feature/<key>
@@ -581,28 +585,34 @@ async Task<CapturedEvent> AddLocalFeatureFlagDataAsync(
581585 static CapturedEvent AddFeatureFlagsToCapturedEvent (
582586 CapturedEvent capturedEvent ,
583587 IReadOnlyDictionary < string , FeatureFlag > flags )
584- {
585- capturedEvent . Properties . Merge ( flags . ToDictionary (
586- f => $ "$feature/{ f . Key } ",
587- f => f . Value . ToResponseObject ( ) ) ) ;
588- capturedEvent . Properties [ "$active_feature_flags" ] = flags
589- . Where ( f => ( bool ) f . Value )
590- . Select ( kvp => kvp . Key )
591- . ToArray ( ) ;
592- return capturedEvent ;
593- }
588+ => AddFeatureFlagsToCapturedEvent (
589+ capturedEvent ,
590+ flags ,
591+ FeatureFlagResponseSelector ,
592+ FeatureFlagEnabledSelector ) ;
594593
595594 static CapturedEvent AddFeatureFlagsToCapturedEvent (
596595 CapturedEvent capturedEvent ,
597596 FeatureFlagEvaluations flags )
597+ => AddFeatureFlagsToCapturedEvent (
598+ capturedEvent ,
599+ flags . Records ,
600+ EvaluatedFlagResponseSelector ,
601+ EvaluatedFlagEnabledSelector ) ;
602+
603+ static CapturedEvent AddFeatureFlagsToCapturedEvent < TFlag > (
604+ CapturedEvent capturedEvent ,
605+ IReadOnlyDictionary < string , TFlag > flags ,
606+ Func < TFlag , object > responseSelector ,
607+ Func < TFlag , bool > enabledSelector )
598608 {
599609 // Single-pass: per-flag $feature/<key> property + $active_feature_flags collection in one
600- // enumeration of the records dictionary. Runs per captured event, so worth keeping tight .
601- var active = new List < string > ( flags . Records . Count ) ;
602- foreach ( var ( key , record ) in flags . Records )
610+ // enumeration. The selectors are cached statically so this hot path does not allocate delegates .
611+ var active = new List < string > ( flags . Count ) ;
612+ foreach ( var ( key , flag ) in flags )
603613 {
604- capturedEvent . Properties [ $ "$feature/{ key } "] = record . Flag . ToResponseObject ( ) ;
605- if ( record . Enabled )
614+ capturedEvent . Properties [ $ "$feature/{ key } "] = responseSelector ( flag ) ;
615+ if ( enabledSelector ( flag ) )
606616 {
607617 active . Add ( key ) ;
608618 }
0 commit comments