当前位置: 首页 > news >正文

readonly vs. const [C#] .net

导读:


       今天在编程的时候,发现了一个知识点,就是 readonly .. 后来看了些文章,终于知道我在常量类中写成这样为什么就是错误的,一直 很奇怪,这和JAVA是有些不一样. 记的我以前在类的声明级别是这样写的 public static const  string  My_Var  = "values";  这个无论如何都编译不过去后来才明白缘由,汗啊 !


关于C#中的const和readonly想必使用C# .NET开发的朋友都很了解吧?总结一下const和readonly也就这么几条吧:



  • const和readonly的值一旦初始化则都不再可以改写; 
  • const只能在声明时初始化;readonly既可以在声明时初始化也可以在构造器中初始化; 
  • const隐含static,不可以再写static const;readonly则不默认static,如需要可以写static readonly; 
  • const是编译期静态解析的常量(因此其表达式必须在编译时就可以求值);readonly则是运行期动态解析的常量; 
  • const既可用来修饰类中的成员,也可修饰函数体内的局部变量;readonly只可以用于修饰类中的成员(UPDATED:谢谢MicroHelper的提醒!)。


前面几条也没什么可说的,不过关于这第4条,里面还是有些文章可做的。试试下面的例子,看看是否与你所想一致吧!

0. 常量与静态只读变量类库(文件名Consts.cs)



复制   保存

public class Consts

{
public const string Const = "const";
public static readonly string Readonly = "readonly";
}

------------------------------------------------------------------------------------


另外的一篇文章.呵呵



  readonly vs. const [C#]
  Updated on Friday, October 29, 2004
  Written by Allen Lee
  Features:
  readonly和const都是用来标识常量的[1]。
  const可用于修饰class的field或者一个局部变量(local variable);而readonly仅仅用于修饰class的field。
  const常量的值必定在编译时就已明确并且恒定的;而readonly常量却有一点不同,那就是其值可以在运行时编译,当然,它也必须遵守作为常量的约束,那就是值必须恒定不变。
  const常量必须在声明的同时对其进行赋值,并且确保该值在编译时可确定并恒定;而readonly常量则可以根据情况选择在声明的同时对其赋予一个编译时确定并恒定的值,或者将其值的初始化工作交给实例构造函数(instant constructor)完成。如:public readonly stringm_Now = DateTime.Now.ToString();,m_Now会随着运行时实际情况变化而变化。
  const常量属于类级别(class level)而不是实例对象级别(instant object level),并且它不能跟static结合一起使用,该常量的值将由整个类的所有实例对象共同分享(详细论述参见后面的Remark区域)。
  readonly常量既可以是类级别也可以是实例对象级别的,这取决于它的声明以及初始化工作怎么实施。readonly可以与static结合使用,用于指定该常量属于类级别,并且把初始化工作交由静态构造函数(static constructor)完成(有关如何把readonly常量声明为类级别或实例对象级别的论述清参见后面的Remark区域)。
  能被const修饰声明为常量的类型必须是以下的基元类型(primitive type):sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, float, bool, decimal, string。
  object, 数组(Array)和结构(struct)不能被声明为const常量。
  一般情况下,引用类型是不能被声明为const常量的,不过有一个例外:string。该引用类型const常量的值可以有两种情况,string或null。其实,string虽然是引用类型,但是.NET却对它特别处理,这种处理叫做字符串恒定性(immutable),使得string的值具有只读特性。有关字符串恒定性的内容,可以参考《Microsoft .NET框架程序设计(修订版)》。
  Examples:
  
  
  using?System;
  
  
  
  public?class?Order
  
  
  
  
  
  {
  
  
  public?Order()
  
  
  
  
  
  
  {
  
  
  Guid?guid?=?Guid.NewGuid();
  
  
  ID?=?guid.ToString("D");
  
  
  }
  
  
  
  //?对于每一份订单,其订单序号都是实时确定的常量。
  
  public?readonly?string?ID;
  
  
  
  
  public?override?string?ToString()
  
  
  
  
  
  
  {
  
  
  return?"Order?ID:?"?+?ID;
  
  
  }
  
  }
  Explaintion:
  如果结合数据库使用,ID field通常都会都会与某个表的主健(primary key)关联起来,如Orders表的OrderID。
  数据库的主健通常采用以下三种方式:
  自动递增值。你可以通过把DataColumn.AutoIncrement设定为true值来激活自动递增特性。
  唯一名称。这个是使用自己定义的算法来生成一个唯一序列号。
  GUID(全局唯一标识符)。你可以通过System.Guid结构来生成GUID,如上例。
  
  
  using?System;
  
  
  
  class?Customer
  
  
  
  
  
  {
  
  
  public?Customer(string?name,?int?kind)
  
  
  
  
  
  
  {
  
  
  m_Name?=?name;
  
  
  m_Kind?=?kind;
  
  
  }
  
  
  
  public?const?int?NORMAL?=?0
  
  public?const?int?VIP?=?1
  
  public?const?int?SUPER_VIP?=?2
  
  
  
  private?string?m_Name;
  
  
  public?string?Name
  
  
  
  
  
  
  {
  
  
  
  
  get?
  
  {?return?m_Name;?}
  
  }
  
  
  
  private?readonly?int?m_Kind;
  
  
  public?int?Kind
  
  
  
  
  
  
  {
  
  
  
  
  get?
  
  {?return?m_Kind;?}
  
  }
  
  
  
  public?override?string?ToString()
  
  
  
  
  
  
  {
  
  
  if(m_Kind?==?SUPER_VIP)
  
  
  return?"Name:?"?+?m_Name?+?"[SuperVip]"
  
  else?if(m_Kind?==?VIP)
  
  
  return?"Name:?"?+?m_Name?+?"[Vip]"
  
  else
  
  return?"Name:?"?+?m_Name?+?"[Normal]"
  
  }
  
  }
  Remarks:
  一般情况下,如果你需要声明的常量是普遍公认的并作为单个使用,例如圆周率,黄金分割比例等。你可以考虑使用const常量,如:public const doublePI = 3.1415926;。如果你需要声明常量,不过这个常量会随着实际的运行情况而决定,那么,readonly常量将会是一个不错的选择,例如上面第一个例子的订单号Order.ID。
  另外,如果要表示对象内部的默认值的话,而这类值通常是常量性质的,那么也可以考虑const。更多时候我们对源代码进行重构时(使用Replace Magic Number with Symbolic Constant),要去除魔数(Magic Number)的影响都会借助于const的这种特性。
  对于readonly和const所修饰的变量究竟是属于类级别的还是实例对象级别的问题,我们先看看如下代码:
  
  
  using?System;
  
  
  
  namespace?ConstantLab
  
  
  
  
  
  {
  
  
  class?Program
  
  
  
  
  
  
  {
  
  
  static?void?Main(string[]?args)
  
  
  
  
  
  
  {
  
  
  Constant?c?=?new?Constant(3);
  
  
  Console.WriteLine("ConstInt?=?"?+?Constant.ConstInt.ToString());
  
  
  Console.WriteLine("ReadonlyInt?=?"?+?c.ReadonlyInt.ToString());
  
  
  Console.WriteLine("InstantReadonlyInt?=?"?+?c.InstantReadonlyInt.ToString());
  
  
  Console.WriteLine("StaticReadonlyInt?=?"?+?Constant.StaticReadonlyInt.ToString());
  
  
  
  
  Console.WriteLine("Press?any?key?to?continue");
  
  
  Console.ReadLine();
  
  
  }
  
  }
  
  
  
  class?Constant
  
  
  
  
  
  
  {
  
  
  public?Constant(int?instantReadonlyInt)
  
  
  
  
  
  
  {
  
  
  InstantReadonlyInt?=?instantReadonlyInt;
  
  
  }
  
  
  
  public?const?int?ConstInt?=?0
  
  
  
  public?readonly?int?ReadonlyInt?=?1
  
  
  
  public?readonly?int?InstantReadonlyInt;
  
  
  
  
  public?static?readonly?int?StaticReadonlyInt?=?4
  
  }
  
  }
  使用Visual C#在Main()里面使用IntelliSence插入Constant的相关field的时候,发现ReadonlyInt和InstantReadonlyInt需要指定Constant的实例对象;而ConstInt和StaticReadonlyInt却要指定Constant class(参见上面代码)。可见,用const或者static readonly修饰的常量是属于类级别的;而readonly修饰的,无论是直接通过赋值来初始化或者在实例构造函数里初始化,都属于实例对象级别。
  一般情况下,如果你需要表达一组相关的编译时确定常量,你可以考虑使用枚举类型(enum),而不是把多个const常量直接嵌入到class中作为field,不过这两种方式没有绝对的孰优孰劣之分。
  
  
  using?System;
  
  
  
  enum?CustomerKind
  
  
  
  
  
  {
  
  
  SuperVip,
  
  
  Vip,
  
  
  Normal
  
  
  }
  
  
  
  class?Customer
  
  
  
  
  
  {
  
  
  public?Customer(string?name,?CustomerKind?kind)
  
  
  
  
  
  
  {
  
  
  m_Name?=?name;
  
  
  m_Kind?=?kind;
  
  
  }
  
  
  
  private?string?m_Name;
  
  
  public?string?Name
  
  
  
  
  
  
  {
  
  
  
  
  get?
  
  {?return?m_Name;?}
  
  }
  
  
  
  private?CustomerKind?m_Kind;
  
  
  public?CustomerKind?Kind
  
  
  
  
  
  
  {
  
  
  
  
  get?
  
  {?return?m_Kind;?}
  
  }
  
  
  
  public?override?string?ToString()
  
  
  
  
  
  
  {
  
  
  return?"Name:?"?+?m_Name?+?"["?+?m_Kind.ToString()?+?"]"
  
  }
  
  }
  然而,当这种结合使用枚举和条件判断的代码阻碍了你进行更灵活的扩展,并有可能导致日后的维护成本增加,你可以代之以多态,使用Replace Conditional with Polymorphism来对代码进行重构。(有关多态的详细介绍,请参见《今天你多态了吗?》一文。)
  Comments:
  readonly field准确来说应该翻译成为“只读域”,这里是为了统一翻译用语才将它和const两者所修饰的量都说成“常量”,希望没有引起误会。
  posted on 2004-10-23 09:06 Allen Lee阅读(6623) 评论(31) 编辑 收藏所属分类: C#
  
  
  评论
  #1楼 2004-10-21 19:03 unruledboy(灵感之源)
  这个....是复制来的还是您写的? 回复 引用 查看
  #2楼 2004-10-21 22:25 Allen Lee [未注册用户]
  本人原创,是本人在学习Split Temporary Variable时觉得对变量与常量、const与readonly的概念模糊,于是查找相关的资料,还特意E-mail《重构》的作者该重构原则使用变量或常量的问题。 回复 引用 查看
  #3楼 2004-10-21 22:44 unruledboy(灵感之源)
  写得很专业了,跟msdn没有差别:) 回复 引用 查看
  #4楼 [楼主] 2004-10-21 22:55 Allen Lee
  因为本人比较懒惰,不太喜欢记东西,所以我已经进了最大的努力使得每一小点的内容都足够简明,以便读起来更加方便易记,务求每一小点都只涉及到一个内容。 回复 引用 查看
  #5楼 2004-10-21 23:08 unruledboy(灵感之源)
  如果再弄个VB.NET和VC++.NET的例子就更加专业了:) 回复 引用 查看
  #6楼 [楼主] 2004-10-22 08:55 Allen Lee
  这个就有点难度了,我当年学习C++的时候是没有关联到.NET的,没有接触过MC++,不过迟一点有个计划学习C++/CLI,到时候如果需要在给出相应的C++/CLI实例 :) 。至于VB.NET,我实在无能为力了,我没有学过VB.NET :( 。 回复 引用 查看
  #7楼 2004-10-22 10:32 DSharp
  不错,写的很清淅。解决了我的困惑,优其是认真的精神太值得我学习。 回复 引用 查看
  #8楼 2004-10-22 15:52 msolap
  非常清晰,真棒!
  顺便问一个我困惑问题:
  为什么C#不支持const参数,像C++一样。例如, void Foo(const Customer customer) {...} , 意图是不允许修改customer的field。 回复 引用 查看
  #9楼 2004-10-22 16:01 msolap
  还想讨论一下,对第一段代码
  // 以下为引用原文
  public Order(string id)
  {
  m_ID = id;
  }
  // 对于每一份订单,其订单序号都是实时确定的常量。
  private readonly string m_ID;
  public string ID
  {
  get { return m_ID; }
  }
  ... 结束
  为什么不用下面的形式?
  public Order(string id)
  {
  ID = id;
  }
  // 对于每一份订单,其订单序号都是实时确定的常量。
  public readonly string ID
  ... 结束
  既然订单序号是实时确定的常量...
  回复 引用 查看
  #10楼 [楼主] 2004-10-22 17:58 Allen Lee
  回答msolap:
  1. 针对你提的问题,我已经在原文的第一个例子下面作了相应的解释。在软件开发初期,我们的确不必要这样做,敏捷原则提醒我们首先考虑一个最简单的方案,直到该方案阻碍了我们的前进,我们就对它重构。只不过我个人习惯了把class的field封装起来而已
  
  。
  2. 这里提供一些与自动递增值相关的例子:
  
  
  DataSet ds = new DataSet();
  
  
  DataTable orders = ds.Tables.Add("Orders");
  
  
  DataColumn orderID = orders.Columns.Add("OrderID", typeof(int));
  
  // 激活自动递增功能。
  
  orderID.AutoIncrement = true
  
  // 设置起始值。
  
  orderID.AutoIncrementSeed = 1
  
  // 设置递增步长。
  
  orderID.AutoIncrementStep = 1
  3. 《设计模式解析》的第8章——Expanding Our Horizons里面对封装的作用阐述的很精彩,建议你看一下。或者如果需要,我也可以做一下业余翻译(因为该书至今还没有中文翻译版)
  
  。
  回复 引用 查看
  #11楼 [楼主] 2004-10-22 18:03 Allen Lee
  另外,对于msolap那个“为什么C#不支持const参数”问题,很抱歉我现在无法给你一个完整的回答。我在学习Remove Assignments to Paramenters重构原则时,发现自己对于C#的参数机制也存在很多不解。不过我现在已经开始查找相关的资料来学习了,不久之后将会奉上一篇心得,希望到时候能给你一点启发,但我不敢做出任何承诺:) 。 回复 引用 查看
  #12楼 2004-10-22 18:09 unruledboy(灵感之源)
  to:Allen Lee
  你干脆写篇读书感想,然后让博客园投稿得了:) 回复 引用 查看
  #13楼 2004-10-22 22:05 msolap
  谢谢你的解释,使用C# Property封装field是个很好的习惯!只是和readonly配合时反而有些累赘。(你提供的ID例子很好地说明了property封装field的益处,但是和readonly没有关系)
  关于const参数,我找到了一篇anders的采访文章(http://www.artima.com/intv/choicesP.html),顺便请allen帮忙解释一下。
  期待你的下一篇心得! 回复 引用 查看
  #14楼 [楼主] 2004-10-22 22:19 Allen Lee
  To msolap:
  是的,我承认第一个例子的readonly的确有点累赘,简直可以说不必要,因为getter property已保证了该field只读,并且string具有字符串恒定性,它的内容本身不会改变。这不是一个更好的readonly例子,所以,如果你找到更好的例子,希望你通知一下我:) 。
  回复 引用 查看
  #15楼 2004-10-23 02:27 Sumtec
  @楼主:
  文章写得不错,甚至比一些MSDN的都写得好。不过我粗略看了一下,又一个地方值得商酌。
  我认为readonly在跟static联合使用的时候才更接近于const的表现,原因很简单,请看:
  Class TestConst
  {
  public const int ABC = 1;
  }
  TestConst test = new TestConst()
  你可以通过TestConst.ABC来使用,但是test.ABC却不存在,这个现象跟Static是一样的。其实解释起来还是挺简单的,如果一个东西被标明为常量,那么从概念上来说,就应该是恒定不变的,不取决于任何一个对象,也不取决议其他外部因素比如程序启动时间等等。因此至少一个const应该不是对象级别的,作为类级别更加合理。
  此外,readonly并不能够成为常量,因为它可以由于对象的不同(或者其他外部因素的不同,例如构造函数的参数)来决定具体的值。所以我认为readonly应该称为“只读字段”、“只读变量”或者“只读成员”,其含义是,一旦对象构造(或者类被初始化,相对于静态只读字段来说),该值就被确定了,至少对于外部来讲,这个东西就是不可更改的。有点类似Immutable的含义,但不能够因此称为const。实际上这两者是不等同的,在IL级别他们有着非常不一样的行为模式。对于const,虽然我们能够看到它的定义,但是编译器很多时候都回应用“常量传播”的方式来进行优化,因此我们很多时候在反编译的代码里面是看不到某些常量被使用的痕迹的。也许比较例外的情况可能就是string类型的常量了,我没有仔细研究过,如果这种类型的常量没有应用“常量传播”的法则,也不是一件奇怪的事情,因为毕竟string是一种引用类型,虽然是immutable的。反过来如果能够被常量传播处理,那我也不觉得奇怪,因为在.NET的文件格式里面有一个MetaData的数据段,其中关于字符串的Blob就有两个,一个是#STRING,还有一个是#US(UserString,用户字符串。由于研究这些底层的东西已经是很久之前的事情了,不知道有没有记错),因此极有可能字符常量会被放在#String里面,那么在进行字符串操作的时候,没有什么理由阻止代码直接指向一个#String里面的token。
  但是readonly则跟普通的成员没有太大区别,包括读取的时候IL语句的格式,除了你在C#等语言里面是没有办法对其进行修改的,这个应该是有编译器和引擎共同检查的。至少你写出对readonly的成员进行写操作的代码,C#/VB.NET的编译器会拒绝给你生成代码的。但是我估计如果你直接修改二进制文件,强迫产生一个写readonly成员的IL,这样的文件我估计是能够被加载进去的,不过应该会被拒绝执行——抛出某种Critical的Exception。
  总之我认为把readonly解释为常量是不合理的。 回复 引用 查看
  #16楼 2004-10-23 03:03 Sumtec
  @msolap:
  C#没有const参数的问题,也许我能够给你稍微解释一下。
  因为C#是一种相对C++更加强类型的语言,因此有些东西部的不做出牺牲——与其说是牺牲,我宁愿认为那是正常的。众所周知,C++语言的根基是C(其实传说中还有一个C+夹在两者之间),而C则是由B语言进化而来的,B语言呢则是从更为古老的BCLP语言里面吸取精华而成的。而BCLP和B语言都没有类型概念,一切变量都是同等对待的,跟VB里面的Variant非常像。当时基本上只有数值概念,顶多有一个AscII码,不要说复杂的面向对象,连GB2312都没有听说过呢。因此这个时候的无类型还是比较吃香的,这样的语言书写起来相当的简单。
  但是没多久,由于VX-11机器的推出,机器字长改变了,因此B语言里面的基本处理单位效率急剧下降,最后不得不妥协,产生出几个基本的类型,用于处理不同长度的数据,这就是C语言有数据类型最基本的原因了。后来的技术发展,使得对象类新越来越多,问题越来越复杂,但是这些都没有改变C语言的一个基本思想,这个思想来自于B语言和BCLP语言,就是尽可能平等的对待所有数据。因此在C语言里面,任何类型的变量都可以通过强制转换变成另外一种毫不相关的数据类型,编译器在这种情况下面也不会去验证是否合法,也不会在运行时动态检验是否合法——C语言假定程序员负责,如果程序员通过强制转换表明可以被强制转换,那么就应该相信程序员的决定是正确的,即使最后程序因此造成整个操作系统的崩溃,那也是程序员的意愿。如果不是这样,黑客的程序如何能够被编写出来呢?实际上这个作用的好处还是比坏处要多那么一点点的,比如说灵活,比如说性能。(说是在的,我认为多出来的那一点点也就是性能方面的问题,灵活性则付出了相应的难验证和控制的代价。)比如说我们可以这么写:
  void foo(const int *ptr)
  {
  boo((int *) ptr);
  }
  void boo(int *ptr)
  {
  }
  换句话说,那个const也仅仅是为了声明一下,好让编译器检查一下是否有被无用的地方。如果你想强制转换成不是const的,那么编译器是不会管你的。因此我们可以说C语言系列(不包括C#)是一个半强类型语言——他强制要求每个变量必须要有类型,但几乎不阻止任何的强制转换。所以即使参数带有const修饰,也不会造成无法使用的问题,必要的时候还是可以转换成不是常量的变量(通常这个之能够是指针)。
  但是C#就不一样了,从一开始就注定了他是一个非常强类型的语言,因为它要运行在.NET Framework上面——一个要求完全管制的平台。那么我们可以想象一下有这么一个函数:
  void foo(const Customer customer);
  它里面需要对customer里面的某个内容进行计算,比如需要调用:
  boo(const Customer customer);
  那如果有另外一个函数也需要同样的计算,那就不能够直接使用者一个函数了,也许需要有一套不带const参数的函数,区别仅仅在于没有const修饰符。当然我们也许可以容许通过类似(const Customer)的强制转换去解决这个问题,但是反过来呢?总不能够允许将常量变成变量把?
  假如boo需要调用一个功能,是否也必须是一个带const参数的函数呢?如果原来已经有了一个不带const参数的函数能够完成同样的功能,那么是否需要创造一个带const标志的函数呢?最后我们会发现很可能所有的东西都必须写上一个const标志,然后我们调用的时候都需要加上const转换。更糟糕的是,如果是const Customer customer,那么表明的应该是customer内部的东西是不可改变的,因为Customer customer这样的声明不可能修函数外部的这个变量所引用的实例。如果真的时表示customer内部的东西比如customer.width也是一个const,那么问题就更多了,是不是Math.Abs函数也必须是const的呢?如果万一没有可能给出一个const版本,例如Point.Offset,是否会有更严重的问题呢?
  简单的说,就是由于C#是一种相对C++来说,更加强类型的语言所造成的。为了避免无休止的const传播问题,因此没有考虑将const参数涉及到C#里面去。 回复 引用 查看
  #17楼 2004-10-23 07:45 msolap
  to Sumtec,
  Sumtec从编译器角度进一步的阐述,让我更清楚了。我想allen兄其实也很清楚。建议allen,再接再励,来一篇“More on readonly vs. const”。
  对c# const参数的解释,我喜欢C#的人性化,多谢了!顺便拜读了Sumtec的几篇大作,也是美文呀!
  回复 引用 查看
  #18楼 [楼主] 2004-10-23 09:07 Allen Lee
  To msolap:
  1. 我反复思考你对第一个例子的疑问,觉得你的建议其实是可以的。ID field既然是实时确定的常量,将达对外公开应该没问题,至于它的初始化工作,那是另外一回事了。所以我重写了第一个例子,添加一个默认构造函数,并去掉原来的带参构造函数,使用Guid来初始化ID field。希望这个新版的例子能够更好的体现readonly的特性,如果你觉得哪里还有不妥,请务必提出来一起讨论。
  2. 在我看来,很多时候getter property比起readonly field更好工作,前者不但具备后者所提供的只读特性,还具有灵活的扩展性,能隐藏潜在变化,所以,个人建议可以的话不妨多点考虑前者。
  3. 至于你提供的那篇Ander的采访,我看了一下,其中提及const参数的那部分内容涉及到C++ const_cast运算符的使用,我已经有了一些想法,但还需要查找一下相关资料保证这些想法的正确性才能形成文字,如果你不介意的话,请再等一下。
  回复 引用 查看
  #19楼 [楼主] 2004-10-23 09:51 Allen Lee
  To Sumtec:
  1. 昨晚网络出现问题,打不开网页,错过了这么多精彩的捧场真是太可惜了!
  
  
  我看了你对于readonly的第一个回复,的确,把readonly译作常量邮电牵强,还有可能引起不必要的误会。我同意你的观点,今天早上我用Visual C#写了一些代码验证一下相关的想法,首先看看我的代码:
  
  
  
  
  Using directives#region Using directives
  
  
  
  using System;
  
  using System.Collections.Generic;
  
  using#endregion
  
  
  
  namespace ConstantLab
  
  
  
  
  
  {
  
  
  class Program
  
  
  
  
  
  
  {
  
  
  static void Main(string[] args)
  
  
  
  
  
  
  {
  
  
  Constant c = new Constant(3);
  
  
  Console.WriteLine("ConstInt = " + Constant.ConstInt.ToString());
  
  
  Console.WriteLine("ReadonlyInt = " + c.ReadonlyInt.ToString());
  
  
  Console.WriteLine("InstantReadonlyInt = " + c.InstantReadonlyInt.ToString());
  
  
  Console.WriteLine("StaticReadonlyInt = " + Constant.StaticReadonlyInt.ToString());
  
  
  
  
  Console.WriteLine("Press any key to continue
  
  ");
  
  
  Console.ReadLine();
  
  
  }
  
  }
  
  
  
  class Constant
  
  
  
  
  
  
  {
  
  
  public Constant(int instantReadonlyInt)
  
  
  
  
  
  
  {
  
  
  InstantReadonlyInt = instantReadonlyInt;
  
  
  }
  
  
  
  public const int ConstInt = 0
  
  
  
  public readonly int ReadonlyInt = 1
  
  
  
  public readonly int InstantReadonlyInt;
  
  
  
  
  public static readonly int StaticReadonlyInt = 4
  
  }
  
  }
  我在Main()里面使用IntelliSence插入Constant的相关field的时候,发现ReadonlyInt和InstantReadonlyInt需要指定Constant的实例对象;而ConstInt和StaticReadonlyInt却要指定Constant class,参见上面代码,这进一步验证你的想法的正确性。为此,我也会修改一下原文相应的地方。
  
  
  2. 另外,我用IL DASM反编译了上面的代码编译出来的exe文件,得带入下的代码(片断)(可惜博客源没有支持插入IL代码
  
  ):
  .method private hidebysig static void Main(string[] args) cil managed
  {
  .entrypoint
  // Code size 139 (0x8b)
  .maxstack 2
  .locals init ([0] class ConstantLab.Constant c,
  [1] int32 CS$0$0000)
  IL_0000: ldc.i4.3
  IL_0001: newobj instance void ConstantLab.Constant::.ctor(int32)
  IL_0006: stloc.0
  IL_0007: ldstr "ConstInt = "
  // 下面这一句清楚说明.NET对于const常量是直接把其直插入代码,而不是引用相关的field。而readonly修饰的field,无论是instant object级别的还是class级别的,全都直接引用对应的instant object或者class的field。
  IL_000c: ldc.i4.0
  IL_000d: stloc.1
  IL_000e: ldloca.s CS$0$0000
  IL_0010: call instance string [mscorlib]System.Int32::ToString()
  IL_0015: call string [mscorlib]System.String::Concat(string,
  string)
  IL_001a: call void [mscorlib]System.Console::WriteLine(string)
  IL_001f: nop
  IL_0020: ldstr "ReadonlyInt = "
  IL_0025: ldloc.0
  IL_0026: ldfld int32 ConstantLab.Constant::ReadonlyInt
  IL_002b: stloc.1
  IL_002c: ldloca.s CS$0$0000
  IL_002e: call instance string [mscorlib]System.Int32::ToString()
  IL_0033: call string [mscorlib]System.String::Concat(string,
  string)
  IL_0038: call void [mscorlib]System.Console::WriteLine(string)
  IL_003d: nop
  IL_003e: ldstr "InstantReadonlyInt = "
  IL_0043: ldloc.0
  IL_0044: ldfld int32 ConstantLab.Constant::InstantReadonlyInt
  IL_0049: stloc.1
  IL_004a: ldloca.s CS$0$0000
  IL_004c: call instance string [mscorlib]System.Int32::ToString()
  IL_0051: call string [mscorlib]System.String::Concat(string,
  string)
  IL_0056: call void [mscorlib]System.Console::WriteLine(string)
  IL_005b: nop
  IL_005c: ldstr "StaticReadonlyInt = "
  IL_0061: ldsfld int32 ConstantLab.Constant::StaticReadonlyInt
  IL_0066: stloc.1
  IL_0067: ldloca.s CS$0$0000
  IL_0069: call instance string [mscorlib]System.Int32::ToString()
  IL_006e: call string [mscorlib]System.String::Concat(string,
  string)
  IL_0073: call void [mscorlib]System.Console::WriteLine(string)
  IL_0078: nop
  IL_0079: ldstr "Press any key to continue..."
  IL_007e: call void [mscorlib]System.Console::WriteLine(string)
  IL_0083: nop
  IL_0084: call string [mscorlib]System.Console::ReadLine()
  IL_0089: pop
  IL_008a: ret
  } // end of method Program::Main
  于是,如果预先知道某个量在编译时的值并保证将来不会改变,而且该量的值更适合作为class级别共享的话,使用const可以优化性能,因为值是直接插入代码的。
  3. 我不知道在这里是否回答了什么,如果还有什么地方不妥的,请你务必提出讨论。
  
  回复 引用 查看
  #20楼 [楼主] 2004-10-23 10:18 Allen Lee
  To msolap:
  对于你的“C#没有const参数”的问题,Sumtec已经解释的很清楚了,基本上Anders的采访也是这个意思。另外,之前也给Eric Gunnerson发了封E-mail询问相关的问题,他的回答如下:
  C# doesn’t support const parameters.
  The short answer why is that having const tends to lead to two separate hierarchies of methods, one set that is const and one which is non-const. This tends to complicate things considerably. It would also be a problem adding it in a multi-language environment such as the CLR –it’s not useful if the libraries don’t support it, but if the libraries support it, it needs to be in the CLS, and all languages would therefore need to support it.
  There are techniques for creating objects that can’t be modified that can be used in some situations.
  Eric
  Eric的回答已经清楚的表明加入const参数这种特性回导致两种不同方法体系的出现,而这种重复是不必要的,而且还会造成其他更糟糕的影响,这些影响Sumtec已经解释的很清楚了,我也不必要在班门弄斧了
  
  。
  回复 引用 查看
  #21楼 2004-10-23 11:10 Stephen
  谢谢Allen Lee 和 Sumtec,你们的文章可读性非常好! 回复 引用 查看
  #22楼 2004-10-23 22:39 msolap
  many thanks to you all! 回复 引用 查看
  #23楼 2004-11-02 17:09 阮
  http://blog.joycode.com/jgtm2000/archive/2003/12/02/8240.aspx
  这篇的例子挺好的。 回复 引用 查看
  #24楼 [楼主] 2004-11-24 19:01 Allen Lee
  点评:
  常量在编程中有着不可或缺的作用,然而你是否清楚在.NET中如何正确使用常量了呢?本文将为你做一个向导,向你介绍使用常量需要注意的各种事项。 回复 引用 查看
  #25楼 2005-01-06 17:23 Paul Wang [未注册用户]
  Const的作用,值应用:
  AssemblyA 中有一个 Const A 那么 AssemblyB引用 AssemblyA的Const A 后 他的值在 AssemblyB中就确定了,AssemblyA 在编译后对 AssemblyB也没有影响。
  而Readonly就不一样了,它类似指针。 回复 引用 查看
  #26楼 2005-05-19 21:35 Bernie [未注册用户]
  用readonly标识的为只读常量,但下面的代码让我不解.
  public struct ConstReadonly
  {
  public int userID;
  public string username;
  }
  public class Class1
  {
  public readonly ConstReadonly Co;
  public readonly int k=20;
  public void DoChange()
  {
  Co.userID=199;//可以通过
  k=40;//不可以通过
  }
  }
  不是所有用readonly标识的除了在定义的时候赋值或在构造函数中赋值外都不允许再修改了吗,为什么这个结构体(值类型)的却可以再修改呢???
  Why?? 回复 引用 查看
  #27楼 [楼主] 2005-05-21 09:22 Allen Lee
  To Bernie:
  你的代码实际上 Co.userID = 199; 也不能通过的。只有当 ConstReadonly 是 class 而不是 struct 时,此句才能通过编译。所以问题可以简单转化为:
  readonly 所修饰的 field 为值类型和为引用类型之间有什么不同。
  当该 field 为值类型时,除了初始化时,你不能在其他时候赋予它一个值或者改变它的内容;当 field 为引用类型时,除了初始化时,你不能在其他时候赋予它一个指向物,但你可以随时改变其指向物的内容(只要指向物的 field 为变量)。
  造成这样的原因,就你所提供的代码(把 ConstReadonly 改为 class )而言,Class1.k 是值类型,它就是这个值类型本身,由于 readonly 的约束,你不能改变它(当然就不能改变它的内容了);而 Class1.Co 是引用类型,它并不是 ConstReadonly 这个引用类型的实例本身,而是一个指向该实例的托管指针,由于 readonly 的约束,你不能改变该托管指针的内容,而该托管指针的内容实际上是 Class1.Co 这个实例对象在托管堆的地址,换句话说,你不能使得该托管指针指向另一个不同的实例,但 readonly 并没有限制你不能改变托管指针的指向物的内容,于是,你可以对该指向物为所欲为。:~
  另外,对于 C++ 的程序员,
  public readonly ConstReadonly Co;
  在语义上有点类似于 C++ 的常量指针:
  public: ConstReadonly * const Co;
  两者都不能改变指针本身的内容(指向物的地址),但能改变指向物的内容(只要指向物允许的话)。
  Hope this helps! 回复 引用 查看
  #28楼 2006-09-07 11:27 壮志
  好 回复 引用 查看
  #29楼 2006-09-07 11:27 壮志
  好 回复 引用 查看
  #30楼 [TrackBack] 2007-03-29 17:06 宏宇
  今天你多态了吗? DoYouPolymorphismToday?[0]
  [引用提示]宏宇引用了该文章, 地址: http://www.cnblogs.com/cuihongyu3503319/archive/2007/03/29/693007.html 回复 引用 查看
  #31楼 [TrackBack] 2007-08-27 19:28 Robot
  Polymorphism!
  [引用提示]Robot引用了该文章, 地址: http://www.cnblogs.com/RobotH/archive/2007/08/27/871850.html 回复 引用 查看
  刷新评论列表
  标题
  姓名
  主页
  Email (只有博主才能看到)邮件地址无效
  请输入验证码 验证码 *
  
  
  内容(请不要发表任何与政治相关的内容) 请输入评论内容
  
  Remember Me?
  登录 使用高级评论 新用户注册 返回页首 恢复上次提交
  [使用Ctrl+Enter键可以直接提交]
  该文被作者在 2005-10-23 07:36 编辑过
  
  
  相关文章:
  不从 System.Object 继承之后
  当调用 Remove 失效时 [C#]
  从模拟字符串型的枚举说起 [C#]
  多态与 new [C#]
  我是谁?[C#]
  多样式星期名字转换 [Design, C#]
  引爆你的集合灵感 [C#, LINQ]
  C# 字符串性能说想 (Code Project 精选翻译)
  从枚举的初始化说起 [C#]
  当多态遇上数组 ... [C++, C++/CLI, C#]
  沪江技术团队寻觅新成员,下一位会是你吗?
  IT人的互动社区—ZDNetChina中文社区
  服务器托管服务商:零刻数据。

本文转自
http://www.cnblogs.com/allenlooplee/archive/2004/10/23/55183.aspx

相关文章:

  • VS2005 常用快捷键
  • ASP.NET中IsPostBack属性
  • 模仿Gmail右上角的Loading
  • 什么是数字信封?
  • - 转 Ext2.0 form使用实例
  • Ext的layout页面布局解析[转]
  • 【原创】Ext 上传文件,前后台实现, Asp.net 代码
  • jsp request,正确使用. 为什么不能使用 getAttribute 得到保存的数据
  • html 另存为/打印/刷新/查看原文件等按钮的代码!!!
  • 四川汶川地震感言
  • Oracle 导入数据库备份 dmp 文件
  • 丫头的拜托
  • 祝福远方的妹妹高考成功
  • 妹妹的高考,续 ----呵呵,妹妹高考上一本线了,恭喜中.....
  • 妹妹高考续--录取了
  • [case10]使用RSQL实现端到端的动态查询
  • __proto__ 和 prototype的关系
  • 2018以太坊智能合约编程语言solidity的最佳IDEs
  • Apache Pulsar 2.1 重磅发布
  • egg(89)--egg之redis的发布和订阅
  • E-HPC支持多队列管理和自动伸缩
  • Kibana配置logstash,报表一体化
  • Python学习之路16-使用API
  • Python中eval与exec的使用及区别
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • sessionStorage和localStorage
  • vue+element后台管理系统,从后端获取路由表,并正常渲染
  • vue总结
  • 分类模型——Logistics Regression
  • 可能是历史上最全的CC0版权可以免费商用的图片网站
  • 驱动程序原理
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • 一个完整Java Web项目背后的密码
  • 在electron中实现跨域请求,无需更改服务器端设置
  • ![CDATA[ ]] 是什么东东
  • (+4)2.2UML建模图
  • (ZT)出版业改革:该死的死,该生的生
  • (附源码)springboot 智能停车场系统 毕业设计065415
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (一)Thymeleaf用法——Thymeleaf简介
  • (原創) 人會胖會瘦,都是自我要求的結果 (日記)
  • (转)memcache、redis缓存
  • ***利用Ms05002溢出找“肉鸡
  • .NET 发展历程
  • .NET 使用配置文件
  • .NET开源快速、强大、免费的电子表格组件
  • .NET企业级应用架构设计系列之结尾篇
  • .NET企业级应用架构设计系列之应用服务器
  • /var/lib/dpkg/lock 锁定问题
  • @TableId注解详细介绍 mybaits 实体类主键注解
  • [ Linux 长征路第五篇 ] make/Makefile Linux项目自动化创建工具
  • [383] 赎金信 js
  • [ACTF2020 新生赛]Upload 1
  • [Android Pro] AndroidX重构和映射
  • [Android]使用Android打包Unity工程