[Better code] - C# - Using Indexer

Posted by William Basics on Sunday, October 31, 2021

什么是Indexer?

Indexer 可以帮助我们如同用索引来访问数组中的元素一样来访问自定义对象中的元素。它不仅可以帮助我们写出更流畅的代码,更提高了代码的可读性。下面我将用一个示例来一起看看Indexer 是如何帮助到我们的。

使用Indexer

背景示例

假设有Company, Department 和 Employee 三个类,它们之间的关系如下UML类图所示:

uml

一个Company 包含多个Department。一个Department包含多个Employee。

我们很容易就能写出他们的定义

public class Company
{
    private List<Department> _departments = new();

    public void AddDepartment(Department department) {
        _departments.Add(department);
    }
}

public class Department
{
    public string Name { get; private set; }

    public Department(string name) {
        Name = name;
    }

    private List<Employee> _employees = new();

    public void AddEmployee(Employee employee) {
        _employees.Add(employee);
    }
}

public class Employee
{
    public string Name { get; private set; }
    public decimal Salary { get; private set; }

    public Employee(string name, decimal salary) {
        Name = name;
        Salary = salary;
    }
}

一般情况下,Company对象会被预先创建并初始化,作为一个数据集供外部查询。

假定外部模块,需要查询这个Company里的某个员工的Salary。那么最直接的写法类似于这样

var salary = CompanyObj.GetDepartment(departmentName).GetEmployee(employeeName).Salary

但是,如果我们使用Indexer的话,代码的表达将变得更加简洁,

var salary = CompanyObj[departmentName][employeeName].Salary

创建Indexer

为类增加Indexer很简单,就像是声明一个property,并且这个“property”可以接受参数。而参数则被方括号围起来。

下面为Company类增加Indexer。

public class Company
{
    ...
    public Department this[string name] => _departments.SingleOrDefault(e => e.Name == name);
}

this 关键词不可或缺,表明了这是个Indexer。

下面是为Department类增加的Indexer

public class Department
{
    ...
	public Employee this[string name] => _employees.SingleOrDefault(e => e.Name == name);
}

调用Indexer

[Fact]
public void RetrieveValueShouldWork()
{
    var company = _fixture.Company;

    var CarlSalary = company["Software"]["Carl"].Salary;
    CarlSalary.Should().Be(14000);

    var FreyaSalary = company["Sales"]["Freya"].Salary;
    FreyaSalary.Should().Be(12000);
}

完整的代码可在这里访问 GitHub

总结

Indexer 帮忙我们简化了代码,提高了代码可读性。它甚至可以接受多个参数,在索引多维数据源时尤其适用。

参考文档

MS-Indexers: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/#indexers-overview