A common requirement when faking models is composing one property’s value from another — for example, building aDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/atulin/forged/llms.txt
Use this file to discover all available pages before exploring further.
FullName field that is the concatenation of an already-generated FirstName and LastName. In a plain C# object initializer this is straightforward, but inside Forged’s lambda-based property configuration each lambda receives its own Forge instance (f), and the lambda for FullName has no way to read what was produced when the FirstName lambda ran. Forged solves this problem with two cooperating tools: Memo() and f.Basic.Func().
The Problem with Object Initializer Lambdas
In a standard C# object initializer you cannot useout var to capture a value:
{ }.
The Memo Pattern
Forged’s solution is a two-part mechanism:-
generator.Memo(out MemoValueGenerator<T> variable)— Wraps the generator in aMemoGenerator<T>that, every time.Generate()is called on the property, stores the produced value in_currentValue. It also assignsvariableto aMemoValueGenerator<T>that reads from that cache. -
MemoValueGenerator<T>.Generate()— Returns the last value captured by the pairedMemoGenerator<T>. Because Forged evaluates properties top-to-bottom during a singleGet()call, by the time a downstream property’s lambda runs, the upstream memo has already cached its value.
Step-by-Step Usage
Declare the memo variables before the faker
Because
out var does not work inside object initializers, declare the MemoValueGenerator<T> variables in the enclosing scope and use null! to satisfy the compiler’s definite-assignment rules:Attach .Memo() to the source properties
Chain
.Memo(out first) at the end of the generator pipeline for any property whose value you want to reuse. The method returns the same MemoGenerator<T>, so it is transparent to the property assignment:Consume the memoized values with f.Basic.Func()
Use The implicit conversion from
f.Basic.Func(() => ...) to wrap a lambda that reads from the MemoValueGenerator<T> variables. The lambda is executed fresh on every Get() call, at which point the upstream properties have already run and their memos are populated:Generator<T> to T (via operator T) means $"{first}" automatically calls first.Generate(), returning the cached string.Complete Example
The following example is drawn directly from the Forged demo project:How f.Basic.Func() Works
ForgeBasic.Func<T>(Func<T> func) creates a FuncGenerator<T> that invokes the supplied lambda on every .Generate() call. This means:
- The lambda captures
firstandlastby reference (they are local variables). - On each
Get()invocation Forged runs property generators top-to-bottom:FirstName→LastName→FullName. - By the time
FullName’s lambda executes,firstandlastalready hold the values produced in the current cycle.
Forged evaluates property generators in the order they appear in the object initializer — top to bottom. Always place
.Memo() source properties above any property that consumes their MemoValueGenerator<T>. If you place FullName before FirstName in the initializer, first.Generate() will return the cached value from the previous Get() call (or the type default on the very first call).Multi-Level Composition
You can chain memos across more than two properties. EachMemoValueGenerator<T> is just a generator itself, so it can participate in further pipelines: