美文网首页
设计类型(二)

设计类型(二)

作者: 沈_ac89 | 来源:发表于2019-03-08 00:29 被阅读0次

    设计类型

    类型和成员基础

    基础概念

    1. 友元程序集

      // 在程序集中的AssemblyInfo.cs中,标记友元程序集名称和公钥的字符串参数(如果程序集没有强签名的话,则只要程序集即可)
      [assembly: InternalsVisibleTo("Friend2")]
      [assembly: InternalsVisibleTo("Friend3,PublicKey=xxxxx")]
      // 其中PublicKey来源:
      /*
      1. 创建强类型签名文件test.pfx
      2. 打开cmd窗口,并cd到该目录
      3. 创建强签名文件:sn -k test.snk
      4. 创建私钥: sn -p test.snk test.pk.snk
      5. 提取公钥: sn -pt test.pk.snk
      具体参考蒋金楠老师的博客,或者查看sn的帮助文档
      */
      
    1. 分部类、结构和接口

      主要的作用:

      • 源代码控制,方便协作
      • 同一个文件中将类或结构分解成不同的逻辑单元
      • 代码拆分

      注意:

      • 编译时候会合并,对CLR不可见。
    2. 继承的内存分配(面试基本都会考)

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Threading.Tasks;
      
      namespace ILLearning
      {
          internal class C
          {
              protected string Hello = "world";
              public virtual void Say(){
                  Console.WriteLine("C");
              }
          }
      
          internal class C1 : C
          {
              protected string HelloWorld = "Hello,Kitty";
              protected int i = 4;
              public override void Say()
              {
                  var that =  this;
                  Console.WriteLine("C1:"+this.i);
                  Console.WriteLine("C1:"+that.HelloWorld);
                  Console.WriteLine("C1");
              }
          }
      
          internal class C2 : C1
          {
              public override void Say()
              {
               this.Hello = "Kitty";
               this.i = 6;
                  this.HelloWorld = "Hello,World";
                  Console.WriteLine("C2:"+this.HelloWorld);
                  Console.WriteLine("C2:"+this.i);
                  /*
                  * 主要易错点在这里:
                  * 正确的答案是:i=6,HelloWorld="Hello,World",原因是C1的对象没有在这里并没有分配到内存,因此this还是指向C2的对象。
                  */
                  base.Say();
                  Console.WriteLine("C2");
              }
          }
      
          public class CTest
          {
              public static void Main()
              {
                  C c = new C2();
                  // 分析过程:
                  /*
                  * new C2()// 开辟C2的堆内存,并向上创建到System.Object,这时候在内存里面已经有Object和C和C1和C2的类型对象
                  * 将c指向C2的堆的内存空间,但是只是含有C的公开或者静态的方法或者属性、字段。
                   */
                  c.Say();
                  // Console.WriteLine(c.Hello); // 不可见
              }
          }
      }
      
    3. 修饰符

      • 注意:protected对于子类都是private(对外来说不可见),但是对于继承链来说,父类都是protected,子类是private。

    IL深化(后续补)

    • 调用虚方法等

    参考内容

    1. 蒋金楠老师的当InternalsVisibleToAttribute特性遭遇"强签名"

    常量和字段

    常量

    • const定义的常量总是隐式为static。

    字段

    重要的字段修饰符

    • Volatile(后续补上)

      
      
    • static和readonly

          public class D
          {
              // 运行时进行初始化,并且不能修改
              public static readonly string a = "test";
              // 可读可写,但是不为对象独有
              private static Int32 b = 0;
              // 在构造函数里面能改变值,不能改变的是引用
              private readonly string c = "test2";
              public D()
              {
                  //a = "test1";// 
                  this.c = "test3";
              }
      
              public void OutPut() {
                  //this.c = "test4";
                  b = 5;
              }
          }
      

    参考内容

    方法

    基础概念

    1. 实例构造器与类(引用类型)

      • 实例化过程不调用构造参数的办法

        • Object.MenberwiseClone
        • 反序列化
      • 注意:不要再构造函数中调用虚方法,避免实例字段没有实例化导致的麻烦。

    2. 实例构造器和结构(值类型)

      • C#编译器不允许定义无参构造器
    3. 类型构造器(静态构造器)

      • 线程安全
    4. 操作符重载

      • implicit:告诉编译器为了生成代码来调用方法,不需要在源代码中进行显示转型。
      • explicit:告诉编译器只有在发现了显示转型时,才调用方法。
      // 参考例子
      public class PersonTest : IEquatable<PersonTest>
      {
          public PersonTest()
          {
          }
    
          public string Id { get; set; }
    
          public string Name { get; set; }
    
          public bool Equals(PersonTest other)
          {
              return !object.ReferenceEquals(null, other) && object.ReferenceEquals(this, other);
          }
    
          public override bool Equals(object obj)
          {
              if (obj == null) return false;
              if (obj.GetType() != this.GetType()) return false;
              return Equals(obj as PersonTest);
          }
    
          public override int GetHashCode()
          {
              return string.IsNullOrEmpty(this.Id) ? 0 : this.Id.GetHashCode();
          }
    
          // 默认成对出现,并且一般要覆写Equals和GetHashCode
          public static bool operator ==(PersonTest a, PersonTest b) => !object.ReferenceEquals(null,a) && object.ReferenceEquals(a, b);
          public static bool operator !=(PersonTest a, PersonTest b) => object.ReferenceEquals(null,a) || object.ReferenceEquals(null,b) || !object.ReferenceEquals(a, b);
    
          /// <summary>
          /// 隐式转换
          /// </summary>
          /// <param name="p"></param>
          public static implicit operator PersonTest(Person p){
              return new PersonTest { Id = p.Id };
          }
    
          /// <summary>
          /// 显示转换成其他对象
          /// </summary>
          /// <param name="p"></param>
          public static explicit operator Person(PersonTest p)
          {
              return new Person { Id = p.Id };
          }
    
      }
    
      public class Person
      {
          public string Id { get; set; }
      }
    
    1. 扩展方法

    2. 分部方法

          partial class E
          {
              private string _name;
              partial void OnNameChange(string value);
      
      
              public string Name
              {
                  get { return _name; }
                  set
                  {
                      OnNameChange(value.ToUpper());
                      _name = value;
                  }
              }
          }
      
          ///// <summary>
          ///// 如果没有实现分部方法,IL里面不会进行调用
          ///// </summary>
          //partial class E {
          //    partial void OnNameChange(string value)
          //    {
          //        Console.WriteLine("");
          //    }
          //}
      
          class E1 : E
          {
              
          }
      
      • 规则和原则
        • 只能在分部类或结构中声明
        • 分部方法的返回类型始终是void,任何参数都不能用out修饰符来标记。
        • 分部方法的声明和实现必须具有完全一致的签名,定制特性会例外,??
        • 如果没有对应的实现部分,不能再代码中创建一个委托来引用这个分部方法。
        • 分部方法总是被视为private方法。

    参数

    基础概念

    1. 规则和原则

      • 可为方法、构造器方法和有参属性(C#索引器)的参数指定默认值。
      • 可为委托定义一部分的参数指定默认值。
      • 有默认值的参数必须放在没有默认值的所有参数之后。
      • 默认值必须是编译时能确定的常量值。
      • 不要重命名参数变量,调用方可能会使用命名参数,可能会提示CS1739的错误。
      • 如果方法从模块外部掉哟那个,更改参数的默认值具有潜在的危险性。
      • 如果参数用ref或out关键字进行标识,就不能设置默认值。
      • 可选或命名参数调用方法,附加规则:
        • 实参可以按人仪顺序传递,但命名实参智能出现在实参的尾部。
        • 可按名称将实参传给没有默认值的参数。
        • C#不允许省略逗号之间的实参。
        • 如果参数要求ref/out,为了以传参数名的方式传递实参。
    2. 隐士类型的局部变量

    3. ref和out关键字

    4. 向方法传递可变数量的参数(params)

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

      • 说明方法的参数类型时,应尽量指定最弱的类型。
      • 相反,一般最好是将方法的返回类型声明为最强的类型。

    属性

    基础概念

    1. System.Tuple
    2. 有参属性

    相关文章

      网友评论

          本文标题:设计类型(二)

          本文链接:https://www.haomeiwen.com/subject/sekfpqtx.html