跳转到内容

C Sharp 只读属性

来自代码酷
Admin留言 | 贡献2025年4月29日 (二) 18:40的版本 (Page creation by admin bot)

(差异) ←上一版本 | 已核准修订 (差异) | 最后版本 (差异) | 下一版本→ (差异)

C#只读属性[编辑 | 编辑源代码]

介绍[编辑 | 编辑源代码]

在C#中,只读属性(Read-only Property)是一种特殊类型的属性,它允许外部代码获取属性的值,但不允许修改该值。只读属性通常用于封装类的内部状态,确保数据的不可变性(immutability),从而提高代码的安全性和可维护性。

只读属性可以通过以下方式实现:

  • 使用get访问器而不提供set访问器
  • 使用init访问器(C# 9.0引入,允许对象初始化期间赋值)
  • 使用readonly字段作为属性支持字段

基本语法[编辑 | 编辑源代码]

以下是只读属性的基本语法:

public class Person
{
    // 只读属性(传统方式)
    public string Name { get; } // 只能在构造函数或声明时初始化
    
    // 使用init访问器(C# 9.0+)
    public int Age { get; init; } // 可在构造函数或对象初始化器中赋值
    
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

详细说明[编辑 | 编辑源代码]

传统只读属性[编辑 | 编辑源代码]

传统只读属性(仅有get访问器)的值只能在构造函数或声明时设置:

public class Circle
{
    public double Radius { get; } // 只读属性
    
    public Circle(double radius)
    {
        Radius = radius; // 只能在构造函数中赋值
    }
    
    public double Area => Math.PI * Radius * Radius; // 计算属性
}

使用init访问器[编辑 | 编辑源代码]

C# 9.0引入的init访问器提供了更灵活的只读属性初始化方式:

public class Product
{
    public string Id { get; init; } // 可在对象初始化器中赋值
    public string Name { get; init; }
    
    // 使用示例:
    // var p = new Product { Id = "P001", Name = "Laptop" };
    // 之后不能再修改Id和Name
}

计算型只读属性[编辑 | 编辑源代码]

只读属性也可以是基于其他字段或属性计算得出的值:

public class Rectangle
{
    public double Width { get; set; }
    public double Height { get; set; }
    
    // 计算型只读属性
    public double Area => Width * Height;
    
    // 使用示例:
    // var rect = new Rectangle { Width = 10, Height = 5 };
    // Console.WriteLine(rect.Area); // 输出50
}

实际应用场景[编辑 | 编辑源代码]

不可变对象[编辑 | 编辑源代码]

只读属性常用于创建不可变对象,这在多线程环境中特别有用:

public class ImmutablePoint
{
    public int X { get; }
    public int Y { get; }
    
    public ImmutablePoint(int x, int y)
    {
        X = x;
        Y = y;
    }
    
    // 方法返回新实例而不是修改现有实例
    public ImmutablePoint Move(int dx, int dy) => new(X + dx, Y + dy);
}

配置对象[编辑 | 编辑源代码]

只读属性适合用于配置对象,确保配置一旦设置就不能被意外修改:

public class AppSettings
{
    public string DbConnectionString { get; init; }
    public int MaxRetryCount { get; init; }
    public bool EnableLogging { get; init; }
    
    // 使用示例:
    // var settings = new AppSettings {
    //     DbConnectionString = "Server=...",
    //     MaxRetryCount = 3,
    //     EnableLogging = true
    // };
}

性能考虑[编辑 | 编辑源代码]

只读属性通常不会引入显著的性能开销。对于简单的属性访问,JIT编译器通常会内联(inline)这些调用。然而,对于计算密集型属性,应考虑缓存结果:

public class ExpensiveCalculation
{
    private double? _cachedResult;
    private readonly double _input;
    
    public double Result
    {
        get
        {
            if (_cachedResult == null)
            {
                // 模拟耗时计算
                _cachedResult = Enumerable.Range(0, 1000000)
                    .Select(i => Math.Pow(_input, i) / Factorial(i))
                    .Sum();
            }
            return _cachedResult.Value;
        }
    }
    
    private static double Factorial(int n) => n <= 1 ? 1 : n * Factorial(n - 1);
    
    public ExpensiveCalculation(double input) => _input = input;
}

与只读字段的比较[编辑 | 编辑源代码]

只读属性与readonly字段有相似之处,但存在重要区别:

特性 只读属性 readonly字段
可以有各种访问修饰符 | 只能通过字段访问控制
可以包含计算逻辑 | 只能是存储的值
可以实现接口属性 | 不能直接实现接口成员
通常被序列化器支持 | 可能需要特殊处理

高级主题[编辑 | 编辑源代码]

只读属性的反射[编辑 | 编辑源代码]

通过反射可以绕过只读属性的限制,但应谨慎使用:

public class ReadOnlyDemo
{
    public string Value { get; } = "Initial";
    
    public static void ModifyReadOnlyProperty()
    {
        var obj = new ReadOnlyDemo();
        Console.WriteLine(obj.Value); // 输出"Initial"
        
        var prop = typeof(ReadOnlyDemo).GetProperty("Value");
        prop.SetValue(obj, "Modified");
        
        Console.WriteLine(obj.Value); // 输出"Modified"
    }
}

只读接口属性[编辑 | 编辑源代码]

接口可以定义只读属性,实现类可以选择如何实现:

public interface IReadOnlyData
{
    string Data { get; } // 只读接口属性
}

public class DataImplementation : IReadOnlyData
{
    public string Data { get; } = "Fixed Data";
}

public class ComputedData : IReadOnlyData
{
    public string Data => DateTime.Now.ToString(); // 每次访问计算新值
}

最佳实践[编辑 | 编辑源代码]

  • 优先使用只读属性而不是公共字段
  • 对于真正的不可变对象,使用只读属性和readonly字段的组合
  • 考虑使用init访问器来提供更灵活的对象初始化方式
  • 对于计算密集型属性,考虑缓存结果
  • 避免通过反射修改只读属性,除非有充分理由

总结[编辑 | 编辑源代码]

C#只读属性是创建安全、不可变对象的重要工具。它们提供了一种封装数据的方式,同时确保数据不会被意外修改。从简单的值封装到复杂的计算属性,只读属性在C#编程中有广泛的应用。随着C#语言的演进(如init访问器的引入),只读属性的使用变得更加灵活和强大。