• C# 基础CLR从“类”说开来【1】


     上篇谈了CLR支持的两种类型:reference-type 和 value-type,这篇来详细谈谈类(class)。   http://www.vsmvp.com/Post.aspx?id=13

      类 (class) 是最基础的 C# 类型。类是一个数据结构,将状态(字段)和操作(方法和其他函数成员)组合在一个单元中。类为动态创建的类实例 (instance) 提供了定义,实例也称为对象 (object)。类支持继承 (inheritance) 多态性(polymorphism),这是派生类 (derived class) 可用来扩展和专用化基类 (base class) 的机制。类的实例使用 new 运算符创建,该运算符为新的实例分配内存、调用构造函数初始化该实例,并返回对该实例的引用。当不再使用对象时,该对象占用的内存将自动收回。在 C# 中,没有必要也不可能显式释放分配给对象的内存。

     
        类包可以包含一下成员:
        (静态成员 (static member),或者是实例成员 (instance member)。静态成员属于类,实例成员属于该类的实例)
     

    成员

    说明

    常量

    与类关联的常量值

    字段

    类的变量

    方法

    类可执行的计算和操作

    属性

    与读写类的命名属性相关联的操作

    索引器

    与以数组方式索引类的实例相关联的操作

    事件

    可由类生成的通知

    运算符

    类所支持的转换和表达式运算符

    构造函数

    初始化类的实例或类本身所需的操作

    析构函数

    在永久丢弃类的实例之前执行的操作

    类型

    类所声明的嵌套类型

     看下面例子:

    public sealed class SomeType
    // 1
    // 嵌套类
    private class SomeNestedType { } // 2
    // 常量,只读字段,静态字段
    private const Int32 c_SomeConstant = 1// 3
    private readonly String m_SomeReadOnlyField = "2"// 4
    private static Int32 s_SomeReadWriteField = 3// 5
    // 类型构造器
    static SomeType() { } // 6
    // 实例构造器
    public SomeType() { } // 7
    public SomeType(Int32 x) { } // 8
    // 静态方法和类型方法
    public static void Main() { } // 9
    public String InstanceMethod() { return null; } // 10
    // 实例属性
    public Int32 SomeProp
    // 11
    get { return 0; } // 12
    set { } // 13
    }
    // 有参索引器
    public Int32 this[String s]
    // 14
    get { return 0; } // 15
    set { } // 16
    }
    // 实例事件
    public event EventHandler SomeEvent; // 17
    }
        
    类型成员的可访问性
     
    CLR术语C#术语含义
    publicpublic

    访问不受限制

    Family or Assemblyprotected internal

    访问仅限于此程序或从此类派生的类

    Assemblyinternal

    访问仅限于此程序

    Family and Assembly不支持
    Family    protected

    访问仅限于此类或从此类派生的类

    Privateprivate

    访问仅限于此类

     

    静态类

     
    static只能用于类,不能用于结构,因为CLR总是允许值类型实例化,无法阻止。
     
    C#编译器对静态类型进行了如下的限制:
     
    • 静态类必须直接从System.Object派生,从其他任何基类派生都没有任何意义
    • 静态类不能实现任何接口,这是因为只有使用了类的一个实例,才可以调用类的接口方法
    • 静态类只能定义静态成员(字段、方法、属性和事件),任何实例成员都将导致编译错误
    • 静态类不能作为字段、方法参数或者局部变量使用,这些玩意都是实例的变量,编译器会报错
    public static class MyStatic
    {
    /// <summary>
    /// 静态字段
    /// </summary>
    private static string staticString = "Hello bangq";
    /// <summary>
    /// 静态属性
    /// </summary>
    public static string StaticString
    {
            get { return staticString; }
            set { staticString = value; }
    }
    /// <summary>
    /// 静态方法
    /// </summary>
    public static void SayHello()
    {
        Console.WriteLine(staticString);
    }
    }
     
    static void Main(string[] args)
    {
    MyStatic.SayHello();//Hello bangq
    MyStatic.StaticString = "你好 BangQ ";
    Console.WriteLine(MyStatic.StaticString);//你好 BangQ
    Console.ReadKey();
    }
    可以这样说,静态类是MonoState模式的一种体现。提到MonoState就不得不提Singleton(单例)模式。
     
     
    class Singleton
    {
        /// <summary>
        /// 静态成员
        /// </summary>
        private static Singleton _instance = null;
        /// <summary>
        /// 静态属性
        /// </summary>
        public static Singleton Instance {
            get {
                if (_instance == null)
                    {
                        _instance = new Singleton();
                    }
                    return _instance;
                 }
            }
        private Singleton() { }//私有的构造函数
        public void SayHello()
            {
                Console.WriteLine("hello bangq");
            }
      }
    单例模式可以保证系统中只有一个对象的实例,通常情况下可以通过 一个静态成员、一个静态属性或方法、一个私有构造函数来实现。
    为了保证系统中只有一个实例,所以在单例模式中将构造函数设置成私有的,这样就不能New一个实例了,必须通过获取实例的属性或者方法。所以当出现这种代码时:Singleton singleton = new Singleton();编译器会报以下错误:
    ConsoleAppDemo.Singleton.Singleton()' is inaccessible due to its protection level 
    如果仔细的看了上面Singleton的代码可以发现,上述代码如果在多线程的情况下可能会创建多个实例,如:两个线程都执行到了
    if (_instance == null),那么第一个线程创建了一个实例,第二个线程又创建了一个实例。关于多线程的问题,在以后会做总结,这里只简单说一下,加个锁就ok了。调整后的代码如下。
        /// <summary>
        /// 锁
        /// </summary>
        private static readonly object _lock = new object();
        /// <summary>
        /// 静态属性
        /// </summary>
        public static Singleton Instance {
            get {
                    if (_instance == null)
                    {
                        lock (_lock)
                            {
                                if (_instance == null)
                                {
                                    _instance = new Singleton();
                                }
                            }
                    }
                return _instance;
                }
        }
     
    这个里面还有很多技巧和可以优化的地方,可以参见博客园兄弟的文章:http://www.cnblogs.com/BoyXiao/archive/2010/05/07/1729376.html
     

    下面再来说下Monostate 模式

       public class SayHello
        {
            private string helloString = "hello wordh";
    
            public SayHello(string message)
            {
                this.helloString = message;
            }
    
            public string HelloString
            {
                get { return helloString; }
                set { this.helloString = value; }
            }
    
        }
    
    
       public class MonoState
        {
           private static SayHello Hello;
    
           public MonoState() 
           {
               Hello = new SayHello("Hello Word");
           }
           public SayHello Say
           {
    
               get {
    
                   return Hello;
               }
               set {
                   Hello = value;
               }
           }
        }

     

    mian函数里面的内容

    monostate.Say.HelloString = "你好,BangQ";

    Monostate是另一种获得系统某个类别对象都表现为一个对象的实现方式,Singleton模式重在通过保证系统中只有唯一实例,而Monostate模式重在保证唯一状态当然Singleton模式更多的是用来 保证唯一实例。Monostate模式更多关注的是行为,即行为上的一致;Singleton模式更多关注的是结构,强制结构上的单一,仅产生唯一实例.关于设计模式方面内容以后有机会再详细讲,再回到类的成员上来。

     

    常量和字段

     
    在C#中使用const定义常量,由于常量的值不会变化,所以常量总是被视为类型定义的一部分,常量总是被视为静态成员而不是实例成员。常量的值必须是在编译时确定的。代码引用一个常量时,直接提取常量的值,嵌入到生成的IL代码中,所以在运行时不需要为常量分配任何内存。不能获取常量的地址,也不能以引用的方式传递常量。CLR via C# 里面的例子:
     
    using System;
    public sealed class SomeLibraryType {
        public const Int32 MaxEntriesInList = 50;
    }
     
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(SomeLibraryType.MaxEntriesInList)
     
        }
    }
    使用ILSpy可以看到 这行代码 IL_0XXX: ldc.i4.s 50 证明了上述所说,const直接嵌入到IL代码里面
     
    【注意:常量不能定义为Static,因为常量总是隐式为static】
     
    一个程序集A定义了一个常量,程序集B引用了这个常量,当程序集A中的常量发生更改时,只有B程序集重新编译才能使新的常量生效。如果想要在运行时获取一个常量,建议使用只读的字段。
     
    字段(field)是一种数据成员,其中容纳了一个值类型的实例或者是一个引用类型的引用。下表总结了字段的修饰符。
     
    CLR术语C#术语说明
    Staticstatic类状态的一部分,而不是实例状态的一部分
    InitOnlyreadonly只能由构造器方法中的代码写入
    Volatilevolatile编译器,CLR或硬件不会执行一些“线程不安全”的优化措施。
     
    readonly也可以定义系统中恒定不变的量。和const不同的是,readonly指定初始值后可以构造函数中更改。readonly的值也可以通过别的手段去更改,比如反射。小结一下:const是编译是常量,readonly是运行时常量。const比较高效,上面说了,无需分配内存。推荐使用 static readonly 代替const
     
    本来想总结下类的其他成员,发现篇幅有点超了,下篇在写吧
    作者:BangQ

    http://www.cnblogs.com/BangQ/

     

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
    说明:本文以“现状”提供且没有任何担保,同时也没有授予任何权利。 | This posting is provided "AS IS" with no warranties, and confers no rights.
    珍爱生命,Leave here!
  • 相关阅读:
    linux cpu load学习笔记
    P1064 金明的预算方案
    P1757 通天之分组背包
    P1352 没有上司的舞会
    P1651 塔
    P1250 种树
    P1938 [USACO09NOV]找工就业Job Hunt
    P4392 [BOI2007]Sound 静音问题
    P3884 [JLOI2009]二叉树问题
    P2880 [USACO07JAN]平衡的阵容Balanced Lineup
  • 原文地址:https://www.cnblogs.com/BangQ/p/2240444.html
Copyright © 2020-2023  润新知