Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

An anonymous type is a compiler-generated, immutable reference type that encapsulates a set of read-only properties into a single object without requiring an explicit class declaration. The compiler infers the type based on the object initializer and assigns it an internal, unutterable name at compile time. Because the exact type name is inaccessible in source code, you must use the implicitly typed local variable keyword (var) to hold the reference if you want to maintain strong typing and access the properties directly at compile time. Assigning the instance to an object variable forfeits direct property resolution. Assigning it to a dynamic variable forfeits compile-time type safety, though it defers property resolution to runtime.
var developer = new { Name = "Alice", ExperienceYears = 5, IsActive = true };

Compiler Behavior and Mechanics

When the C# compiler encounters an anonymous type declaration, it generates a sealed internal class deriving directly from System.Object. To optimize type generation, the compiler creates a generic class based on the property names and their order. The property types are passed as generic type arguments. For the example above, the compiler generates a structure conceptually similar to this valid C# representation:
[CompilerGenerated]
internal sealed class AnonymousType<TName, TExperienceYears, TIsActive>
{
    private readonly TName _name;
    private readonly TExperienceYears _experienceYears;
    private readonly TIsActive _isActive;

    public TName Name { get { return _name; } }
    public TExperienceYears ExperienceYears { get { return _experienceYears; } }
    public TIsActive IsActive { get { return _isActive; } }

    public AnonymousType(TName name, TExperienceYears experienceYears, TIsActive isActive)
    {
        _name = name;
        _experienceYears = experienceYears;
        _isActive = isActive;
    }

    // Equals, GetHashCode, and ToString overrides omitted for brevity
}

Type Unification

The compiler optimizes anonymous types through type unification. If multiple anonymous type declarations within the same assembly specify the exact same property names and in the exact same order, the compiler maps them to the same underlying generic class definition. If the property types also match, they share the exact same constructed type.
var obj1 = new { X = 10, Y = 20 };
var obj2 = new { X = 50, Y = 100 };

// Evaluates to true; they share the same constructed compiler-generated type.
bool isSameType = obj1.GetType() == obj2.GetType(); 

var obj3 = new { Y = 20, X = 10 };
// Evaluates to true; property order differs, resulting in distinct generic type definitions.
bool isDifferentType = obj1.GetType() != obj3.GetType();

var obj4 = new { X = "Hello", Y = "World" };
// Evaluates to true; they share the same generic type definition, but use different generic type arguments.
bool isDifferentConstructedType = obj1.GetType() != obj4.GetType();

Equality Semantics

Although anonymous types are reference types, the compiler overrides the Equals and GetHashCode methods to enforce value equality semantics. Two anonymous type instances are considered equal if and only if all their corresponding properties are equal.
var point1 = new { X = 1, Y = 2 };
var point2 = new { X = 1, Y = 2 };

bool isValueEqual = point1.Equals(point2); // true
bool isReferenceEqual = object.ReferenceEquals(point1, point2); // false
The compiler also overrides the ToString method to output a concatenated string of the property names and their current values.
Console.WriteLine(point1.ToString()); 
// Output: { X = 1, Y = 2 }

Projection Initializers

You can omit explicit property names during instantiation if you are initializing the anonymous type from existing variables or member accesses. The compiler projects the variable or member name as the property name.
string environment = "Production";
int port = 8080;

// The compiler infers property names 'environment' and 'port'
var config = new { environment, port }; 

Non-Destructive Mutation

Because anonymous types are strictly immutable, their properties cannot be reassigned after initialization. However, starting in C# 10, anonymous types support the with expression, which allows for non-destructive mutation. This creates a new instance of the anonymous type, copying the existing values and applying the specified modifications.
var baseConfig = new { Host = "localhost", Port = 80, UseSsl = false };

// Creates a new instance, changing only the Port and UseSsl properties
var secureConfig = baseConfig with { Port = 443, UseSsl = true };
Master C# with Deep Grasping Methodology!Learn More