C# Classes and Objects: Create Instances and Trace References
A class defines a type; new creates an instance, and a variable holds a reference to it. Build small inventory objects and learn exactly which object each assignment and method call changes.
What you will understand
- Read fields and instance methods inside a class declaration.
- Distinguish a type name, reference variable and object instance.
- Create objects with field defaults and object initializers.
- Use dot notation to select members on a receiver.
- Trace independent instances, aliases and reference reassignment.
- Explain class references passed through ordinary parameters.
- Create a deliberate small snapshot and recognize its limits.
- Handle a missing object and test an inventory operation.
Read a class declaration one part at a time
The previous lesson introduced objects as state plus behavior. Now we will read and write the syntax that creates those objects. InventoryItem represents one item record with a name and stock count. Its class definition contains two fields and a method that describes one instance.
In class InventoryItem, class introduces a type definition and InventoryItem names that type. The outer braces enclose its members. public string Name is a field declaration, public int Stock is another, and public string Describe() declares an instance method that returns a string. Each member belongs inside the class, not inside Main.
We use public fields to keep the first syntax examples small. They allow direct reads and writes and therefore do not guarantee valid inventory state. The lesson’s goal is object creation, member access and reference behavior; later access-modifier and property lessons restrict how fields can change. Each editor here contains a separate complete Program.cs.
Program.cs · Declare the type, create an instance, select a member
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Pen: 5 in stock
InventoryItem is the type; pen is a local reference variable; new InventoryItem() creates an instance. The assignments change fields on that instance. Describe() is an instance method that reads the receiving object’s fields and returns text for Main to print.
Separate declaration, creation and initialization
InventoryItem pen declares a local variable whose type is InventoryItem. new InventoryItem() creates an object and supplies a reference to it. The assignment stores that reference in pen. These are related steps, but the type name, the variable and the created object are different concepts.
The class declares no constructor, so C# supplies an implicit parameterless constructor for it. The field initializers give every newly created instance the chosen starting values Unnamed and zero. Later we will study explicit constructors that require useful starting inputs rather than relying on placeholder names.
An object initializer combines creation with assignments to accessible members: new InventoryItem { Name = "Pen", Stock = 5 }. It uses commas between member assignments and a semicolon after the complete statement. Creation happens before those assignments. This syntax does not automatically validate a name or prevent a negative stock value.
Program.cs · Field initializers give each new object a starting value
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Unnamed: 0 in stock
The field initializers run as part of creating the instance, so Name starts as Unnamed and Stock as zero. These chosen defaults are not validation. A parameterless constructor is supplied implicitly here because the class declares no instance constructor; constructors are studied later.
Program.cs · An object initializer groups member assignments
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Pen: 5 in stock
The braces after new contain comma-separated member assignments. The object is created, then its accessible members are assigned. These are the same public fields used earlier; object initializer syntax does not turn them into properties or prevent invalid values.
The dot identifies which object receives the work
pen.Name accesses a field through pen’s reference. pen.Describe() calls a method on that same receiver. Inside an instance method, an unqualified instance field such as Stock belongs to the current object. The compiler knows which instance from the call’s receiver.
Two new expressions create two separate InventoryItem objects. They share a class definition and method code, but not the storage for their instance fields. Selling pens should affect the pen object’s count while leaving a notebook object unchanged. You do not need two different class definitions to give the instances different values.
TrySell demonstrates a useful operation with a quantity parameter and a Boolean success result. It refuses quantities at or below zero and quantities greater than current stock, otherwise reducing stock once. For this exercise, assume the object starts with nonnegative stock. The method guards its own calls, while the public field can still be assigned externally.
Program.cs · One class can create independent items
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Pen: 3 in stock
Notebook: 3 in stock
Two new expressions create two instances. The first assignment reduces only the pen’s Stock. The methods are defined once in the class, but each call reads the fields of its receiver. Equal stock counts afterward do not imply the references identify the same object.
Program.cs · An instance method acts on its receiver
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Accepted: True
Pen: 3 in stock
Notebook: 3 in stock
pen.TrySell(2) uses the pen object as its receiver. Inside TrySell, Stock refers to that instance’s field. The method checks the requested quantity, changes stock only on acceptance and reports success with a Boolean. The notebook is a separate receiver and stays unchanged.
Program.cs · A type name is not an instance receiver
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Compilation fails: an object reference is required for the non-static Describe member.Describe needs an instance whose Name and Stock it can read. InventoryItem names the type, not a particular item. Create an object and call pen.Describe() rather than making the method static simply to remove this diagnostic.
Assignment copies a reference, not an object
An InventoryItem variable stores a reference to an instance. When selected = pen runs, the reference value is copied. Both variables now lead to the same object. Think of two arrows pointing at one item card rather than two separately copied cards.
Changing selected.Stock therefore changes what pen.Stock reads. In contrast, assigning selected = new InventoryItem creates another instance and makes selected point to it. pen still points to the original. A member write changes the object; a variable assignment can change which object that variable identifies.
Use object.ReferenceEquals to ask whether two references identify the same object. Comparing matching names or stock numbers answers a different question about data. Two separate items can contain equal values, while one item can change its values over time without becoming a different object.
Program.cs · Assignment copies a reference rather than the item
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Pen: 1 in stock
True
selected and pen point to one object. Writing through selected changes what pen reads too. ReferenceEquals confirms shared identity. The assignment did not execute new, allocate an independent InventoryItem, or create a snapshot of the fields.
Program.cs · Reassigning a variable redirects that reference
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Pen: 5 in stock
Notebook: 3 in stock
False
The first assignment shares a reference. The later new expression creates another object and redirects selected to it. It does not replace the object referenced by pen. Distinguish changing a member, selected.Stock = 1, from changing a variable’s reference, selected = new InventoryItem(...).
Trace references through methods and deliberate copies
Passing a class variable to an ordinary method copies its reference value into the parameter. The helper can then mutate the same object through that reference, just as with arrays. No ref keyword is required for such member mutations to be visible in the caller.
An independent snapshot requires a separate instance. In this small model, create a new InventoryItem and assign the original Name and Stock into it. The integer count is copied, so later changes to the original count do not affect the snapshot. The string value is immutable; assigning a different name later changes a field reference rather than mutating the shared string.
Do not generalize this small example into a deep-copy algorithm. If a class contains a mutable array or another mutable object, assigning that member’s reference would share the child object. Decide what a copy is supposed to preserve before implementing it. A snapshot also does not automatically save itself to disk or provide an audit history.
Program.cs · An ordinary parameter receives a reference copy
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Display pen: 5 in stock
Rename receives a copy of the reference and can reach the same object, so changing Name is visible in Main. The object was not passed by ref. Reassigning the parameter to another object would redirect only its local reference, following the parameter lesson.
Program.cs · Create an independent object deliberately
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Pen: 5 in stock
Pen: 1 in stock
False
A separate new expression creates the snapshot instance, then copies the selected field values. Stock is an independent integer field. Name is a shared reference to an immutable string, which is safe for this example. This is not a general deep-copy technique for classes containing mutable child objects.
A variable can exist without referring to an object
A nullable reference variable may contain null, meaning it currently identifies no object. InventoryItem? selected = null expresses that possibility when nullable annotations are enabled. This differs from new InventoryItem(), which creates a real instance even if its members still have defaults.
A call such as selected.Describe() needs an actual receiver. Check selected is null before using it when absence is a normal case. The guard can present No item selected instead of attempting an instance call through a missing reference. Nullable compiler analysis helps reveal risky uses, but null checks remain meaningful runtime behavior.
A type name also is not a receiver for an instance member. InventoryItem.Describe() fails compilation because the method needs one instance’s fields. Do not convert every method to static to remove the error; first identify which object should receive the operation. The intentional compiler example appears with the receiver examples above.
Program.cs · A class variable may have no object to use
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
No item selected
InventoryItem? explicitly allows a missing reference when nullable reference annotations are enabled. null is not an empty InventoryItem and has no instance to receive Describe(). The guard makes the missing-selection case explicit. If nullable annotations are disabled, the annotation can produce a warning rather than changing runtime behavior.
Check identity, state and independence together
For two objects, record which new expression created each one and which variable currently points where. Then trace every field write or method call through its receiver. This simple reference sketch often explains an unexpected update faster than staring at the final report.
Test a normal sale, a sale of all remaining stock, zero, a negative quantity and a request above the stock count. A refused request should preserve the receiver’s count. Also check that the second independent item remains unchanged after each request. Testing only the accepted message misses important state behavior.
The lab uses in-memory item records and fixed source inputs. It does not persist inventory, process payments or coordinate concurrent sales. Its public fields are intentionally visible while learning class syntax. The next lesson examines fields, local variables and static members more closely, before constructors and properties strengthen the model.
Guided repair: create an independent snapshot
The report needs an independent pre-sale snapshot, but backup changes from five to three when the original is sold. Repair the backup creation while keeping the sale and class behavior intact.
Program.cs · A supposed backup still points at the original
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Original: 3
Backup: 3
The requirement is an independent pre-sale stock snapshot, but backup is an alias. Both reads reach the modified object. Changing the variable name to backup does not create a copy or preserve history.
Repair checkpoints
- Identify backup = original as a reference copy, not an object copy.
- Create a separate InventoryItem before the sale.
- Copy Name and Stock into the new object.
- Verify the original becomes three while backup remains five.
- Keep the scope clear: these two fields are copied, not an arbitrary graph of mutable child objects.
Reveal the repair and explanation
Program.cs · Copy the intended fields into a separate instance
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Original: 3
Backup: 5
backup now refers to a distinct object created before the sale. Its copied Stock remains five. This snapshot is sufficient for the two simple fields in this exercise, but is neither durable history nor a complete object-graph cloning strategy.
Independent lab: two items, one guarded sale
Complete an inventory item’s guarded sale operation and demonstrate that selling one item does not change a separate item. Use a single class definition for both instances.
Acceptance checklist
- Create separate Pen and Notebook instances with stock five and three.
- Keep Describe returning Name plus the current Stock in the demonstrated format.
- Implement public bool TrySell(int quantity) as an instance method.
- Return false without a state change when quantity is zero, negative or greater than Stock.
- Otherwise subtract quantity once and return true.
- Sell two pens and print Sale accepted: True, Pen: 3 in stock, Notebook: 3 in stock.
- Keep output in Main; assume initial stock is nonnegative and acknowledge that public fields still permit direct writes.
Program.cs · Inventory practice starter
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Sale accepted: False
Pen: 5 in stock
Notebook: 3 in stock
The two independent objects and report already work. TrySell is a compilable placeholder that refuses everything. Complete the guarded instance operation, then test accepted and refused requests without modifying the other item.
Show hints
- Use a guard with quantity <= 0 || quantity > Stock.
- Only the accepted path changes Stock.
- The call pen.TrySell(2) identifies the instance to update.
Reveal the reference solution
Program.cs · Inventory practice reference solution
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Sale accepted: True
Pen: 3 in stock
Notebook: 3 in stock
The method refuses zero, negative and excessive quantities before changing Stock. An accepted request decreases only its receiver’s field. Public fields remain a deliberate teaching shortcut: callers could bypass the guard, so this is not complete encapsulation. A later properties lesson closes that boundary.
Changed-input checks
- Quantity 0: refused, pen stock remains 5.
- Quantity -1: refused, pen stock remains 5.
- Quantity 6: refused, pen stock remains 5.
- Quantity 5: accepted, pen stock becomes 0.
- After selling all five, another sale of one is refused.
- Notebook stock stays 3 for every pen sale.
Recap: read each object operation
Identify the type, the reference variable, the created instance and the receiver of every member access.
class InventoryItem- Declare a custom reference type and its members.
InventoryItem pen- Declare a local variable that can hold an item reference.
new InventoryItem()- Create an instance using the parameterless constructor.
new InventoryItem { ... }- Create the instance, then assign accessible members.
pen.Describe()- Call an instance method on pen’s referenced object.
selected = pen- Copy the reference; do not clone the object.
selected = new InventoryItem()- Redirect selected to a newly created instance.
selected is null- Check whether this reference identifies no object.
Frequently asked questions
Does declaring a variable create an object?
No. A declaration introduces a variable; new creates an instance. A local variable must be assigned before it can be read.
Why does InventoryItem.Describe() fail?
Describe is an instance method that needs one item’s fields. Call it through an object reference, such as pen.Describe().
Does an object initializer validate my data?
No. It assigns accessible members after object creation. The public fields in this lesson still permit invalid values; later lessons restrict that boundary.
Does selected = pen create a backup?
No. It copies the reference to the same object. Create another instance and deliberately copy the required state when an independent snapshot is intended.
Can a helper change my item without ref?
Yes. An ordinary parameter receives a reference copy that reaches the same object. Reassigning that parameter is different from changing the object’s members.
Is null the same as an item with zero stock?
No. null identifies no object, while a zero-stock item is an actual instance with a count of zero.
Can you trace the object references?
Distinguish creation, member access, aliasing, reassignment and a missing reference. Read the explanation after checking your answers.
Next step and references
A class defines members, new creates an instance and a variable holds a reference. Follow the receiver to understand a member write, distinguish aliases from separate instances, and deliberately decide what a snapshot copies.
Next is C# Fields, Methods, and Static Members. You will distinguish fields from locals and decide which data belongs to an instance or to the type.
Official references
Original SovranCode explanations, examples and exercises. Technical details were checked against these references.
Last updated: .