字段、属性、索引器

141 阅读5分钟

1.字段

字段是类或结构体中用于存储数据的变量,它是类或结构体的一个成员。字段可以是任何数据类型,包括基本数据类型(如int、double等)、引用类型(如string、自定义类等)。

Ⅰ.字段的定义
public class Person{
    //共有字段,可以被外部访问
    public string name;
    //私有字段,只能Person内部访问
    private int age;
}
Ⅱ.字段初始化
public class Person{
    //初始值未“Bob”
    public string name = "Bob";
    //默认初始值为0
    private int age;
}
Ⅲ.只读字段
  • 使用const的时候需要在声明字段时初始化。
  • 使用 readonly 关键字可以声明只读字段,可以在声明时或者构造函数中初始化。
  • 只读字段只能在声明时或在类的构造函数中赋值,一旦赋值后就不能再修改。
Person p = new Person("ZJU");

//p.School = "THU";School只读不能修改

public class Person
{
    
    public const string name = "Lisa";
	// 只读字段,在声明时初始化
    public readonly int Id = 12345;
	// 只读字段,在构造函数中初始化
    public readonly string School;
    
    public Person(string school)
    {
        School = school;
    }
}
Ⅳ.静态字段

使用 static 关键字可以声明静态字段。静态字段属于类本身,而不是类的实例,所有实例共享同一个静态字段的值。

Counter c1 = new Counter();
Counter c2 = new Counter();

//输出实例化的个数,直接通过类名进行访问
Console.WriteLine(Counter.instanceCount);

public class Counter
{
    // 静态字段,用于记录创建的实例数量
    public static int instanceCount = 0;

    public Counter()
    {
        //每次创建新实例,计数器+1
        instanceCount++;
    }
}
Ⅴ.字段的访问控制

通过不同的访问修饰符(如 public、private、protected、internal 等)可以控制字段的访问级别,从而实现信息隐藏和封装。

2.属性

Ⅰ.属性基础概念
  • 属性(Property)是一种用于封装类、结构体或接口中的字段的成员,它在外部提供了统一的访问方式,隐藏了内部字段的具体实现细节,增强了类的封装性和安全性。同时,属性还可以在数据访问时添加额外的逻辑,如数据验证、计算等。
Ⅱ.属性的声明
  • 自动属性:编译器会自动生成一个私有字段来存储属性的值,开发者无需手动定义。优点:简洁。缺点:无法进行数据验证、计算等
  • 传统属性:传统属性需要显式定义一个私有字段,并在属性的 get 和 set 访问器中编写逻辑来操作该字段。
Person p = new Person();
p.Name = "Lihua";
Console.WriteLine(p.Name);

public class Person
{
    //声明私有字段
    private string name;
    //属性
    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    //自动实现属性
    public int Age {  get; set; }
}
Ⅲ.只读/只写属性
  • 只读属性:只包含 get 访问器的属性称为只读属性,它的值通常在对象创建时初始化,之后不能被修改。
class Circle
{
    private readonly double radius;

    public Circle(double radius)
    {
        this.radius = radius;
    }

    public double Area
    {
        get { return radius * radius * Math.PI; }
    }
}
  • 只写属性:只包含 set 访问器的属性称为只写属性,很少使用这种情况。
Ⅳ.属性访问修饰符
  • get 和 set 访问器可以有不同的访问修饰符,比如public,private等。
Student s = new Student();
s.SetScore(99.99);//只能通过SetScore方法来给_score赋值
Console.WriteLine(s.Score);

public class Student
{
    private double _score;

    public double Score
    {
        get { return _score; }
        private set { _score = value; }
    }
    public void SetScore(double myScore)
    {
        _score = myScore;
    }
}
Ⅴ.静态属性
  • 使用static关键字,静态属性属于类本身,直接通过类名来访问。
Calculator.Add(1, 1);
Calculator.Add(1, 2);
Console.WriteLine(Calculator.Count);

public class Calculator
{
    private static int _count;

    public static int Count
    {
        get { return _count; }
        set { _count = value; }
    }

    public static int Add(int a, int b)
    {
        _count++;
        return a + b;
    }
}

3.索引器

索引器(Indexer)是一种特殊的属性,它允许对象像数组一样通过索引来访问其元素,提供了一种简洁且直观的方式来访问对象中的一组数据。

Ⅰ.基础语法

使用 this 关键字,并指定索引参数的类型和数量。

[访问修饰符] 返回类型 this[索引类型 参数名]
{
    get
    {
        // 获取索引位置元素的逻辑
    }
    set
    {
        // 设置索引位置元素的逻辑
    }
}
MyArray ma = new MyArray();
ma[0] = "abc";
Console.WriteLine(ma[0]);

public class MyArray
{
    private string[] _arr = new string[10];

    public string this[int index]
    {
        get
        {
            if(index < 0 || index > _arr.Length)
            {
                throw new IndexOutOfRangeException();
            }
            return _arr[index];
        }
        set
        {
            if (index < 0 || index > _arr.Length)
            {
                throw new IndexOutOfRangeException();
            }
            _arr[index] = value;
        }
    }
}

索引器的索引参数可以是除 refout 之外的任何类型,常见的有 int、string 等。

string 类型索引:使用字符串作为索引值来访问对象的元素。这种索引类型适用于需要通过名称或键来访问元素的场景,类似于字典。

MyArray ma = new MyArray();
ma["A"] = 1;
ma["B"] = 2;
Console.WriteLine(ma["A"]);
Console.WriteLine(ma["B"]);
 
public class MyArray
{
    private Dictionary<string, int> data = new Dictionary<string, int>();

    public int this[string index]
    {
        get { return data[index]; }
        set { data[index] = value; }
    }
}
Ⅱ.多维索引器

索引器也可以支持多个索引参数,从而实现多维数组的访问。

Matrix m = new Matrix();
m[1, 2] = 0;
m[1, 3] = 10;
Console.WriteLine($"{m[1,2]},{m[1,3]}");
 
public class Matrix
{
    private int[,] _matrix = new int[3, 4];

    public int this[int row,int col]
    {
        get { return _matrix[row,col]; }
        set { _matrix[row,col] = value; }
    }
}
Ⅲ.索引器重载

索引器重载是指在一个类中定义多个具有不同参数列表的索引器。

Matrix m = new Matrix();

// 使用第一个索引器设置和获取 _count 数组元素
m[5] = 10;
Console.WriteLine(m[5]);

// 使用第二个索引器设置和获取 _name 数组元素
m[1, true] = "ABC";
Console.WriteLine(m[1, true]);

// 使用第三个索引器设置和获取 _name 数组元素
m["1"] = "CCD";
Console.WriteLine(m["1"]);

public class Matrix
{
    private int[] _count = new int[10];
    private string[] _name = new string[10];

    // 重载索引器:使用整数索引访问 _count 数组
    public int this[int index]
    {
        get
        {
            if(index >= 0 || index < _count.Length)
            {
                return _count[index];
            }
            throw new IndexOutOfRangeException();
        }
        set
        {
            if (index >= 0 || index < _count.Length)
            {
                _count[index] = value;
            }
            else
            {
                throw new IndexOutOfRangeException();
            }
        }
    }
    // 重载索引器:使用整数索引访问 _name 数组
    public string this[int index,bool isNameArray]
    {
        get
        {
            if (isNameArray)
            {
                if(index >= 0 || index < _count.Length)
                {
                    return _name[index];
                }
                else
                {
                    throw new IndexOutOfRangeException();
                }
            }
            throw new ArgumentException("无效参数");
        }

        set
        {
            if (isNameArray)
            {
                if(index >= 0 || index < _count.Length)
                {
                    _name[index] = value;
                }
                else
                {
                    throw new IndexOutOfRangeException();
                }
            }
            else
            {
                throw new ArgumentException("无效参数");
            }
        }
    }
    // 重载索引器:使用string索引访问 _name 数组
    public string this[string index]
    {
        get
        {
            if(int.TryParse(index,out int _index) && _index >= 0|| _index < _name.Length)
            {
                return _name[_index];
            }
            throw new ArgumentException("无效的字符串索引或索引超出 _name 数组范围");
        }

        set
        {
            if (int.TryParse(index, out int _index) && _index >= 0 || _index < _name.Length)
            {
                _name[_index] = value;
            }
            else
            {
                throw new ArgumentException("无效的字符串索引或索引超出 _name 数组范围");
            }
        }
    }
}