>> >> >> Reference << << << <<<<<<Ref>>>>>>
>> >> >> Indexer << << << <<<<<<Idx>>>>>>
Matched: 0

Tags

    Categories

      Types

        Top Results

          Property
          M: 2025-06-01 - ljf12825

          属性是提供了对字段的安全访问机制,是面向对象编程中封装性的重要体现

          定义

          属性(Property)是对字段(Field)的封装,提供了受控的访问方式
          简单说:字段是数据,属性是访问字段的接口

          • 字段可以直接被外部访问(不安全)
          • 属性可以控制读写逻辑(安全、可扩展)

          基本语法

          完整的属性写法

          private string _name; // 私有字段
          
          public string Name; // 公有属性
          {
              get { return _name; } // 读取访问器
              set { _name = value; } // 写入访问器
          }
          

          示例

          class Person
          {
              private int _age;
          
              public int Age;
              {
                  get { return _age; }
                  set 
                  {
                      if (value >= 0 && value <= 150)
                          _age = value;
                      else
                          throw new ArgumentException("年龄必须在0~150岁之间")
                  }
              }
          }
          
          Person p = new Person();
          p.Age = 25; // 调用set访问器
          Console.WriteLine(p.Age); // 调用get访问器
          

          自动实现属性(Auto-Implemented Property)

          C#为了简化代码,允许不用手动写字段

          class Person
          {
              public string Name { get; set; } // 自动生成私有字段
          }
          

          等价于

          private string _name;
          public string Name
          {
              get { return _name; }
              set { name = value; }
          }
          

          只读、只写

          • 只读属性
          public string Name { get; } // 外部只能读
          
          • 只写属性
          private int _age;
          public int Age { set { age = value; } } // 外部只能写
          

          属性中的逻辑控制

          属性不仅是字段访问的包装,它还可以嵌入逻辑

          private int _age;
          public int Age
          {
              get { return age; }
              set 
              {
                  if (value < 0)
                      throw new ArgumentException("年龄不能为负数");
                  age = value;
              }
          }
          

          优点:

          • 可以做验证
          • 可以触发事件
          • 可以延迟计算(懒加载)

          表达式体属性(Expression-Bodied Property)

          C# 6+ 提供了简化写法

          public string FullName => FirstName + " " + LastName; // 只读属性
          public int Age { get => _age; set => age = value; } // 读写属性
          

          静态属性

          属性也可以是静态的

          class Counter
          {
              private static int _count;
              public static int Count
              {
                  get { return count; }
                  set { count = value; }
              }
          }
          

          属性 vs 方法

          • 访问语义:obj.Prop更像字段访问,而obj.Method()是调用方法
          • 属性适合快速获取、可能有简单逻辑的值
          • 方法适合复杂计算、可能消耗性能的操作

          错误示例

          public int ComputeValue { get { return ExpensiveCalculation(); } }
          

          这是应使用方法而不是属性,否则会误导使用者以为是简单读取

          索引器(Indexer)

          可以像访问数组一样访问对象

          class MyList
          {
              private int[] _data = new int[100];
              public int this[int index]
              {
                  get { return data[index]; }
                  set { data[index] = value; }
              }
          }
          

          init访问器

          C# 9+ 支持,只允许初始化时设置属性的值;用来增强“不可变对象(immutable object)”的设计能力

          class Person
          {
              public string Name { get; init; }
              public int Age { get; init; }
          }
          

          使用

          var p = new Person
          {
              Name = "Alice",
              Age = 25
          };
          
          // p.Age = 30; // 编译错误:只能在对象初始化时赋值
          

          record的完美结合

          C# 9 引入的record类型天生和init绑定,用于不可变数据模型

          public record Player
          {
              public string Name { get; init; }
              public int Level { get; init; }
          }
          

          或缩写

          public record Player(string Name, int Level);
          

          使用

          var p1 = new Player("Alice", 10);
          var p2 = p1 with { Level = 20 }; // 复制修改
          

          init的实现机制(底层原理)

          • init本质上时一个set,但带有initonly修饰符
          • 编译器限制:只能在构造函数或对象初始化器中调用它

          用反编译工具(如ILSpy)看,会看到它生成的set方法带[IsExternalInit]属性

          抽象属性

          抽象类可以拥有抽象属性,这些属性应在派生类中被实现

          public abstract class Person
          {
              public abstract string Name { get; set; }
              public abstract int Age { get; set; }
          }
          
          class Student : Person
          {
              public string Code { get; set; } = "N.A"; // 默认值 C# 6.0+ 支持
              public override string Name { get; set; } = "N.A";
              public override int Age { get; set; } = 0;
          }
          

          访问修饰符

          可以给get和set分别设置访问级别

          public class BankAccount
          {
              private decimal _balance;
          
              public decimal Balance
              {
                  get { return _balance; }
                  private set { _balance = value; } // 只能在类内部修改
              }
          
              public BankAccount(string accountNumber, decimal initialBalance)
              {
                  AccountNumber = accountNumber;
                  Balance = initialBalance;
              }
          
              public void Deposit(decimal amount)
              {
                  if (amount > 0)
                      Balance += amount; // 类内部可以修改
              }
          }
          

          计算属性 vs 缓存属性

          计算属性(每次访问都计算)

          public class Rectangle
          {
              public double Width { get; set; }
              public double Height { get; set; }
          
              public double Area => Width * Height; // 每次访问都计算
          }
          

          缓存属性(计算一次并缓存)

          public class Rectangle
          {
              private double? _area;
              private double _width;
              private double _height;
          
              public double Width
              {
                  get => _width;
                  set 
                  {
                      _width = value;
                      _area = null; // 宽度改变时清除缓存
                  }
              }
          
              public double Height
              {
                  get => _height;
                  set
                  {
                      _height = value;
                      _area = null; // 高度改变时清除缓存
                  }
              }
          
              public duoble Area
              {
                  get
                  {
                      if (_area == null)
                          _area = Width * Height;
                      return _area.Value;
                  }
              }
          }
          

          value

          value是C#属性和索引器中的一个隐式关键字
          在属性的setinit访问器中,value表示“外部赋进来的值”

          private int age;
          
          public int Age
          {
              get { return age; }
              set { age = value; }
          }
          

          当你写

          person.Age = 20;
          

          实际上编译器会做

          person.set_Age(20);
          

          此时value = 20
          set块里的value就是调用者赋给属性的那个值

          value是隐式的参数

          在编译器层面,属性的set被视作一个带一个参数的函数

          void set_Age(int value) => age = value;
          

          你不能声明它,也不能修改它的名字,value是一个固定的、隐式存在的形参

          实际用途示例

          用来做验证

          private int age;
          
          public int Age
          {
              get { return age; }
              set
              {
                  if (value < 0)
                      throw new ArgumentException("年龄不能为负数");
                  age = value;
              }
          }
          

          这里的value就是外部传进来的值

          用来做转换

          private string name;
          
          public string Name
          {
              get => name;
              set => name = value.Trim(); // 自动去掉空格
          }
          

          init中同样存在

          public string Name { get; init; } = "Default";
          

          如果改成显式写法

          private string _name;
          public string Name
          {
              get => _name;
              init => _name = value; // 仍然用 value
          }
          

          索引器中的value

          在索引器(indexer)中value同样出现

          class MyList
          {
              private int[] data = new int[10];
          
              public int this[int index]
              {
                  get => data[index];
                  set => data[index] = value; // 外部传来的赋值内容
              }
          }
          

          调用

          list[3] = 42; // => 调用 set 索引器,value = 42
          

          注意事项

          • value只能在setinit块中使用
          • get中使用会编译错误
          • 在表达式体属性中,仍然可以使用
          set => field = value;
          
          • 不能改名,也不能声明自己的value变量

          IL机制

          如果反编译

          public int Age { get; set; }
          

          可以看到两个方法

          public int get_Age() { ... }
          public void set_Age(int value) { ... }
          

          也就是说

          • get -> 无参数方法,返回字段
          • set -> 带一个参数(就是value

          这就是C#的属性语法糖的底层实现