C# Object Initialization Like a Pro: From Inline Blocks to Explicit Assignments (Basic → Expert)
You don’t just “new up” an object in C# — you decide how expressive, safe, and readable that initialization will be. Whether you’re building a DTO from Graph API or wiring repositories, your initialization style matters.
Here’s your tiered playbook.
🟢 BASIC — “Just get me an object”
Inline initializer block (concise, declarative):
var defaultCallee = new ParticipantEndpoint
{
OdataType = "#microsoft.graph.callRecords.participantEndpoint",
UserAgent = new UserAgent
{
OdataType = "#microsoft.graph.callRecords.clientUserAgent",
HeaderValue = null,
ApplicationVersion = null,
AdditionalData =
{
["Platform"] = "unknown",
["ProductFamily"] = "unknown",
["CommunicationServiceId"] = null,
["AzureADAppId"] = null
}
},
AdditionalData = { ["Identity"] = null }
};
✅ Benefits:
- Compact, declarative.
- Great for simple DTOs, tests, mocks.
- Less boilerplate.
⚠️ Caveat:
- Harder to debug line-by-line in watch windows.
- Nested object initializers can become dense.
🟡 MEDIUM — “Step by step, please”
Explicit assignments (procedural, verbose):
var defaultCallee = new ParticipantEndpoint();
defaultCallee.OdataType = "#microsoft.graph.callRecords.participantEndpoint";
defaultCallee.UserAgent = new UserAgent();
defaultCallee.UserAgent.OdataType = "#microsoft.graph.callRecords.clientUserAgent";
defaultCallee.UserAgent.HeaderValue = null;
defaultCallee.UserAgent.ApplicationVersion = null;
defaultCallee.UserAgent.AdditionalData["Platform"] = "unknown";
defaultCallee.UserAgent.AdditionalData["ProductFamily"] = "unknown";
defaultCallee.UserAgent.AdditionalData["CommunicationServiceId"] = null;
defaultCallee.UserAgent.AdditionalData["AzureADAppId"] = null;
defaultCallee.AdditionalData["Identity"] = null;
✅ Benefits:
- Debug-friendly: you can set breakpoints after each assignment.
- Easier when conditional logic is needed.
- Works well in step-through tutorials or when building incrementally.
⚠️ Caveat:
- Verbose and repetitive.
- Can hide intent under “noise.”
🔵 ADVANCED — “Design for maintainability”
When to choose one over the other?
- Inline initializer: Use when the object is a “data bag” (DTO, record, config snapshot). Perfect for declarative intent.
- Explicit assignment: Use when initialization involves conditions, loops, validations, or services. Perfect for imperative workflows.
Example (mixed):
var endpoint = new ParticipantEndpoint
{
OdataType = "#microsoft.graph.callRecords.participantEndpoint",
UserAgent = new UserAgent
{
OdataType = "#microsoft.graph.callRecords.clientUserAgent"
}
};
// Add conditionals later
if (isUnknownPlatform)
endpoint.UserAgent.AdditionalData["Platform"] = "unknown";
if (!string.IsNullOrEmpty(version))
endpoint.UserAgent.ApplicationVersion = version;
🟣 EXPERT — “Object initialization is OOP design”
It’s not just syntax sugar — it’s how your model communicates intent:
-
Immutability: prefer
record
types with init-only setters when data must not change after construction. - Encapsulation: hide complex initialization in factory methods or builders.
- Performance: fewer allocations when using initializer blocks vs. multiple statements.
- Readability: initializer blocks group related data; assignments spread across code.
👉 Example with a factory:
public static ParticipantEndpoint CreateDefault(string? version = null)
=> new()
{
OdataType = "#microsoft.graph.callRecords.participantEndpoint",
UserAgent = new UserAgent
{
OdataType = "#microsoft.graph.callRecords.clientUserAgent",
ApplicationVersion = version,
AdditionalData =
{
["Platform"] = "unknown",
["ProductFamily"] = "unknown"
}
},
AdditionalData = { ["Identity"] = null }
};
Now your construction logic is reusable, testable, and intention-revealing.
⚡ Cheat Sheet
Goal | Use | Example |
---|---|---|
Quick, concise DTO | Inline initializer | new Foo { A=1, B=2 } |
Debug / conditional setup | Explicit assignments | foo.A=1; if(x) foo.B=2; |
Enforce invariants | Factory or Builder | Foo.CreateDefault() |
Immutability |
record with init
|
public record Foo { int A { get; init; } } |
🧠 Mental Model
Think of object initialization in C# as levels of intent:
- Declare data shape → inline initializer.
- Assemble step by step → explicit assignment.
- Encapsulate complexity → factory/builder patterns.
Pick the tool that matches your debuggability, readability, and design goals.
✍ Written by: Cristian Sifuentes – Full-stack dev crafting scalable apps with [.NET - Azure], [Angular - React], Git, SQL & AI integrations. Dark mode, clean code, and atomic commits enthusiast.
✨ Final Thought:
Initializer blocks and assignments aren’t competitors — they’re complementary. Use inline blocks when declaring state; use assignments when narrating logic. The expert move? Wrap them both in clean abstractions.
Top comments (0)