Summary: in this tutorial, you’ll learn about C# anonymous types to create a single object that consists of read-only properties without having to define a class first.
Introduction to C# anonymous types
Typically, you define a class that has a set of properties and use the class to create objects.
But sometimes you need to create an object that has a set of read-only properties without having to define a class. To do it, you use anonymous types.
By definition, anonymous types allow you to encapsulate a set of read-only properties into a single object without having to define a class first.
Behind the scenes, the compiler will generate the type name for the anonymous type. Therefore, the type name is not accessible at the source code level. The compiler also infers the type of each property of the anonymous type.
To create anonymous types, you use the new
operator with an object initializer. For example, the following demonstrates how to create an anonymous type with two properties named Name
and Price
:
using static System.Console;
var product = new
{
Name = "Phone X",
Price = 999.99
};
WriteLine($"{product.Name} : {product.Price:C}");
Code language: C# (cs)
Output:
Phone X : $999.99
Code language: C# (cs)
If you hover the mouse over the Name
or Price
properties, you’ll see that the compiler infers the type of the Name
property to string and the type of the Price
property to double
:
Because the Name
and Price
properties are read-only, if you attempt to assign a new value to it, you’ll get a compiled error. For example:
product.Price = 2000; // error
Code language: C# (cs)
To form a new anonymous object with the same name and different price, you can use the with
expression.
The with
expression creates an anonymous type object where one or more properties have new values. For example:
using static System.Console;
var product = new
{
Name = "Phone X",
Price = 999.99
};
WriteLine($"{product.Name} : {product.Price:C}");
var productX = product with
{
Price = 1099.99
};
WriteLine($"{productX.Name} : {productX.Price:C}");
Code language: C# (cs)
Output:
Phone X : $999.99
Phone X : $1,099.99
Code language: C# (cs)
In this example, we use the with
expression to create a new instance of the same anonymous type with the product
object, but with the new Price
value.
It’s possible to define an array of anonymous types like this:
var products = new[]
{
new { Name = "Phone X", Price = 999.99 },
new { Name = "Computer Y", Price = 1999.99 },
new { Name = "Tablet Z", Price = 2999.99 }
};
Code language: C# (cs)
And an anonymous type can have properties that are instances of explicit types. For example:
using static System.Console;
var product = new
{
Name = "Phone X",
Price = 999.99,
attribute = new ProductAttribute() { Color = "Black", Weight = 100 }
};
WriteLine($"{product.Name}: {product.attribute.Color}");
class ProductAttribute
{
public string? Color
{
get; init;
}
public decimal? Weight
{
get; init;
}
}
Code language: C# (cs)
In this example, we define an anonymous object with a property that is an instance of the ProductAttribute
class.
The supertype of anonymous types
Anonymous types inherit directly from the object
type. And you cannot cast anonymous types to any type except object
.
If you have two or more anonymous objects that have the same set of properties in the same order and types, the compiler will treat these objects with the same type. In other words, they share the same generated type. For example:
using static System.Console;
var productX = new { Name = "Phone X", Price = 999.99 };
var productY = new { Name = "Phone Y", Price = 555.99 };
WriteLine(productX.GetType() == productY.GetType()); // true
Code language: C# (cs)
Output:
<>f__AnonymousType0`2[System.String,System.Double]
True
Code language: C# (cs)
In this example, productX
and productY
will share the same anonymous type’s name. Note that you may get a different generated type name.
Two anonymous objects of the same type are equal if all their properties are equal. For example:
using static System.Console;
var productX = new { Name = "Phone X", Price = 99 };
var productY = new { Name = "Phone X", Price = 99 };
WriteLine(productX.Equals(productY));
Code language: C# (cs)
Output:
True
Code language: C# (cs)
The reason is that anonymous types override the Equals
and GetHashCode
methods in terms of Equals
and GetHashCode
methods of its properties.
Anonymous types override the ToString
method by concatenating the results of ToString
method of all properties and wrapping it in curly braces:
using static System.Console;
var productX = new { Name = "Phone X", Price = 99 };
WriteLine(productX.ToString());
Code language: C# (cs)
Output:
using static System.Console;
var productX = new { Name = "Phone X", Price = 99 };
WriteLine(productX.ToString());
Code language: C# (cs)
Output:
{ Name = Phone X, Price = 99 }
Code language: C# (cs)
Summary
- Use anonymous types to create an object with read-only properties.
- Use the
new
keyword and an object initializer to declare an anonymous type. - Use anonymous types when you want to create an object that stores data temporarily without defining a separate type.
- Anonymous types are immutable, implicitly typed, and created at runtime.