• CLR via C# 读书笔记---常量、字段、方法和参数


    常量

    常量是值从不变化的符号。定义常量符号时,它的值必须能在编译时确定。确定后,编译器将唱两只保存在程序集元数据中。使用const关键字声明常量。由于常量值从不变化,所以常量总是被视为类型定义的一部分。换言之,常量总是被视为静态成员,而不是实例成员。常量的值直接潜入代码,在运行时不需要为常量分配任何内存。

    字段

    字段是一种数据成员,其中容纳了一个值类型的实例或者一个对引用类型的引用。由于字段存储在动态类型中,所以它们的值在运行时才能获取。字段还解决了常量存在的版本控制问题。字段可以是任何数据类型。

    CLR支持readonly字段和 read/write字段,大多数字段是read/write字段,意味着在代码执行过程中,字段值可多次改变,但readonly字段只能在构造器方法中写入。

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

    public sealed class AType {
      // InvalidChars must always refer to the same array object
      public static readonly Char[] InvalidChars = new Char[] { 'A', 'B', 'C' };
    }
    
    public sealed class AnotherType {
      public static void M() {
         // The lines below are legal, compile, and successfully 
         // change the characters in the InvalidChars array
        //编译成功没什么问题
         AType.InvalidChars[0] = 'X';
         AType.InvalidChars[1] = 'Y';
         AType.InvalidChars[2] = 'Z';
    
         // The line below is illegal and will not compile because 
         // what InvalidChars refers to cannot be changed
        // 非法的,无法通过编译
         //AType.InvalidChars = new Char[] { 'X', 'Y', 'Z' };
      }
    }
    

    方法

    实例构造器和类

    构造器是将类型的实例初始化为良好状态的特殊方法。构造器在方法定义源数据表中始终叫做.ctor(vs构造函数的代码段就是ctor),创建引用类型的实例时,首先为实例的数据字段分配内存,然后初始化对象的附加字段,最后调用类型的实例构造器来设置对象的初始状态。

    构造引用类型的对象是,在调用实力构造器之前,为对象分配的内存总总是先被归零,没有被构造器显式重写的所有字段都保证获得0null

    实例构造器永远不能被继承,一个类型可以定义多个实力构造器,每个构造器都必须有不同的签名。

    C#用简单的语法在构造引用类型的实例时初始化类型定义中的字段

    internal sealed class SomeType {
        private Int32 m_x = 5;
    }
    

    SomeType的构造器先把5存到m_x,在调用基类的构造器。

    如果有几个以初始化的实力字段和许多重载的构造器方法,可以创建单个构造器来执行这些公共的初始化,然后让其他构造器都显式调用这个公共的构造器,这样能减少代码。

    internal sealed class SomeType {
        // Do not explicitly initialize the fields here
        private Int32 m_x;
        private String m_s;
        private Double m_d;
        private Byte m_b;
    
        // This method MUST be called by all constructors.
        private void SetFieldDefaults() {
            m_x = 5;
            m_s = "Hi there";
            m_d = 3.14159;
            m_b = 0xff;
        }
    
        // This constructor sets all fields to their default.
        public SomeType() {
            SetFieldDefaults();
        }
    
        // This constructor sets all fields to their default, then changes m_x.
        public SomeType(Int32 x) {
            SetFieldDefaults();
            m_x = x;
        }
    
        // This constructor sets all fields to their default, then changes m_s.
        public SomeType(String s) {
            SetFieldDefaults();
            m_s = s;
        }
    
        // This constructor sets all fields to their default, then changes m_x & m_s.
        public SomeType(Int32 x, String s) {
            SetFieldDefaults();
            m_x = x;
            m_s = s;
        }
    }
    

    实例构造器和结构

    CLR总是允许创建值类型的实例,并且没有办法阻止值类型的实例化。值类型其实并不需要定义构造器,但CLR确实允许为值类型定义构造器。但必须显式调用才能执行。结构不能包含显式的无参构造器。没有无参构造器,值类型的字段总是被初始化为0null

    扩展方法

    要想定义自己的扩展方法,只需要在第一个参数前面添加this关键字。
    规则和原则

    • C#只支持扩展方法,不支持扩展属性、扩展事件、扩展操作符。
    • 扩展方法必须在非泛型的静态类中声明,类型没有限制,只有第一个参数前面能用this关键字标记
    • 编译器在查找扩展方法时,要求静态类文件本身必须必有文件作用域,扩展方法必须在顶级静态类中作用,不鞥你在嵌套类中定义
    • 多个静态类可以定义相同的扩展方法,编译器检测到多个扩展方法,会提示方法调用不明确
    • 扩展方法存在版本控制问题,将来Microsoft添加和你一样的扩展方法,程序就会有不同的行为。

    来一个扩展方法栗子:

    internal static class StringBuilderExtensions {
    public static Int32 IndexOf(this StringBuilder sb, Char value) {
        for (Int32 index = 0; index < sb.Length; index++)
            if (sb[index] == value) return index;
        return -1;
    }
    }
    

    参数

    可选参数和命名参数

    private static Int32 s_n = 0;
    

    public static void Go() {
    ImplicitlyTypedLocalVariables();

      // 1. Same as: M(9, "A", default(DateTime), new Guid());
      // 直接调用M
      M();
    
      // 2. Same as: M(8, "X", default(DateTime), new Guid());
      // 前两个参数指定,后两个是默认值
      M(8, "X");
    
      // 3. Same as: M(5, "A", DateTime.Now, Guid.NewGuid());
      // 使用明明参数指定值
      M(5, guid: Guid.NewGuid(), dt: DateTime.Now);
    
      // 4. Same as: M(0, "1", default(DateTime), new Guid());
      // s_n先传入0 ,然后加1,把1传入,再加1 ,此时s_n是 2
      M(s_n++, s_n++.ToString());
    
      // 5. Same as: String t1 = "2"; Int32 t2 = 3; 
      //             M(t2, t1, default(DateTime), new Guid());
      // 2传入 s。然后加1,传入x
      M(s: (s_n++).ToString(), x: s_n++);
     }
      private static void M(Int32 x = 9, String s = "A",
      DateTime dt = default(DateTime), Guid guid = new Guid()) {
    
      Console.WriteLine("x={0}, s={1}, dt={2}, guid={3}", x, s, dt, guid);
    }
    

    规则和原则

    • 可为方法、构造器方法和有参属性的参数指定默认值。
    • 有默认值的参数必须放在没有默认值的所有参数之后。换言之,一旦定义了有默认值的参数,它右边的所有参数也必须有默认值。
    • 默认值必须是编译时能确定的常量值
    • 不要重命名参数变量,否则任何顶用着已传参数名的方式传递实参代码也必须得修改
    • 如果方法从模块外部调用,更改参数的默认的默认值具有潜在的危险性
    • 如果参数用refout关键字进行了标识,就不能设置默认值

    隐式类型的局部变量

    var关键字的真正价值就是让程序员少打几个字。

    以传引用的方式向方法传递参数

    即C#中refout关键字。以前用的不做,现在看来得经常使用。
    先看一下out的栗子

    public static void Main()
    {
        Int32 X;    //没有初始化
        GetValue(out x); //
        Console.WriteLine(x); //显式10
    }
    
    private static void GetValue(out Int32 v)
    {
        v = 10; // 在这里面必须初始化
    }
    

    在看一个ref的栗子

    public static void Main() {
    		Int32 x = 5;         // x is 初始化的字段
    		AddVal(ref x);        // x must be initialized.
    		Console.WriteLine(x); // Displays "15"
    	}
    
    	private static void AddVal(ref Int32 v) {
    		v += 10;  // This method can use the initialized value in v.这方法可以改变已初始化的值
    	}
    

    为值类型使用outref,等同于以传值的方式传递引用类型。
    暂时就介绍上面两种使用refout的方式。

    向方法传递可变数量的参数

    通过使用param关键字完成

    private static Int32 Add(params Int32[] values) {
      // NOTE: it is possible to pass the 'values' 
      // array to other methods if you want to.
    
      Int32 sum = 0;
      for (Int32 x = 0; x < values.Length; x++)
         sum += values[x];
      return sum;
    }
    Console.WriteLine(Add(1, 2, 3, 4, 5));//显式15
    

    参数和返回类型的设计规范

    声明方法的参数类型时,应尽量指定最弱的类型,宁愿要结构也不要基类。而返回类型要声明为最强的类型


    总结

    今天的依然是非常无聊但却非常基础的文章,比如扩展方法refout默认参数等等这些都在实际编程中是经常用到的,必须要掌握牢固,今天一个微软亚洲研究院大数据系列讲座开课了,努力坚持学完。

  • 相关阅读:
    springboot开发之配置Servlet三大组件(Servlet、Filter、Listener)
    springboot开发之配置嵌入式Servlet容器两种方式
    springboot开发之利用idea自带的插件模拟客户端请求
    springboot开发之配置自定义的错误界面和错误信息
    springboot开发之删除员工
    springboot开发之修改员工
    springboot开发之添加员工
    springboot开发之thymeleaf页面公共元素的抽取
    oracle 创建表、删除表、添加字段、删除字段、表备注、字段备注、修改表属性
    C#拼装JSON数组简易方法
  • 原文地址:https://www.cnblogs.com/kui0621/p/4912830.html
Copyright © 2020-2023  润新知