C#--属性详解

本章讨论属性,它允许源代码用简化语法来调用方法。CLR支持两种属性:无参属性 有参属性。在C#中称有参属性索引器

无参属性

面向对象设计和编程的重要原则之一就是数据封装,意味着类型的字段永远不应该公开,否则很容易因为不恰当的使用字段而破坏对象的状态。
下面这种使用方式是不好的:

public sealed class Employee{
    public String Name;
    public Int32 Age
}

Employee e = new Employee();
e.Name = "Jeffrey Richter";
e.Age = 45;
//下面这种方式很容易就破坏了对象的状态
e.Age = -5;

基于以上原因,强烈建议将所有字段都设为private.要允许用户或类型获取或设置状态信息就公开一个针对该用途的方法。封装了字段访问的方法通常称为访问器方法。

比如下面这样使用属性:

private sealed class Employee {
    private String m_Name; // prepended 'm_' to avoid conflict
    private Int32 m_Age;  // prepended 'm_' to avoid conflict

    public String Name {
        get { return (m_Name); }
        set { m_Name = value; } // 'value' identifies new value
    }

    public Int32 Age {
        get { return (m_Age); }
        set {
            if (value <= 0)    // 'value' identifies new value
                throw new ArgumentOutOfRangeException("value", "must be >0");
            m_Age = value;
        }
    }
}

emp.Name = "Jeffrey Richter";
emp.Age = 45;	   // Updates the age
Console.WriteLine("Employee info: Name = {0}, Age = {1}", emp.Name, emp.Age);

e.Age = -5; // 会抛出`ArgumentOutOfRangeException`异常
Int32 EmployeeAge = e.Age ; 

注意:我相信大部分的初学者在开始之初都讲不出字段属性的区别,现在我可以稍微尝试着说下了。字段经常用来封装一些不经常修改的内容,建议将字段设为private,而属性一定要将取值get设值set想象成两个方法,虽然C#简化了这种语法,但编译器在后台仍然会在指定的属性名之前添加set_get_前缀来生成方法名,更适合用来封装那些可能发生变化的内容。可以将属性看成智能字段,可以设置只读属性只写属性

自动实现的属性

public String Name {get;set;}

声明属性而不提供get/set方法的实现,C#会自动为你声明一个私有字段。实现get/set方法。

合理定义属性

  • 属性可以只读或只写,字段访问总是可读或可写的(readonly字段在构造器中可写)。如果定义属性,最好同时位它提供getset访问方法。
  • 属性方法可抛出异常,字段不会
  • 属性不能作为outref参数传递给方法
  • 属性可能花较长时间完成,字段访问总是立即完成
  • 连续多次访问,属性方法可能每次返回不同的值,而字段每次都是相同的值

对象和集合初始化器

对象初始化语法:

Employee e = new Employee(){ Name = "Jeff" , Age = 45 };

如果想调用的本来就是一个无参构造器,C#还允许省略括号

下面演示集合初始化器,下面构造一个ClassRoom对象,并初始化Students集合

Classroom classroom = new Classroom{
    Student = { "Jeff" , "Kristin" , "Aidan"}
};

使用起来还是非常方便的,编译器在后台默认调用集合的Add方法,把对象添加到集合中。

匿名类型

利用C#的匿名类型功能,可以很简洁的语法来自动声明不可变的元组类型。

 var o1 = new { Name = "Jeff", Year = 1964 };

 // Display the properties on the console:
 Console.WriteLine("Name={0}, Year={1}", o1.Name, o1.Year);

匿名类型经常与LINQ联合使用

有参属性

在C#中有参属性被称为索引器。C#是用数组风格的语法来公开有参属性,换句话说索引器可以看成是C#对[]操作符的重载

下面演示一个BitArray类,它允许哦那个数组风格的语法来索引一组二进制位

internal sealed class BitArray {
//容纳了一个二进制位的私有字节数组
// Private array of bytes that hold the bits
private Byte[] m_byteArray;
private Int32 m_numBits;

// Constructor that allocates the byte array and sets all bits to 0
//分配字节数组,并将所有为初始为0
public BitArray(Int32 numBits) {
    // Validate arguments first.
    if (numBits <= 0)
        throw new ArgumentOutOfRangeException("numBits must be > 0");

    // Save the number of bits.
    m_numBits = numBits;

    // Allocate the bytes for the bit array.
    m_byteArray = new Byte[(m_numBits + 7) / 8];
}


// This is the indexer.
// 这是索引器
public Boolean this[Int32 bitPos] {
    // 这是索引器的get方法
    get {
        // Validate arguments first
        if ((bitPos < 0) || (bitPos >= m_numBits))
            throw new ArgumentOutOfRangeException("bitPos", "bitPos must be between 0 and " + m_numBits);

        // Return the state of the indexed bit.
        return ((m_byteArray[bitPos / 8] & (1 << (bitPos % 8))) != 0);
    }

    // 索引器的set访问器方法
    set {
        if ((bitPos < 0) || (bitPos >= m_numBits))
            throw new ArgumentOutOfRangeException("bitPos", "bitPos must be between 0 and " + m_numBits);

        if (value) {
            // Turn the indexed bit on.
            m_byteArray[bitPos / 8] = (Byte)
               (m_byteArray[bitPos / 8] | (1 << (bitPos % 8)));
        } else {
            // Turn the indexed bit off.
            m_byteArray[bitPos / 8] = (Byte)
               (m_byteArray[bitPos / 8] & ~(1 << (bitPos % 8)));
        }
    }
}
}


private static void BitArrayTest() {
    // Allocate a BitArray that can hold 14 bits.
    // 构造一个数组
    BitArray ba = new BitArray(14);

    // Turn all the even-numbered bits on by calling the set accessor.
    for (Int32 x = 0; x < 14; x++) {
        //这里实际是调用的set方法,将偶数为设为true
        ba[x] = (x % 2 == 0);
    }

    // Show the state of all the bits by calling the get accessor.
    for (Int32 x = 0; x < 14; x++) {
        // 这里调用了get方法,显示状态。
        Console.WriteLine("Bit " + x + " is " + (ba[x] ? "On" : "Off"));
    }
}

C#使用this[...]作为表达索引器的语法,如上面的

public Boolean this[Int32 bitpos]

代表set时,传入一个index的索引,get时返回一个Boolean类型


总结

今天的内容还是比较有趣的, 搞懂了一直以来困惑的字段属性的区别。复习了对象初始化器、集合初始化器、匿名类型等等,最后学习了下有参属性即索引器。以前在做题的时候遇到过,现在也清楚多了。接下来到了事件了哈哈,做个小栗子一起学习下吧。

posted on 2015-10-27 10:52  kuiblog.com  阅读(531)  评论(0编辑  收藏  举报