索引器讓物件像陣列一樣存取,運算子多載讓自訂型別支援數學/邏輯運算。兩者本質都是方法的語法糖,編譯為特定命名的方法。本文從語法、IL 實作、多載規則、進階應用全方位解析。
public class BitArray
{
private int[] _bits;
private int _length;
public BitArray(int length)
{
_length = length;
_bits = new int[(length - 1) >> 5 + 1];
}
// 索引器宣告
public bool this[int index]
{
get
{
if (index < 0 || index >= _length) throw new IndexOutOfRangeException();
return (_bits[index >> 5] & (1 << index)) != 0;
}
set
{
if (index < 0 || index >= _length) throw new IndexOutOfRangeException();
if (value) _bits[index >> 5] |= 1 << index;
else _bits[index >> 5] &= ~(1 << index);
}
}
}
// 編譯為 IL 相當於:
// public bool get_Item(int index) { ... }
// public void set_Item(int index, bool value) { ... }
this,無自訂名稱in/ref/out 不可用get/set 可個別限制 (如 public int this[int i] { get; private set; })static (屬於實例)
public class Matrix
{
private double[,] _data;
public Matrix(int rows, int cols) => _data = new double[rows, cols];
public double this[int row, int col]
{
get => _data[row, col];
set => _data[row, col] = value;
}
}
// 使用
var m = new Matrix(3, 3);
m[1, 2] = 3.14;
double val = m[1, 2];
public class Base
{
public virtual string this[int i] => $"Base[{i}]";
}
public class Derived : Base
{
// 覆寫需使用 override
public override string this[int i] => $"Derived[{i}]";
// 新增多載
public string this[string key] => $"Key:{key}";
}
// 基底索引器存取
var d = new Derived();
Console.WriteLine(d[0]); // Derived[0]
Console.WriteLine(((Base)d)[0]); // Base[0]
// 或內部使用 base[0]
| 分類 | 可多載運算子 | 參數數量 | 回傳型別限制 |
|---|---|---|---|
| 一元 | + - ! ~ ++ -- true false | 1 | ++/-- 必須同類別;true/false 必須為 bool 且成對出現 |
| 二元 | + - * / % & | ^ << >> == != > < >= <= | 2 | 至少一個參數為宣告類別;==/!=、>/<、>=/<= 必須成對 |
| 轉換 | implicit / explicit | 1 | 參數與回傳型別必須不同,且至少一個為宣告類別;不可為 object/interface;不可有繼承關係 |
public static 修飾ref/outvirtual/abstract/override/sealed
public readonly struct Complex : IEquatable
{
public double Real { get; }
public double Imag { get; }
public Complex(double real, double imag) => (Real, Imag) = (real, imag);
// 二元運算子
public static Complex operator +(Complex a, Complex b) => new(a.Real + b.Real, a.Imag + b.Imag);
public static Complex operator -(Complex a, Complex b) => new(a.Real - b.Real, a.Imag - b.Imag);
public static Complex operator *(Complex a, Complex b) =>
new(a.Real * b.Real - a.Imag * b.Imag, a.Real * b.Imag + a.Imag * b.Real);
public static Complex operator /(Complex a, Complex b)
{
double denom = b.Real * b.Real + b.Imag * b.Imag;
return new((a.Real * b.Real + a.Imag * b.Imag) / denom,
(a.Imag * b.Real - a.Real * b.Imag) / denom);
}
// 一元運算子
public static Complex operator -(Complex a) => new(-a.Real, -a.Imag); // 取負
public static Complex operator ++(Complex a) => new(a.Real + 1, a.Imag + 1); // 自增 (邏輯自訂)
// 比較運算子 (成對)
public static bool operator ==(Complex a, Complex b) => a.Equals(b);
public static bool operator !=(Complex a, Complex b) => !a.Equals(b);
// 隱式/顯式轉換
public static implicit operator Complex(double d) => new(d, 0); // double -> Complex
public static explicit operator double(Complex c) => c.Magnitude; // Complex -> double (需強制轉型)
// 物件相等
public bool Equals(Complex other) => Real == other.Real && Imag == other.Imag;
public override bool Equals(object obj) => obj is Complex c && Equals(c);
public override int GetHashCode() => HashCode.Combine(Real, Imag);
public double Magnitude => Math.Sqrt(Real * Real + Imag * Imag);
public override string ToString() => $"{Real} + {Imag}i";
}
// 使用
var a = new Complex(3, 4);
var b = new Complex(1, 2);
var c = a + b; // 4 + 6i
var d = -a; // -3 - 4i
var e = a * b; // -5 + 10i
bool eq = a == b; // false
Complex f = 5.0; // 隱式轉換 5 + 0i
double mag = (double)f; // 顯式轉換 5.0
a++ 回傳舊值,++a 回傳新值,兩者呼叫同一個 operator++&&/|| 短路邏輯,必須成對定義,回傳 bool
public class Vector
{
private readonly double[] _components;
public int Dimension => _components.Length;
public Vector(params double[] components) => _components = components ?? throw new ArgumentNullException();
// 索引器
public double this[int i]
{
get => _components[i];
set => _components[i] = value;
}
// 運算子
public static Vector operator +(Vector a, Vector b)
{
if (a.Dimension != b.Dimension) throw new InvalidOperationException("維度不符");
var result = new double[a.Dimension];
for (int i = 0; i < a.Dimension; i++) result[i] = a[i] + b[i];
return new Vector(result);
}
public static Vector operator *(Vector v, double scalar)
{
var result = new double[v.Dimension];
for (int i = 0; i < v.Dimension; i++) result[i] = v[i] * scalar;
return new Vector(result);
}
public static double operator *(Vector a, Vector b) // 內積
{
if (a.Dimension != b.Dimension) throw new InvalidOperationException("維度不符");
double sum = 0;
for (int i = 0; i < a.Dimension; i++) sum += a[i] * b[i];
return sum;
}
}
public class Matrix
{
private readonly double[,] _data;
public int Rows => _data.GetLength(0);
public int Cols => _data.GetLength(1);
public Matrix(int rows, int cols) => _data = new double[rows, cols];
public Matrix(double[,] data) => _data = data;
public double this[int r, int c] { get => _data[r, c]; set => _data[r, c] = value; }
public static Matrix operator *(Matrix a, Matrix b)
{
if (a.Cols != b.Rows) throw new InvalidOperationException("矩陣維度不符");
var result = new Matrix(a.Rows, b.Cols);
for (int i = 0; i < a.Rows; i++)
for (int j = 0; j < b.Cols; j++)
for (int k = 0; k < a.Cols; k++)
result[i, j] += a[i, k] * b[k, j];
return result;
}
}
| 特性 | 索引器 | 運算子多載 |
|---|---|---|
| 本質 | get_Item/set_Item 方法 | op_Addition/op_Implicit 等特殊名稱方法 |
| 適用場景 | 集合存取、矩陣、字典語意 | 數值型別、領域模型、DSL |
| 多載依據 | 參數簽名 | 運算子符號 + 參數型別 |
| 介面支援 | 可在介面宣告 | 不可在介面宣告 (C# 11+ static abstract 除外) |
| 慣例 | 引數驗證拋 IndexOutOfRangeException | 回傳新例項、不可修改參數 |
設計原則:索引器提供「容器存取」語意,運算子提供「數學/邏輯運算」語意。濫用會導致程式碼難讀 (如 customer + order 語意不明)。僅在型別具有明確數學結構 (向量、矩陣、複數、單位量) 或容器語意 (List、Dictionary、BitArray) 時使用。