DEV Community

Cristian Sifuentes
Cristian Sifuentes

Posted on

C# Object Initialization Like a Pro: From Inline Blocks to Explicit Assignments (Basic Expert)

C# Object Initialization Like a Pro: From Inline Blocks to Explicit Assignments (Basic → Expert)

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 } }; 
Enter fullscreen mode Exit fullscreen mode

✅ 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; 
Enter fullscreen mode Exit fullscreen mode

✅ 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; 
Enter fullscreen mode Exit fullscreen mode

🟣 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 } }; 
Enter fullscreen mode Exit fullscreen mode

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:

  1. Declare data shape → inline initializer.
  2. Assemble step by step → explicit assignment.
  3. 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)