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
 
本来想总结下类的其他成员,发现篇幅有点超了,下篇在写吧
posted @ 2011-11-07 22:54  BangQ  阅读(1052)  评论(7编辑  收藏  举报