类型属性(Property)解析

分类索引:C# 语言和运行时剖析--前言

 

属性的定义 


一.概念

  1. 特性:在语义上接近于字段(Field),但在使用上接近于方法的一种类成员。
  2. 使用场景:最常用于简单封装和检查类/对象的数据成员(Field)。 属性也可以被称为智能字段。
  3. 属性的用法:无参属性(默认), 有参属性(索引器).
  4. 注意:属性的最重要作用是封装字段,使字段不暴露在类型外部随意被随意修改。也可以使用方法来达到这一目的,但属性更为轻量级。

 

二.代码示例

internal class Employee
    {
        private string name;
        private Int32 age;

        public Int32 Age
        {
            get
            {
                return this.age;
            }
            set
            {
                if (value < 0)
                    throw new ArgumentOutOfRangeException("value", value.ToString(),
                        "年龄必须大于等于0");
                this.age=value;
            }
        }

        public string Name
        {
            get
            {
                return this.name;
            }
            set
            {
                this.name=value;
            }
        }
    }

 

三.在对象初始化器中使用属性

  • 说明:我们经常在初始化对象时,初始化对象的一些公共属性,可以使用简化的方法在对象构造器中直接初始化属性。
  • 优点:
    • 简化初始化语法。
    • 使代码表达式的上下文不中断,可以连接多个方法,增强了代码的可读性和优美成都。
  • 代码示例:
string s = new Employee() { Age = 20, Name = "Simon" }.ToString().ToUpper();

            //相当于如下操作:
            Employee employee = new Employee();
            employee.Age = 20;
            employee.Name = "Simon";

            string s1 = employee.ToString();
            s1 = s1.ToUpper();

        前一种表达式的表达能力显然更强。

  • 在集合初始化时,也可以使用类似语法,如下:
internal class Employee
    {
        private string name;
        private Int32 age;
        private List<string> jobs;

        public List<string> Jobs
        {
            get
            {
                return this.jobs;
            }
            set
            {
                this.jobs=value;
            }
        }

        public Int32 Age
        {
            get
            {
                return this.age;
            }
            set
            {
                if (value < 0)
                    throw new ArgumentOutOfRangeException("value", value.ToString(),
                        "年龄必须大于等于0");
                this.age=value;
            }
        }

        public string Name
        {
            get
            {
                return this.name;
            }
            set
            {
                this.name=value;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Employee employee = new Employee()
            {
                Age = 20,
                Name = "Simon",
                Jobs = { "Specialist","Supervisor","Manager"}
            };

        }
    }

        编译器将会自动将Jobs属性中字符串匹配到List的Item中。

 

自动实现属性


一.概念

  1. 使用场景:如果只是为了封装一个支持字段而创建一个属性,不需要实现任何逻辑的情况下,C#提供一种更简单的语法,称为自动实现属性。
  2. 原理:此种情况下,C#会自动为你声明一个私有字段。注意,字段不是被省略了,只是不用显示的定义而已。
  3. 示例:如下类型Employee中的Name属性就是一个自动实现属性。
internal class Employee
    {
        private string Name {get;set;}
        private Int32 age;
        private List<string> jobs;

        public List<string> Jobs
        {
            get
            {
                return this.jobs;
            }
            set
            {
                this.jobs=value;
            }
        }

        public Int32 Age
        {
            get
            {
                return this.age;
            }
            set
            {
                if (value < 0)
                    throw new ArgumentOutOfRangeException("value", value.ToString(),
                        "年龄必须大于等于0");
                this.age=value;
            }
        }
    }

二.不适用的场景

  1. 由编译器自动生成的字段往往名称不固定,每次重新编译时,这个字段的名称都有可能改变。所以,具有自动实现属性的类型无法被序列化和反序列化
  2. 自动实现属性,由于不具备任何逻辑,所以无法被调试。
  3. 虽然自动实现属性比较简单实现,但一般还是不推荐使用这个功能。

 

有参属性(索引器)


一.概念

  1. 使用场景:当类型具有集合的特性时,可以使用有参属性。
  2. 使用方法:get访问器方法可以接受一个或者多个参数,set访问器方法接受两个或者多个参数
  3. 优点:如果说属性是一种智能字段,那么有参属性就是一种智能数组。从实践来讲,有参属性并没有提供什么新功能,也是对代码表达能力的一种提升。
  4. 示例:C#使用数组风格的语法来公开有参属性。
public sealed class BitArray
    {
        //因为C#中没有直接的Bit数据类型,所以使用Byte数组保存Bit
        private Byte[] byteArray;
        private Int32 numBits;

        public BitArray(Int32 numBits)
        {
            if (numBits <= 0)
                throw new ArgumentOutOfRangeException("numBits 必须大于0");

            this.numBits = numBits;

            //保证小于8位的bit能被分配到Byte中
            byteArray = new Byte[(numBits) + 7 / 8];
        }

        //索引器定义
        public Boolean this[Int32 bitPos]
        {
            get {
                if ((bitPos < 0) || (bitPos >= numBits))
                    throw new ArgumentOutOfRangeException("bitPos");

                return (byteArray[bitPos / 8] & (1 << (bitPos % 8))) != 0;
            }

            set {
                if ((bitPos < 0) || (bitPos >= numBits))
                    throw new ArgumentOutOfRangeException("bitPos");

                if (value)
                {
                    byteArray[bitPos / 8] = (Byte)(byteArray[bitPos / 8] | 1 << (bitPos % 8));
                }
                else
                {
                    byteArray[bitPos / 8] = (Byte)(byteArray[bitPos / 8] & ~1 << (bitPos % 8));
                }
            }
        }
    }

    class Program
    {
       //调用索引器的get和set演示
        static void Main(string[] args)
        {
            BitArray ba = new BitArray(14);

            for (Int32 x = 0; x < 14; x++)
            {
                ba[x] = (x % 2 == 0);
            }

            for (Int32 x = 0; x < 14; x++)
            { 
                Console.WriteLine("Bit" + x + " is " + (ba[x] ? "On" : "Off"));
            }
        }
    }

 

二.实践注意

  1. C#只支持对象的索引器,不支持静态的索引器。
  2. C#可以重载索引器,但是重载的索引器必须使用不同类型的形参

 

 

属性使用最佳实践讨论


一.属性具有方法特性

  1. 相同点:
    • 属性可以抛出异常
    • 可以使用虚属性或者抽象属性(对应虚方法和抽象方法)
    • 可以在接口中使用属性,使实现接口的类型必须实现属性
  2. 不同点:
    • 属性只能使用get/set语句
    • 属性不应该处理较为复杂的运算逻辑,不利于数据字段的线程同步。

 

二.属性在语义上接近字段

  1. 属性的主要应用场景应该是对类型字段和数据状态的封装,检查,简单计算等,不应该滥用于其他场景。
posted @ 2012-12-16 14:37  simon_developer  阅读(3225)  评论(0编辑  收藏  举报