C# object
last modified July 5, 2023
In this article we show how to create and work with objects in C#.
An object is a basic building block of a C# program.
An object is a combination of data and methods. The data and the methods are called members of an object. All types are objects in C#.
Objects are created from templates. The templates are defined with
class
, struct
, or record
keywords.
We say that we create object instances from these templates.
Attributes and methods of an object are accessed with the dot operator. Constructors are specialized methods for creating objects.
C# struct object
A structure is a value type. The type is defined with the struct
keyword. Structures are meant to represent lightweight objects like Point,
Rectangle, Color and similar.
var p = new Point(2, 5); Console.WriteLine(p); public struct Point { private int x; private int y; public Point(int x, int y) { this.x = x; this.y = y; } public override string ToString() { return $"Point x:{x}, y:{y}"; } }
In the program, we create a lightweight object from a Point
structure. We also override the ToString
method, which gives a
human-readable representation of the object.
$ dotnet run Point x:2, y:5
Primitive types are objects
C# primitive types are objects. Under the hood, they are structures.
float x = 12.3f; int y = 34; bool z = false; Console.WriteLine(x.GetType()); Console.WriteLine(y.GetType()); Console.WriteLine(z.GetType()); Console.WriteLine(y.Equals(34));
We assign three primitive types to three variables.
Console.WriteLine(x.GetType()); Console.WriteLine(y.GetType()); Console.WriteLine(z.GetType()); Console.WriteLine(y.Equals(34));
The float, int, and bool types are objects. We call GetType
and
Equals
methods on these objects.
C# object's ToString method
Each object has a ToString
method. It returns a human-readable
representation of an object. The default implementation returns the fully
qualified name of the type of the Object
. Note that when we call
the Console.WriteLine
method with an object as a parameter, the
ToString
is being called.
var b = new Being(); var o = new Object(); Console.WriteLine(o.ToString()); Console.WriteLine(b.ToString()); Console.WriteLine(b); class Being { public override string ToString() { return "This is Being class"; } }
We have a Being
class in which we override the default
implementation of the ToString
method.
public override string ToString() { return "This is Being class"; }
Each class created inherits from the base object
. The
ToString
method belongs to this object class. We use the
override
keyword to inform that we are overriding a method.
var b = new Being(); var o = new Object();
We create one custom defined object and one built-in object.
Console.WriteLine(o.ToString()); Console.WriteLine(b.ToString());
We call the ToString
method on these two objects.
Console.WriteLine(b);
As we have specified earlier, placing an object as a parameter to the
Console.WriteLine
will call its ToString
method.
This time, we have called the method implicitly.
$ dotnet run System.Object This is Being class This is Being class
C# object attributes
Object attributes is the data bundled in an instance of a class. The object attributes are called instance variables or member fields. An instance variable is a variable defined in a object template, for which each object in the template has a separate copy.
var u1 = new User(); u1.name = "John Doe"; u1.occupation = "gardener"; var u2 = new User(); u2.name = "Roger Roe"; u2.occupation = "driver"; Console.WriteLine(u1); Console.WriteLine(u2); class User { public string? name; public string? occupation; public override string ToString() { return $"{name} is a {occupation}"; } }
We have a User
class with two member fields.
class User { public string? name; public string? occupation; public override string ToString() { return $"{name} is a {occupation}"; } }
We declare a name and occupation member fields. The public
keyword specifies that the member fields are accessible outside the class block.
var u1 = new User(); u1.name = "John Doe"; u1.occupation = "gardener";
We create an instance of the User
class and set the name and
occupation attributes. We use the dot operator to access the attributes of
objects.
Console.WriteLine(u1); Console.WriteLine(u2);
We print the two user objects to the console.
$ dotnet run John Doe is a gardener Roger Roe is a driver
C# object methods
Methods are functions defined inside the body of an object template. They are used to perform operations with the attributes of our objects.
var c = new Circle(); c.SetRadius(5); Console.WriteLine(c.Area()); class Circle { private int radius; public void SetRadius(int radius) { this.radius = radius; } public double Area() { return this.radius * this.radius * Math.PI; } }
In the code example, we have a Circle class. We define two methods.
private int radius;
We have one member field. It is the radius of the circle. The
private
keyword is an access specifier. It tells that the variable
is restricted to the outside world. If we want to modify this variable from the
outside, we must use the publicly available SetRadius
method.
public void SetRadius(int radius) { this.radius = radius; }
This is the SetRadius
method. The this
variable is a
special variable which we use to access the member fields from methods. The
this.radius
is an instance variable, while the radius is a local
variable, valid only inside the SetRadius
method.
var c = new Circle(); c.SetRadius(5);
We create an instance of the Circle
class and set its radius by
calling the SetRadius
method on the object of the circle. We use
the dot operator to invoke the method.
public double Area() { return this.radius * this.radius * Math.PI; }
The Area
method returns the area of a circle. The
Math.PI
is a built-in constant.
$ dotnet run 78.5398163397448
C# object constructor
Constructors are used to initialize fields.
var name = "Lenka"; var dob = new DateTime(1990, 3, 5); var u = new User(name, dob); Console.WriteLine(u); class User { private DateTime Born; private string Name; public User(string Name, DateTime Born) { this.Name = Name; this.Born = Born; } public override string ToString() => $"{this.Name} was born on {this.Born.ToShortDateString()}"; }
We have a constructor for the User
class.
var u = new User(name, dob);
We create the User
object, passing its constructor two values.
This is the time when the constructor of the object is called.
public User(string Name, DateTime Born) { this.Name = Name; this.Born = Born; }
Inside the constructor method, we initialize two attributes: Name
and Born
. Since the names of the attributes and constructor
parameters are the same, the this
keyword is mandatory. It is used
to refer to the attributes of the class.
$ dotnet run Lenka was born on 3/5/1990
C# object auto-implemented properties
C# has auto-implemented or automatic properties. With automatic properties, the compiler transparently provides the backing fields for us.
var u = new User(); u.Name = "John Doe"; u.Occupation = "gardener"; Console.WriteLine($"{u.Name} is a {u.Occupation}"); struct User { public string? Name { get; set; } public string? Occupation { get; set; } }
This code is much shorter. We have a User
struct in which we have
two properties: Name
and Occupation
.
var u = new User(); u.Name = "John Doe"; u.Occupation = "gardener"; Console.WriteLine($"{u.Name} is a {u.Occupation}");
We normally use the properties as usual.
public string? Name { get; set; } public string? Occupation { get; set; }
Here we have two automatic properties. There is no implementation of the accessors and there are no member fields. The compiler autogenerates them for us.
$ dotnet run John Doe is a gardener
C# object deconstruction
Deconstructing is unpacking types into single pieces; for instance, a tuple into its items or an object into its properties.
To deconstruct class instances, we have to implement the Deconstruct
method.
var u = new User("John", "Doe", "gardener"); var (fname, lname, occupation) = u; Console.WriteLine($"{fname} {lname} is a(n) {occupation}"); var (fn, ln) = u; Console.WriteLine($"{fn} {ln}"); class User { string FirstName { get; set; } string LastName { get; set; } string Occupation { get; set; } public User(string fname, string lname, string occupation) { FirstName = fname; LastName = lname; Occupation = occupation; } public void Deconstruct(out string fname, out string lname) { fname = FirstName; lname = LastName; } public void Deconstruct(out string fname, out string lname, out string occupation) { fname = FirstName; lname = LastName; occupation = Occupation; } }
We can have multiple Deconstruct
methods.
var u = new User("John", "Doe", "gardener"); var (fname, lname, occupation) = u;
Here, we deconstruct the User
type into three variables.
var (fn, ln) = u; Console.WriteLine($"{fn} {ln}");
Here, we deconstruct the user into two variables.
public void Deconstruct(out string fname, out string lname) { fname = FirstName; lname = LastName; }
The variables that are deconstructed have the out
modifier.
$ dotnet run John Doe is a(n) gardener John Doe
C# record object
A record is a reference type whose main purpose is to hold data. Records allow us to create objects quickly.
var users = new List<User> { new ("John", "Doe", 1230), new ("Lucy", "Novak", 670), new ("Ben", "Walter", 2050), new ("Robin", "Brown", 2300), new ("Amy", "Doe", 1250), new ("Joe", "Draker", 1190), new ("Janet", "Doe", 980), new ("Albert", "Novak", 1930), }; users.ForEach(Console.WriteLine); Console.WriteLine(users[0].FirstName); Console.WriteLine(users[0].LastName); Console.WriteLine(users[0].Salary); record User(string FirstName, string LastName, int Salary);
In the example, we have a list of User
object.s
var users = new List<User> { new ("John", "Doe", 1230), new ("Lucy", "Novak", 670), new ("Ben", "Walter", 2050), new ("Robin", "Brown", 2300), new ("Amy", "Doe", 1250), new ("Joe", "Draker", 1190), new ("Janet", "Doe", 980), new ("Albert", "Novak", 1930), };
A list of objects is created. Target-typed new expressions do not require type specification for constructors when the type is known. The compiler infers the type from the left side of the assignment operation.
Console.WriteLine(users[0].FirstName); Console.WriteLine(users[0].LastName); Console.WriteLine(users[0].Salary);
We access the three attributes of the record object with the dot operator.
record User(string FirstName, string LastName, int Salary);
The record is defined in one line. A record autogenerates several methods for
us, including Object.Equals
and Object.ToString
.
$ dotnet run User { FirstName = John, LastName = Doe, Salary = 1230 } User { FirstName = Lucy, LastName = Novak, Salary = 670 } User { FirstName = Ben, LastName = Walter, Salary = 2050 } User { FirstName = Robin, LastName = Brown, Salary = 2300 } User { FirstName = Amy, LastName = Doe, Salary = 1250 } User { FirstName = Joe, LastName = Draker, Salary = 1190 } User { FirstName = Janet, LastName = Doe, Salary = 980 } User { FirstName = Albert, LastName = Novak, Salary = 1930 } John Doe 1230
Source
Objects - create instances of types
In this article we have worked with objects in C#.
Author
List all C# tutorials.