Generator: Create Flyweight Pattern
Summary
Add a source generator that produces boilerplate-free, GoF-consistent Flyweight pattern implementations with explicit key semantics, deterministic caching, and configurable eviction.
The generator lives in PatternKit.Generators and emits self-contained C# with no runtime PatternKit dependency.
Primary goals:
- Generate flyweight caches with explicit keys and factory creation.
- Make thread-safety, eviction, and comparer behavior explicit.
- Provide deterministic policies and clear diagnostics.
Motivation / Problem
Flyweight is often re-implemented ad hoc:
- inconsistent keying/comparers
- unclear threading guarantees
- unbounded caches that leak memory
- hard-to-test eviction policies
A generator can emit a correct, consistent flyweight cache once.
Supported Targets (must-have)
The generator must support creating flyweights for:
partial class / partial struct
partial record class / partial record struct
Two usage models:
- Factory-on-type: annotate the flyweight value type; generator emits a cache type.
- Cache host: annotate a host type that declares key/value types explicitly.
Proposed User Experience
A) Annotate value type, generate cache
[Flyweight(typeof(string), CacheTypeName = "GlyphCache")]
public partial record struct Glyph(char Value, int Width);
public static partial class Glyph
{
[FlyweightFactory]
private static Glyph Create(string key) => /* ... */;
}
Generated (representative shape):
public sealed partial class GlyphCache
{
public GlyphCache(IEqualityComparer<string>? comparer = null);
public Glyph Get(string key);
public bool TryGet(string key, out Glyph value);
public void Clear();
}
B) Bounded cache
[Flyweight(typeof(string), Capacity = 10_000, Eviction = FlyweightEviction.Lru)]
public partial record struct Glyph(char Value, int Width);
Generated cache uses deterministic eviction.
Attributes / Surface Area
Namespace: PatternKit.Generators.Flyweight
Core
Enums:
FlyweightEviction: None, Lru (v1), Ttl (v2)
FlyweightThreadingPolicy: SingleThreadedFast, Locking, Concurrent
Semantics (must-have)
Keying
Creation
Threading policies
SingleThreadedFast: no locks; documented not thread-safe.
Locking: lock around dictionary operations; ensures single-create per key.
Concurrent: uses ConcurrentDictionary; creation semantics must be documented (may call factory multiple times unless guarded).
Eviction
V1:
-
None and Lru.
-
If Capacity > 0:
- enforce capacity with deterministic eviction.
Lru implementation must be O(1) amortized and deterministic.
Allocation expectations
- Executing
Get should not allocate on hits.
- Eviction bookkeeping allocates minimally and predictably.
Diagnostics (must-have)
Stable IDs, actionable:
PKFLY001 Type marked [Flyweight] must be partial.
PKFLY002 No [FlyweightFactory] method found.
PKFLY003 Multiple [FlyweightFactory] methods found.
PKFLY004 Factory method signature invalid.
PKFLY005 Cache type name conflicts with existing type.
PKFLY006 Invalid eviction configuration (e.g., Capacity=0 with Lru).
Generated Code Layout
ValueTypeName.Flyweight.g.cs
Determinism:
- stable emission order by fully-qualified symbol name.
Testing Expectations
-
Same key returns same value instance/value (as appropriate for TValue).
-
Factory invoked once per key under Locking.
-
Capacity eviction is deterministic (drop oldest / least recently used).
-
Threading policy smoke tests.
-
Diagnostics:
- missing/duplicate factory
- invalid config
Acceptance Criteria
Generator: Create Flyweight Pattern
Summary
Add a source generator that produces boilerplate-free, GoF-consistent Flyweight pattern implementations with explicit key semantics, deterministic caching, and configurable eviction.
The generator lives in
PatternKit.Generatorsand emits self-contained C# with no runtime PatternKit dependency.Primary goals:
Motivation / Problem
Flyweight is often re-implemented ad hoc:
A generator can emit a correct, consistent flyweight cache once.
Supported Targets (must-have)
The generator must support creating flyweights for:
partial class/partial structpartial record class/partial record structTwo usage models:
Proposed User Experience
A) Annotate value type, generate cache
Generated (representative shape):
B) Bounded cache
Generated cache uses deterministic eviction.
Attributes / Surface Area
Namespace:
PatternKit.Generators.FlyweightCore
[Flyweight]on value type or cache hostType KeyTypestring? CacheTypeName(default:<ValueTypeName>FlyweightCache)int Capacity(default: 0 = unbounded)FlyweightEviction Eviction(default:None)FlyweightThreadingPolicy Threading(default:Locking)bool GenerateTryGet(default: true)[FlyweightFactory]on factory methodstatic TValue Create(TKey key)orstatic ValueTask<TValue> CreateAsync(TKey key, CancellationToken ct = default)Enums:
FlyweightEviction:None,Lru(v1),Ttl(v2)FlyweightThreadingPolicy:SingleThreadedFast,Locking,ConcurrentSemantics (must-have)
Keying
Uses
IEqualityComparer<TKey>.If none provided:
EqualityComparer<TKey>.Default.For
string, optional case-insensitive mode can be added later.Creation
On miss, cache calls generated factory.
Concurrency semantics must be explicit:
Threading policies
SingleThreadedFast: no locks; documented not thread-safe.Locking: lock around dictionary operations; ensures single-create per key.Concurrent: usesConcurrentDictionary; creation semantics must be documented (may call factory multiple times unless guarded).Eviction
V1:
NoneandLru.If
Capacity > 0:Lruimplementation must be O(1) amortized and deterministic.Allocation expectations
Getshould not allocate on hits.Diagnostics (must-have)
Stable IDs, actionable:
PKFLY001Type marked[Flyweight]must bepartial.PKFLY002No[FlyweightFactory]method found.PKFLY003Multiple[FlyweightFactory]methods found.PKFLY004Factory method signature invalid.PKFLY005Cache type name conflicts with existing type.PKFLY006Invalid eviction configuration (e.g., Capacity=0 with Lru).Generated Code Layout
ValueTypeName.Flyweight.g.csDeterminism:
Testing Expectations
Same key returns same value instance/value (as appropriate for TValue).
Factory invoked once per key under Locking.
Capacity eviction is deterministic (drop oldest / least recently used).
Threading policy smoke tests.
Diagnostics:
Acceptance Criteria
[Flyweight]works for class/struct/record class/record struct.Get+ optionalTryGet.