深入理解c#的字段与属性

属性

属性的作用就是保护字段,对字段的赋值和取值进行限定

属性的本质就是两个方法,一个叫get()对取值进行限定,一个叫set()对存值进行限定,属性只是对属性的再赋值。

如果只有get是只读属性,set是只写属性。在get里面用的值是字段的值,set里面用的是value的值

所以在类里面的public 方法要访问私有字段,尽量访问属性,虽然这样可能会降低访问的效率,但是一方面会更安全地访问字段,另一方面可能是必须经过属性里的某个方法的处理才能得到这个字段的值。

代码风格

下面的例子展示了使用属性保护字段的过程,并且使用了异常处理来处理异常,并把错误信息返回而不是显示出来,这样体现了界面与业务相分离,也就是这个Person类既可以用在字符界面下,也可以用在GUI界面下,就用复用性。

这里把构造函数设置成私有的是因为,构造函数没有返回值,所以总会创建一个对象,而在Name属性的set方法里面抛出一个异常在构造函数里并不方便处理,所以把这个处理工作放在了GetAPerson这个Person类的静态方法里,这是外界创建Person类的唯一接口,也可以通过这种方法实现单例程序。

另外这个程序还是用了c#的异常处理机制,实在是方便(相比c++而言啦),Message是Exception的一个属性。

c#源代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 属性与字段
{
    class Program
    {
        static void Main(string[] args)
        {
            string result;
            Person p = Person.GetAPerson("", out result);
            if (p==null)
            {
                Console.WriteLine(result);
            }
            Console.ReadKey();
        }
    }
    class Person
    {
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {

                    if (value == "")
                    {
                        throw new Exception("名字不能为空");
                    }
                    else
                    {
                        _name = value;
                    }

            }
        }
        private Person(String name)
        {
            Name = name;
        }
        /// <summary>
        /// 外界获得一个Person对象的唯一借口;
        /// </summary>
        /// <param name="name">Person的名字</param>
        /// <param name="result">如果创建对象成功,result存储"成功",失败则result存储失败的原因</param>
        /// <returns>返回创建的对象,如果对象创建失败返回NULL</returns>
        public static Person GetAPerson(string name,out string result)
        {
            Person tmp;
            try
            {
                tmp = new Person(name);
                result = "成功";
                return tmp;
            }
            catch(Exception e)
            {

                result =e.Message;//Message是一个属性;
                //Console.WriteLine("抓住了");
                return null;
            }
        }
    }
}

IL中间代码

我们我们通过reflector来分析一下这个代码的中间代码;

set方法

其中set方法如果转换为IL中间代码则变为

.method public hidebysig specialname instance void set_Name(string ‘value‘) cil managed
{
    // token: 06000002
    .maxstack 2         //表示程序栈有两个变量
    .locals init (              //定义一个bool类型变量,压入call stack里面
        [0] bool CS$4$0000)
    L_0000: nop  // 00          //什么也不做
    L_0001: ldarg.1  // 03      //把第一个形参拿进来,就是value
    L_0002: ldstr "" // 7201000070
    L_0007: call bool [mscorlib]System.String::op_Equality(string, string) // 281200000A
    L_000c: ldc.i4.0  // 16
    L_000d: ceq  // FE01
    L_000f: stloc.0  // 0A
    L_0010: ldloc.0  // 06
    L_0011: brtrue.s L_001f // 2D0C
    L_0013: nop  // 00
    L_0014: ldstr "\u540d\u5b57\u4e0d\u80fd\u4e3a\u7a7a" // 7203000070
    L_0019: newobj instance void [mscorlib]System.Exception::.ctor(string) // 731300000A
    L_001e: throw  // 7A
    L_001f: nop  // 00
    L_0020: ldarg.0  // 02
    L_0021: ldarg.1  // 03
    L_0022: stfld string 属性与字段.Person::_name // 7D04000004
    L_0027: nop  // 00
    L_0028: ret  // 2A
}

我们可以分析几句重要的,其它的我都做了注释

if (value == "")
{
     throw new Exception("名字不能为空");
}
else
{
     _name = value;
}
    L_0001: ldarg.1  // 03      //把第一个形参拿进来,就是value
    L_0002: ldstr "" // 7201000070
    L_0007: call bool [mscorlib]System.String::op_Equality(string, string) // 281200000A
    L_000c: ldc.i4.0  // 16
    L_000d: ceq  // FE01

ldarg.1把形参value拿过来,ldstr “”找一个字符串”“的引用拿过来,然后调用op_Equality方法比较两者一样不,ldc.i4.0表示表达式的值是这个0先压入到栈里,为了方便一会儿跳转判断用,ceq如果比较结果为真,则产生一个值为1的int变量,如果结果为假,产生一个值为0 的变量;把这个变量压入刚才的0上面。


    L_000f: stloc.0  // 0A
    L_0010: ldloc.0  // 06
    L_0011: brtrue.s L_001f // 2D0C
    L_0013: nop  // 00
    L_0014: ldstr "\u540d\u5b57\u4e0d\u80fd\u4e3a\u7a7a" // 7203000070
    L_0019: newobj instance void [mscorlib]System.Exception::.ctor(string) // 731300000A
    L_001e: throw  // 7A
    L_001f: nop  // 00
    L_0020: ldarg.0  // 02
    L_0021: ldarg.1  // 03
    L_0022: stfld string 属性与字段.Person::_name // 7D04000004

stloc.0 和ldloc.0是刚才比较的结果产生的那个int值从栈里弹出来,

brtrue.s L_001f 讲如果栈顶是1就运行到L_001f ,很明显刚才1弹出来之后,栈顶是0(上面ldc.i4.0的作用),则继续往下运行,先加载一个静态字符串”\u540d\u5b57\u4e0d\u80fd\u4e3a\u7a7a”,就是我们为Exception传的实参”名字不能为空”的unicode编码,可以在Unicode转换中文工具这个转换,然后调用Exception的构造函数,在然后调用throw跳转。

GetAPerson方法

下面是GetAPerson方法的部分IL中间代码

    L_0002: ldarg.0  // 02
    L_0003: newobj instance void 属性与字段.Person::.ctor(string) // 7303000006
    L_0008: stloc.0  // 0A
    L_0009: ldarg.1  // 03
    L_000a: ldstr "\u6210\u529f" // 7211000070
    L_000f: stind.ref  // 51
    L_0010: ldloc.0  // 06
    L_0011: stloc.2  // 0C
    L_0012: leave.s L_0022 // DE0E
    L_0014: stloc.1  // 0B
    L_0015: nop  // 00
    L_0016: ldarg.1  // 03
    L_0017: ldloc.1  // 07
    L_0018: callvirt instance string [mscorlib]System.Exception::get_Message() // 6F1500000A
    L_001d: stind.ref  // 51
    L_001e: ldnull  // 14
    L_001f: stloc.2  // 0C
    L_0020: leave.s L_0022 // DE00
    L_0022: nop  // 00
    L_0023: ldloc.2  // 08
    L_0024: ret  // 2A
.try L_0001 to L_0014 catch [mscorlib]System.Exception handler L_0014 to L_0022

最后一句.try L_0001 to L_0014 catch [mscorlib]System.Exception handler L_0014 to L_0022表明如果catch到异常就执行L_0014 to L_0022的代码

reflector技能

可以把exe反编译成c#,VB,IL中间代码等。

当你看一个方法看不到的时候,可以先转换为IL中间代码。

时间: 2024-11-08 11:53:35

深入理解c#的字段与属性的相关文章

字段和属性的区别

可以理解为字段是类内部用的,属性是类外部用的. 下面是个C#写的例子,这个例子作用是可以通过属性获取时间值,但是只能通过SetTime()方法设定时间值,我想这也是为什么要分字段和属性的一大原因吧. 首先声明的是private类型的变量(字段)hours,通常字段写法都是加个"_"符号,然后声明只读属性hours. 在类内部可以通过变量(字段)对其进行读写,在类外部不能访问变量(字段),只能访问属性.而且在此例内如果想修改时间值,只能通过调用SetTime()方法往里传值(因为这里的各

[.net 面向对象编程基础] (9) 类的成员(字段、属性、方法)

[.net 面向对象编程基础] (9) 类的成员(字段.属性.方法) 前面定义的Person的类,里面的成员包括:字段.属性.方法.事件等,此外,前面说的嵌套类也是类的成员. a.类的成员为分:静态成员(static)和非静态成员 b.静态成员用static标识,不标识则默认为非静态成员 c.静态成员属于类所有,动态成员则属于实例所有,即对象 d.静态成员为类所有实例共享,无论类有多少实例或副本,静态成员只占用存中一块区域.非静态成员则在类的每个实例,都创建一个内存域. 下面主要说明一下类的主要

字段和属性

刚到公司,坐在座位上看昨天打印的单例模式:其中分不清字段和属性的区别了,现在来复习一波 属性和字段的区别 在C#中,我们可以自由的访问公有字段,但在一些场合中,我们可能希望限制字段的赋值范围.或是要求字段只能读或只能写,或是在改变字段时能改变对象的其他一些字段值,这些单靠字段是无法做到的, 于是就有了属性,属性中包含两个块:set和get,set块负责属  性的写入工作,get块负责属性的读取工作. get和set 有两个类person: public class person//这样定义的是字

在netcore中实现字段和属性注入

简单来说,使用Ioc模式需要两个步骤,第一是把服务注册到容器中,第二是从容器中获取服务,我们一个一个讨论并演化.这里不会考虑使用如Autofac等第三方的容器来代替默认容器,只是提供一些简单实用的小方法用于简化应用层的开发. 将服务注入到容器 asp.netcore官方给出的在容器中注册服务方法是,要在Startup类的ConfigureServices方法中添加服务,如下所示: public void ConfigureServices(IServiceCollection services)

【JVM虚拟机】(8)--深入理解Class中--方法、属性表集合

#[JVM虚拟机](8)--深入理解Class中--方法.属性表集合 之前有关class文件已经写了两篇博客: 1.[JVM虚拟机](5)---深入理解JVM-Class中常量池 2.[JVM虚拟机](6)---深入理解Class中访问标志.类索引.父类索引.接口索引 3.[JVM虚拟机](7)---深入理解Class中-属性集合 那么这篇博客主要讲有关 方法表集合 相关的理解和代码示例. 方法表集合: 告知该方法是什么修饰符修饰?是否有方法值?返回类型是什么?方法名称,方法参数,还有就是方法内

C# 字段、属性、成员变量 [转载]

一.定义与作用 1.字段(field):是C#类级别定义的,和方法同一级别. 一般用来类内部进行访问,充当一种类中的"全局变量"角色:或者配合属性来使用 2.属性:同样是C#类级别定义的,一般是供外部类访问的. 3.成员变量:"全局变量",在类中定义的变量,区别于在方法中定义的局部变量.它与字段属性不是同一级别概念,字段,属性都可以叫做成员变量. 二.使用 class Basic { private string FieldVar;//这是字段,在当前类中调用 pr

字段与属性(c#)

对实现者来说像方法 对调用者来说像字段 可以将属性看成一种智能字段,属性就是字段与方法的扩展. 面向对象的一个原则就是数据封装,所以不能将字段以公有方式提供给外界. 就像上面例子不能给年龄赋负数,而不使用属性要给外界提供私有字段就得写两个get set 方法,用属性的话虽然使类型定义变得复杂了,但代码写起来却改善了许多.

深入理解css中的定位属性:position

深入理解css中的定位属性:position 在网页设计中,position属性的使用是非常重要的.有时如果不能认识清楚这个属性,将会给我们带来很多意想不到的困难. position属性共有四种不同的定位方法,分别是static.fixed.relative.absolute. 第一部分:static static定位是HTML元素的默认值,即没有定位,元素出现在正常的流中,因此,这种定位就不会收到top,bottom,left,right的影响. 如html代码如下: <div class=&qu

C#:字段与属性

MSDN中是这么介绍字段和属性的: A field is a variable of any type that is declared directly in a class or struct. 字段:“字段”是直接在类或结构中声明的任何类型的变量. A property is a member that provides a flexible mechanism to read, write, or compute the value of a private field.Properti