Bird
Raised Fist0
C Sharp (C#)programming~10 mins

Indexer with custom types in C Sharp (C#) - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Indexer with custom types
Create class with custom type
↓
Define private storage (e.g., array)
↓
Define indexer method: get/set
↓
Use indexer to access elements
↓
Get or set custom type elements
↓
End
The flow shows creating a class with a private storage of custom types, defining an indexer to get or set elements by index, and then using that indexer to access or modify elements.
Execution Sample
C Sharp (C#)
class Box {
  public string Content;
}

class Container {
  private Box[] boxes = new Box[2];
  public Box this[int i] {
    get => boxes[i];
    set => boxes[i] = value;
  }
}

var c = new Container();
c[0] = new Box { Content = "Apple" };
var fruit = c[0].Content;
This code defines a Container class with an indexer to get/set Box objects by index, then stores a Box with content "Apple" at index 0 and retrieves its content.
Execution Table
StepActionIndexer CallIndexValue Set/GetResult/Output
1Create Container instanceN/AN/AN/AContainer object created with boxes array of size 2 (all null)
2Set c[0]set0Box { Content = "Apple" }boxes[0] now holds Box with Content "Apple"
3Get c[0]get0N/AReturns Box with Content "Apple"
4Access Content propertyN/AN/AN/A"Apple" string retrieved from Box
5EndN/AN/AN/AExecution complete
💡 Indexer calls complete; program ends after retrieving content.
Variable Tracker
VariableStartAfter Step 2After Step 3Final
cnullContainer instance with boxes[null, null]Same instanceSame instance
boxesnull[Box{Content="Apple"}, null][Box{Content="Apple"}, null][Box{Content="Apple"}, null]
fruitundefinedundefined"Apple""Apple"
Key Moments - 2 Insights
Why do we use an indexer instead of a normal method to access boxes?
The indexer lets us use array-like syntax (c[0]) to get or set elements, making code cleaner and more intuitive, as shown in execution_table steps 2 and 3.
What happens if we try to get an index that was never set?
The indexer returns the default value in the array, which is null for reference types like Box. This is implied in step 1 where boxes start as null.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what is the value of boxes[0] after step 2?
Anull
BBox with Content "Apple"
CEmpty Box
DString "Apple"
💡 Hint
Check the 'Value Set/Get' and 'Result/Output' columns in step 2.
At which step is the indexer 'get' accessor called?
AStep 3
BStep 2
CStep 1
DStep 4
💡 Hint
Look at the 'Indexer Call' column to find where 'get' is used.
If we set c[1] to a new Box with Content "Orange", what changes in variable_tracker after step 2?
Aboxes remains [Box{Content="Apple"}, null]
Bfruit changes to "Orange"
Cboxes becomes [Box{Content="Apple"}, Box{Content="Orange"}]
Dc becomes null
💡 Hint
Think about how setting an indexer updates the boxes array.
Concept Snapshot
Indexer with custom types:
- Define private storage (e.g., array of custom objects)
- Use 'public Type this[int index]' with get and set
- Access elements like array: obj[index]
- Enables clean syntax to get/set custom objects
- Useful for container-like classes
Full Transcript
This example shows how to create a class with an indexer that works with custom types. We start by defining a class Container that holds an array of Box objects. The indexer allows us to get or set Box objects by index using array-like syntax. When we create a Container instance, its boxes array starts with nulls. Setting c[0] to a new Box with Content "Apple" stores that Box at index 0. Getting c[0] returns that Box, and accessing its Content property gives us the string "Apple". The execution table traces these steps, showing how the indexer is called for get and set, and how variables change. Key points include understanding why indexers simplify access and what happens if an index is not set. The quiz questions reinforce these ideas by asking about variable states and indexer calls.

Practice

(1/5)
1.

What is the main purpose of an indexer in a C# class?

Choose the best description.

easy
A. To create a constructor for initializing objects.
B. To define a method that returns the size of a collection.
C. To allow objects to be accessed using array-like bracket syntax with custom keys.
D. To overload the + operator for custom types.

Solution

  1. Step 1: Understand what an indexer does

    An indexer lets you use square brackets [] to access elements inside an object, similar to arrays.
  2. Step 2: Identify the correct description

    Only To allow objects to be accessed using array-like bracket syntax with custom keys. describes this behavior correctly by mentioning bracket syntax and custom keys.
  3. Final Answer:

    To allow objects to be accessed using array-like bracket syntax with custom keys. -> Option C
  4. Quick Check:

    Indexer = bracket access [OK]
Hint: Indexers let you use [] on objects like arrays [OK]
Common Mistakes:
  • Confusing indexers with constructors
  • Thinking indexers define collection size
  • Mixing indexers with operator overloading
2.

Which of the following is the correct syntax to declare an indexer in a C# class using a custom type Key as the index?

public class MyCollection {
    // Indexer declaration here
}
easy
A. public string this[Key key] { get; set; }
B. public string this(Key key) { get; set; }
C. public string this[Key] { get; set; }
D. public string this[Key key] => get; set;

Solution

  1. Step 1: Recall indexer syntax

    Indexers use the keyword this followed by square brackets with the parameter type and name inside.
  2. Step 2: Check each option

    public string this[Key key] { get; set; } correctly uses this[Key key] with get and set accessors. Others have syntax errors or wrong brackets.
  3. Final Answer:

    public string this[Key key] { get; set; } -> Option A
  4. Quick Check:

    Indexer syntax = this[Type name] { get; set; } [OK]
Hint: Indexer uses this[Type name] with get/set inside braces [OK]
Common Mistakes:
  • Using parentheses instead of brackets
  • Omitting parameter name in brackets
  • Using expression-bodied syntax incorrectly
3.

Given the following code, what will be the output?

public record Key(int Id);

public class Store {
    private Dictionary<Key, string> data = new();
    public string this[Key key] {
        get => data.TryGetValue(key, out var val) ? val : "Not found";
        set => data[key] = value;
    }
}

var store = new Store();
store[new Key(1)] = "Apple";
store[new Key(2)] = "Banana";
Console.WriteLine(store[new Key(1)]);
Console.WriteLine(store[new Key(3)]);
medium
A. Not found\nNot found
B. Apple\nBanana
C. Compilation error
D. Apple\nNot found

Solution

  1. Step 1: Understand how the indexer accesses data

    The indexer uses a Dictionary with Key as key type. It returns the value if found, else "Not found".
  2. Step 2: Check the keys used

    Keys with Id 1 and 2 are added. Accessing new Key(1) finds "Apple". Accessing new Key(3) is missing, so returns "Not found".
  3. Final Answer:

    Apple Not found -> Option D
  4. Quick Check:

    Key(1) found, Key(3) missing = Apple, Not found [OK]
Hint: Indexer uses dictionary lookup; missing keys return default [OK]
Common Mistakes:
  • Assuming new Key(1) is different from stored Key(1)
  • Expecting Banana for Key(3)
  • Ignoring TryGetValue fallback
4.

Identify the error in the following code snippet that implements an indexer with a custom type Key:

public class DataStore {
    private Dictionary<Key, int> dict = new();
    public int this[Key key] {
        get { return dict[key]; }
        set { dict.Add(key, value); }
    }
}
medium
A. Using dict.Add in set causes exception if key exists; should use dict[key] = value.
B. Indexer must return string, not int.
C. Key type cannot be used as indexer parameter.
D. Missing constructor for DataStore class.

Solution

  1. Step 1: Analyze the set accessor

    The set accessor uses dict.Add(key, value), which throws an exception if the key already exists.
  2. Step 2: Suggest correct usage

    To update or add, use dict[key] = value; which overwrites existing or adds new key.
  3. Final Answer:

    Using dict.Add in set causes exception if key exists; should use dict[key] = value. -> Option A
  4. Quick Check:

    dict.Add throws if key exists; use dict[key] = value [OK]
Hint: Use dict[key] = value to avoid exceptions on set [OK]
Common Mistakes:
  • Using Add instead of index assignment in set
  • Thinking Key cannot be indexer parameter
  • Confusing return types in indexer
5.

You want to create a class Matrix that uses a custom struct Position as the indexer key to store integer values. The Position struct has two int fields: Row and Col. Which approach correctly implements the indexer to allow getting and setting values by Position?

public struct Position {
    public int Row, Col;
    public Position(int r, int c) { Row = r; Col = c; }
}

public class Matrix {
    private Dictionary<Position, int> values = new();
    // Implement indexer here
}
hard
A. public int this[Position pos] { get => values[pos]; set => values[pos] = value; }
B. public int this[Position pos] { get => values.TryGetValue(pos, out var v) ? v : 0; set => values[pos] = value; }
C. public int this[Position pos] { get { return values[pos]; } set { values.Add(pos, value); } }
D. public int this[int row, int col] { get => values[new Position(row, col)]; set => values[new Position(row, col)] = value; }

Solution

  1. Step 1: Consider safe get behavior

    Using TryGetValue with fallback 0 avoids exceptions if the position key is missing.
  2. Step 2: Check set accessor correctness

    Using values[pos] = value; safely adds or updates the dictionary entry.
  3. Step 3: Evaluate other options

    public int this[int row, int col] { get => values[new Position(row, col)]; set => values[new Position(row, col)] = value; } uses two parameters, not matching the question's single Position key. public int this[Position pos] { get { return values[pos]; } set { values.Add(pos, value); } } uses Add in set, which throws if key exists. public int this[Position pos] { get => values[pos]; set => values[pos] = value; }'s get accessor throws if key missing.
  4. Final Answer:

    public int this[Position pos] { get => values.TryGetValue(pos, out var v) ? v : 0; set => values[pos] = value; } -> Option B
  5. Quick Check:

    Safe get with TryGetValue + index assignment set [OK]
Hint: Use TryGetValue in get to avoid exceptions on missing keys [OK]
Common Mistakes:
  • Using Add in set causing exceptions
  • Not handling missing keys in get
  • Using multiple parameters instead of custom type
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard