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'};
        }
    }
  • 相关阅读:
    面向接口程序设计思想实践
    Block Chain Learning Notes
    ECMAScript 6.0
    Etcd Learning Notes
    Travis CI Build Continuous Integration
    Markdown Learning Notes
    SPRING MICROSERVICES IN ACTION
    Java Interview Questions Summary
    Node.js Learning Notes
    Apache Thrift Learning Notes
  • 原文地址:https://www.cnblogs.com/errornull/p/9748961.html
Copyright © 2011-2022 走看看