zoukankan      html  css  js  c++  java
  • CLR via C#-类型和类型成员

    类型的各种成员

    类型的成员有:常量、字段、实例构造器、类型构造器、方法、操作符重载、转换操作符、属性、事件、类型

    以下C#代码展示了一个类型定义,其中包含所有可能的成员。

    public sealed class SomeType{
        //嵌套类
        private class SomeNestedType { }
    
        //常量、只读和静态可读/可写字段
        private const int constantField = 1;
        private readonly String readOnlyField = "2";
        private static int staticField = 3;
    
        //类型构造器
        static SomeType() { }
    
        //实例构造器
        public SomeType() { }
        public SomeType(int x) { }
    
        //实例方法和静态方法
        private String InstanceMethod() { return null; }
        public static void Main() { }
    
        //实例属性
        public int SomeProp{ get; set; }
    
        //实例有参属性(索引器)
        public int this[String s]{ get; set;}
    
        //实例事件
        public event EventHandler SomeEvent;
    }

    类型的可见性

    定义文件范围的类型时,可将类型的可见性指定为public或internal。

    public类型不仅对定义程序集中的所有代码可见,还对其他程序集中的代码可见。

    internal类型则仅对定义程序集中的所有代码可见,对其他程序集中的代码不可见。

    定义类型时不显式指定可见性,C#编译器会帮你指定为internal。

    //public即可有本程序集也可由其他程序集的代码访问
    public class ThisIsAPublicType{}
    //internal,只可有本程序集的代码访问
    internal class ThisisAnInternalType{}
    //没有显式声明,默认为internal
    class ThisIsAlsoAnInternalType{}

    成员的可访问性

    成员的可访问性按照限制最大到限制最小排列:

    private,成员只能由定义类型或任何嵌套类型中的方法访问

    protected,成员只能由定义类型,任何嵌套类型或者不管在什么程序集中的派生类型中的方法访问

    internal,成员只能由定义程序集中的方法访问

    protected internal,成员可由任何嵌套类型、任何派生方法(不管在什么程序集)或者定义程序集的任何方法访问

    public,成员可由任何程序集的任何方法访问

    成员默认是private

    如果没有显式声明成员的可访问性,编译器通常默认选择private。

    接口成员默认是public

    CLR要求接口类型的所有成员都具有public可访问性,因此编译器自动将所有接口成员的可访问性设为public。

    原始成员与重写成员必须具有相同的可访问性

    派生类型重写基类型定义的成员时,C#编译器要求原始成员与重写成员具有相同的可访问性。


    合理使用类型的可见性和成员的可访问性

    默认为密封类的优势

    定义新类型时,编译器应默认生成密封类,使他不能作为基类使用。但是包括C#编译器在内的许多编译器都默认生成非密封类。

    密封类之所以比非密封类好,有以下三个方面的原因

    ①版本控制

    ②性能

    ③安全性和可预测性

    定义类时遵循的原则

    显式指定类为sealed

    定义类时,除非确定要将其作为基类,并允许派生类对他进行转化,否则总是显式地指定为sealed。类默认为internal。

    字段定义为private

    类的内部,将数据字段定义为private。

    避免将成员定义为protected或internal

    在类的内部,将自己的方法属性事件定义为private和非虚。当然也会将某个定义为public,一边公开类型的某些功能。

    尽量避免上述任何成员定义为protected或internal,因为这使类型面临更大的安全风险。迫不得已,也会尽量选择protected或internal。

    virtual永远最后才考虑,因为虚成员会放弃许多控制,丧失独立性,变得彻底依赖于派生类的正确行为。

    定义辅助类封装独立的功能

    当算法的实现开始变复杂时,定义一些辅助类型来封装独立的功能。

    如果定义的辅助类型只有一个超类型使用,就在超类型中嵌套这些辅助类型。

    这样除了可以限制范围,还允许嵌套的辅助类型中的代码引用超类型中定义的私有成员。

    VS的代码分析工具强制执行了一条设计规则,即对外公开的嵌套类型必须在文件或程序集范围中定义,不能在另一个类型中定义。

    因为一些开发人员觉得引用嵌套类型时,所用的语法过于繁琐。


    静态类

    有一些永远不需要实例化的类,例如Console,Math等。这些类只有static成员。

    事实上,这种类的唯一作用就是组合一组相关的成员。例如Math类就定义了一组执行数学运算的方法。

    在C#中,要用static关键字定义不可实例化的类。该关键字只能用于类,不能用于结构(值类型)。因为CLR总是允许值类型实例化,这是没办法阻止的。

    静态类的限制

    ①静态类直接从基类System.Object派生,从其他任何基类派生都没有意义。

    静态类不能实现任何接口,只有使用类的实例时,才可调用类的接口方法。

    静态类只能定义静态成员(字段,方法,属性,事件),任何实例成员都会导致编译器报错。

    静态类不能作为字段、方法参数或局部变量使用,因为他们都代表引用了实例的变量,不允许,会报错。

    下面是一个定义了静态成员的静态类。代码虽然能通过编译,有一个警告,但该类没有做任何有意义的事情。

    public static class AStaticClass
    {
        public static void AStaticMethod(){}
        public static String AStaticProperty(){
            get{return s_AStaticField;}
            set{s_AStaticField=value;}
        }
        private static String s_AStaticField;
        public static event EventHandler AStaticEvent;
    }

    使用关键字static定义类,将导致C#编译器将该类标记为abstract和sealed。编译器不在类型中生成实例构造器方法。


    分部类

    partial关键字告诉C#编辑器:类、结构或接口的定义源代码可能要分散到一个或多个源代码文件中。

    将类型源代码分散到多个文件的原因有三。

    源代码控制

    使用partial关键字可将类型的代码分散到多个源代码文件中,每个文件都可以单独签出,多个程序员可以同时编辑类型。

    在同一个文件中将类或结构分解成不同的逻辑单元

    创建一个类型提供多个功能,使类型能提供完整解决方案。为

    了简化实现,有时会在一个源代码文件中重复声明同一个分部类型。

    然后分部类型的每个部分都实现一个功能,并配以他的全部字段、方法、属性和事件等。

    这样就可以方便地看到组合以提供一个功能的全体成员,从而简化编码。

    代码拆分


    常量

    基元类型与非基元类型常量

    基元类型常量

    常量是值从来不变化的符号。定义常量符号时,他的值必须能在编译时确定。

    确定后,编译器将常量值保存在程序集元数据中。这意味着只能定义编译器识别的基元类型的常量。

    非基元类型常量

    C#也允许定义非基元类型的常量变量constant variable,前提是把值设为null。

    public sealed class SomeType{
        //SomeType不是基元类型但C#允许置为null的这种;类型的常量变量
        public const SomeType Empty=null;
    }

    常量的值直接嵌入代码

    常量值从不变化,所以常量总是被视为静态成员,而不是实例成员。

    定义常量将会创建元数据。代码引用常量符号时,编译器在定义常量的程序集的元数据中查找该符号,提取常量的值,将值嵌入生成的IL代码中。

    所以在运行时不需要为常量分配任何内存。除此之外,不能获取常量的地址,也不能以传引用的方式传递常量。

    这些限制意味着常量不能很好的支持跨程序集的版本控制。因此只有确定一个符号的值从不变化才定义常量。

    如果希望运行时从一个程序集中提取另一个程序集中的值,那不应该使用常量,而应该使用readonly字段。

     


    字段

    字段是一种数据成员,其中容纳了一个值类型的实例或者对一个引用类型的引用。

    字段的内存分配

    CLR支持类型字段和实例字段。如果是类型字段,容纳字段数据所需的动态内存是在类型对象中分配的。

    通常是在引用了该类型的任何方法首次进行JIT编译的时候,将类型加载到一个AppDomain时,创建类型对象。

    如果是实例字段,容纳字段数据所需的动态内存是在构造类型的实例时分配。

    由于字段存储在动态内存中,所以他们的值在运行时才能获取。

    字段解决了常量存在的版本控制问题。而且字段可以是任何数据类型,不像常量仅仅局限于编译器内置的基元类型。

    只读字段和可读写字段

    CLR支持只读字段和可读写字段。

    可读写意味着在代码执行过程中,字段值可多次改变。只读字段只能在构造器方法中写入。

    编译器和验证机制确保只读字段不会被构造器以外的任何方法写入。不过可利用反射来修改只读字段。

    以常量的代码为例,可以使用一个静态只读字段代替常量来修正版本控制问题。

    public sealed class SomeLibraryType{
        public static readonly Int32 MaxEntriesInList=50;
    }

    假设DLL程序集到开发人员将50修改为100,并重新生成程序集。

    当应用程序代码重新执行时,她将自动提取字段到新值100.应用程序不需要重新生成就可以直接运行。

    引用类型只读字段

    当某个字段是引用类型,并且该字段被标记为readonly时,不可改变的是引用,而非字段引用的对象。

    public sealed class AType{
        public static readonly Char[] InvalidChars=new Char[]{'A','B','C'};
    }
    public sealed class AnotherType{
        public static void M(){
            //下面三行代码是合法的,可以通过编译
            AType.InvalidChars[0]='X';
            AType.InvalidChars[1]='Y';
            AType.InvalidChars[2]='Z';
            //下面一行代码是非法的,无法通过编译
            //因为不能让InvalidChars引用别的东西
            AType.InvalidChars=new Char[]{'X','Y','Z'};
        }
    }
  • 相关阅读:
    IOS
    .net程序集强命名(签名)
    spring.net Corn表达式[转]
    SQLite日期类型【转】
    Use SQLite Instead of Local Storage In Ionic Framework【转】
    VS常用快捷键
    安装ASP.net mvc3 Installation failed with error code: (0x80070643), "安装时发生严重错误 "
    支付宝支付(沙箱环境相关配置)
    如何在Windows远程服务器中引入本地磁盘
    设计模式-策略模式
  • 原文地址:https://www.cnblogs.com/errornull/p/9748961.html
Copyright © 2011-2022 走看看