c#构造函数和析构函数

c#构造函数和析构函数

借鉴文章:c#静态构造函数 与 构造函数 : https://www.cnblogs.com/jiagoushi/p/3775046.html
c#析构函数:https://www.cnblogs.com/melao2006/p/4239302.html
托管资源与非托管资源: https://www.cnblogs.com/cjm123/p/8391823.html
intptr:https://blog.csdn.net/u011555996/article/details/70238177
https://www.cnblogs.com/equation/articles/6560451.html
C#中Dispose、析构函数、close的区别 :https://www.cnblogs.com/newlive/p/5393873.html
c#析构函数:https://www.cnblogs.com/melao2006/p/4239302.html

1,析构函数

当一个类的对象离开作用域时,析构函数将被调用(系统自动调用)。析构函数的名字和类名一样,不过要在前面加上 ~ 。对一个类来说,只能允许一个析构函数,析构函数不能有参数,并且也没有返回值。析构函数的作用是完成一个清理工作,如释放从堆中分配的内存。
一个类中可以有多个构造函数,但析构函数只能有一个。对象被析构的顺序,与其建立时的顺序相反,即后构造的对象先析构。

  • 析构函数用于释放被占用的系统资源。

  • 析构函数的名字由符号“~”加类名组成。

  • 只能在类中使用析构函数,不能在结构中使用析构函数。

  • 一个类只能有一个析构函数。

  • 不能继承或重载析构函数。

  • 析构函数只能被自动调用。

  • 析构函数没有任何修饰符、没有任何参数、也不返回任何值。

  • 垃圾回收器决定了析构函数的调用,我们无法控制何时调用析构函数。

  • 垃圾回收器检查是否存在应用程序不再使用的对象。如果垃圾回收器认为某个对象符合析构,则调用析构函数(如果有)并回收用来存储此对象的内存。

  • 程序退出时会调用析构函数。

  • 我们可以通过调用Collect强制进行垃圾回收,但是请不要这样做,因为这样做可能导致性能问题。

 class Program
        {
            class First                     // 基类First
            {
                public First()
                { Console.WriteLine("First构造函数执行了"); }
                ~First()                    // 析构函数
                {
                    Console.WriteLine("~First()析构函数");
                    Console.ReadLine();
                }
            }
            class Second : First            // Second类从First类派生
            {
                public Second()
                { Console.WriteLine("Second构造函数执行了"); }
                ~Second()                   // 析构函数
                {
                    Console.WriteLine("~Second()析构函数");
                    Console.ReadLine();
                }
            }
            class Third : Second            // Third类从Second类派生
            {
                public Third()
                { Console.WriteLine("Third构造函数执行了"); }
                ~Third()                    // 析构函数
                {
                    Console.WriteLine("~Third()析构函数");
                    Console.ReadLine();
                }
            }
            static void Main(string[] args)
            {
                Third Third1 = new Third(); // 创建类的实例
                //GC.SuppressFinalize(Third1);这句话加上之后,上面的析构函数就不会被执行
                GC.Collect();//强制对所有代进行即时垃圾回收。
                Console.ReadLine(); 
            }
        }
        /*
        程序运行时,这三个类的析构函数将自动被调用,调用顺序是按照从派生程度最大的(~Third())到派生程度最小的(~First())次序调用的,和构造函数的调用顺序正好相反。
        */
        /*执行结果:
        First构造函数执行了
        Second构造函数执行了
        Third构造函数执行了
        ~Third()析构函数
        ~Second()析构函数
        ~First()析构函数

        */

2,构造函数

默认构造函数   如果没有为类指定任何构造函数,编译器会自动为类创建一个无参构造函数,用以初始化类的字段;

  • 如果为类编写了构造函数,那么编译器就不会再自动生成无参构造函数了
  • 不加任何修饰符的无参构造函数 默认是私有构造函数
  • C#不允许用户为结构定义无参构造函数。

静态构造函数不能访问实例成员,只能用来初始化一些静态字段或者属性,仅在第一次调用类的任何成员时自动执行,没有修饰符(public,private),没有参数。每个类只能有一个静态构造函数,但可以同时还有一个无参实例构造函数,如下。

  • 静态构造函数没有修饰符修饰(public,private),因为静态构造函数不是我们程序员调用的,是由.net 框架在合适的时机调用的。
  • 无参数的静态构造函数和无参数的构造函数是可以并存的。因为他们一个属于类级别,一个属于实例级别,并不冲突。
  • 静态构造函数中不能实例化实例变量。(变量可以分为类级别和实例级别的变量,其中类级别的有static关键字修饰)。
  • 静态函数的调用时机,是在类被实例化或者静态成员被调用的时候进行调用,并且是由.net框架来调用静态构造函数来初始化静态成员变量。
  • 静态构造函数只会被执行一次。并且是在上一条的调用时机中进行调用。
  • 就像如果没有在类中写构造函数,那么框架会为我们生成一个构造函数,那么如果我们在类中定义了静态变量,但是又没有定义静态构造函数,那么框架也会帮助我们来生成一个静态构造函数来让框架自身来调用。
public class Demo
{
    static Demo() { }
    public Demo() { }
}

如下代码列举了几种静态构造函数会执行的情况

//当我们实例化一个类的时候,静态构造函数和一般构造函数都会被执行
class testClass
        {
            public static string ss = "初始值";
            static testClass()
            {
                 Console.WriteLine("执行了静态构造函数");
            }
            public testClass()
            {
                Console.WriteLine("这个是无参数的构造函数");
            }
            public testClass(string a)
            {
                Console.WriteLine(a);
            }
        }
        static void Main(string[] args)
        {
            testClass aa = new testClass();
            Console.ReadLine();
        }
        //执行结果为: (注意testClass aa = new testClass();执行后,静态构造函数和一般构造函数都会被执行)
         /*执行了静态构造函数
          *这个是无参数的构造函数
          */
//当我们分别从类和其派生类中调用该类的静态变量,会调用哪个静态构造函数?
public class A
    {
        public static string staticText;
        public string Text;
        static A()
        {
            staticText = "AAA";
            Console.WriteLine("类A执行了static构造函数");
        }
        public A()
        {
            Text = "AAAAAAAAAAAAAAAAAAAAAAAAAA";
            Console.WriteLine("类A执行了public构造函数");
        }
    }
    public class B : A
    {
        static B()
        {
            staticText = "BBB";
            Console.WriteLine("类B执行了static构造函数");
        }
        public B()
        {
            Text = "BBBBBBBBBBBBBBBBB";
            Console.WriteLine("类B执行了public构造函数");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(A.staticText);
            Console.WriteLine(B.staticText);
            Console.Read();
        }
    }
    /*执行结果如下
    类A执行了static构造函数
    AAA
    AAA
    */
    /*
    我们来分析一下出现这个情况的原因所在,当显示A.strText的时候,因为strText是静态变量,所以框架会调用A的静态构造函数,此时strText的值为AAA.正确

    当显示B.strText的时候,因为B继承自A,所以会首先调用A的静态构造函数,但是因为静态构造函数只会调用一次,所以不会调用A的静态构造函数,但是又因为strText属于类A,而不是B,所以B得静态构造函数不会执行,故输出的均为AAA。
    */
//当我们对类和其派生类进行实例化的时候,静态构造函数分别怎么执行? 
public class A
    {
        public static string staticText;
        public string Text;
        static A()
        {
            staticText = "AAA";
            Console.WriteLine("类A执行了static构造函数");
        }
        public A()
        {
            Text = "AAAAAAAAAAAAAAAAAAAAAAAAAA";
            Console.WriteLine("类A执行了public构造函数");
        }
    }
    public class B : A
    {
        static B()
        {
            staticText = "BBB";
            Console.WriteLine("类B执行了static构造函数");
        }
        public B()
        {
            Text = "BBBBBBBBBBBBBBBBB";
            Console.WriteLine("类B执行了public构造函数");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            B b = new B();
            A a = new A();
            Console.WriteLine(A.staticText);
            Console.WriteLine(B.staticText);
            Console.Read();
        }
    }
    /*
     类A执行了static构造函数
     类B执行了static构造函数
     类A执行了public构造函数
     类B执行了public构造函数
     类A执行了public构造函数
     BBB
     BBB
     */
    /*
    首先我们实例化了B,此时会调用B的静态构造函数,但是因为strText是A的静态变量,
    所以首先会先调用A的静态构造函数将strText赋值为AAA,此时又会调用B的静态构造函数
    将strText赋值为BBB,所以此时strText的值应该为BBB,所以输出均为BBB。 
    */

私有构造函数不能通过new运算符在外部代码中实例化(但可以编写一个静态方法或属性在其内部实例化一个对象,再将结果返回给外部代码)。私有构造函数的作用:

  • 如果一个类的所有构造函数都设为private,那么这个类是不能被继承的。因为父类的所有构造函数都不可以访问,那就没办法执行父类中任何有参数或者无参数的构造函数。
  • 永远不会实例化,因为它仅用作某些静态成员的容器
  • 常见的应用是工具类和单例模式。有些时候,我们不希望一个类被过多地被实例化,比如有关全局的类、路由类等。这时候,我们可以为类设置构造函数并提供静态方法。希望类只能通过调用某个静态方法来实例化(即所谓的对象实例化的类工厂方法)

要避免实例化,使用私有构造函数好呢,还是使用抽象类更好一些?答案在于要理解这二者的区别。首先来考虑继承,虽然抽象类不能实例化,但其真正的目的是用于作为基类,以便派生类(可实例化)创建自己的实现。使用私有构造函数的类不会被继承,而且也不能被继承。其次,私有构造函数只能禁止外部类对该类进行实例化,却不能禁止在该类内部创建实例。
私有构造函数的特性也可以用于管理对象的创建。虽然私有构造函数不允许外部方法实例化这个类,但却允许此类中的公共方法(有时也称为工厂方法,factory method)创建对象。也就是说,类可以创建自身的实例、控制外界对它的访问,以及控制创建的实例个数

应用场景

每台计算机可以有若干个打印机,但只能有一个Printer Spooler,避免两个打印作业同时输出到打印机。
(摘自吕震宇的C#设计模式(7)-Singleton Pattern)

PC机中可能有几个串口,但只能有一个COM1口的实例。

系统中只能有一个窗口管理器。

.NET Remoting中服务器激活对象中的Sigleton对象,确保所有的客户程序的请求都只有一个实例来处理。

C#设计模式之5:简单工厂和工厂方法模式:https://www.cnblogs.com/pangjianxin/p/7986176.html
.NET设计模式(2):单件模式(Singleton Pattern):http://terrylee.cnblogs.com/archive/2005/12/09/293509.html
C#基础(五)——类中私有构造函数作用:https://www.cnblogs.com/jianfeng1201/p/3939550.html

    public class Demo
        {
            private Demo() { }
            public static Demo NewDemo()
            {
                return new Demo();
            }
        }

如下代码单例模式中的双重锁定,有如下特点

  • 直到对象要求产生一个实例才执行实例化;这种方法称为“惰性实例化”。惰性实例化避免了在应用程序启动时实例化不必要的 singleton。
  • 解决了线程并发和线程安全等问题
public sealed class Pizza
{
    static Pizza pizza =null;
    //创建了一个进程辅助对象,为加锁做准备 方法的原子性,避免了两个线程同时访问一个资源
    static readonly object padlock = new object();
    Singleton()
    {
    }
    public static Pizza Instance
    {
        get
        {
          if (pizza ==null)
          {
            lock (padlock)
            {
                if (pizza ==null)
                {
                    instance = new Singleton();
                    Console.WriteLine("我已经在内部实例化了Pizza类");
                    Console.ReadLine();
                }
                return instance;
            }
          }
          return instance;
        }
    }
}
 class Program
    {
        static void Main(string[] args)
        {
            Pizza pizza = Pizza.Instance;
            Pizza pizza1 = Pizza.Instance;
            Pizza pizza2 = Pizza.Instance;
            Pizza pizza3 = Pizza.Instance;
            Pizza pizza4 = Pizza.Instance;
            Console.ReadLine();
        }
    }
    /*所有的执行结果如下所示: (即使我们多次调用,那么也只会实例化一次)
     * 我已经在内部实例化了Pizza类
     */

我们还可以把类的属性设置为readonly,去静态初始化

 /*
     在此实现中,将在第一次引用类的任何成员时创建实例。公共语言运行库负责处理变量初始化。该类标记为 sealed 以阻止发生派生,
     而派生可能会增加实例。此外,变量标记为 readonly,这意味着只能在静态初始化期间(此处显示的示例)或在类构造函数中分配变量。
         */
    public sealed class Pizza
    {
        static readonly Pizza cheesePizza = new Pizza("测试值");
        public  string testActionTime = "默认值";
        private Pizza(string ss)
        {
            Console.WriteLine("变量testActionTime值为:"+ testActionTime);
            testActionTime = ss;
            Console.WriteLine("变量testActionTime值为:" + ss);
            Console.ReadLine();
        }

         static Pizza()
        {
            Console.WriteLine("静态属性被调用了");
            Console.ReadLine();
        }

        public static Pizza CheesePizza
        {
            get
            {
                return cheesePizza;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Pizza pizza = Pizza.CheesePizza;
            Pizza pizza1 = Pizza.CheesePizza;
            Pizza pizza2 = Pizza.CheesePizza;
            Pizza pizza3= Pizza.CheesePizza;
            Pizza pizza4= Pizza.CheesePizza;
            Console.ReadLine();
        }
    }
    /*全部运行结果如下: (只有当我们去引用类的成员的时候,才会被初始化,也称为惰性初始化)
     * 变量testActionTime值为:默认值
       变量testActionTime值为:测试值
       
       静态属性被调用了
     */

如下在所引用出的作者所说是-----实现了延迟初始化,并具有很多的优势,是值得推荐的一种实
现方式。

public sealed class Pizza
    {
        private Pizza()
        {
            Console.WriteLine("Pizza的构造函数被执行了");
            Console.ReadLine();
        }
        private Pizza(string ss)
        {
            Console.WriteLine("Pizza的构造函数被执行了,传递的参数为" + ss);
            Console.ReadLine();
        }
        static Pizza()
        {
            Console.WriteLine("Pizza的静态构造函数被执行了");
            Console.ReadLine();
        }
        public static Pizza CheesePizza
        {
            get
            {
                return Nested.instance;
            }
        }
        class Nested
        {
            static Nested()
            {
                Console.WriteLine("Nested的静态构造函数被执行了");
                Console.ReadLine();
            }
            private Nested()
            {
                Console.WriteLine("Nested的无参构造函数被执行了");
                Console.ReadLine();
            }
            internal static readonly Pizza instance = new Pizza("Hello");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Pizza pizza = Pizza.CheesePizza;
            Console.ReadLine();
        }
    }
    /*全部运行结果如下: 
    Pizza的静态构造函数被执行了
    Pizza的构造函数被执行了,传递的参数为Hello
    Nested的静态构造函数被执行了
     */

构造函数的执行顺序通常情况下:构造函数先调用System.Object的构造函数,再按照继承的层次结构从上往下进行,直到最终要实例化的类为止,即最先调用的是基类的构造函数,但如果类有静态构造函数,且为首次调用该类,则先调用的是子类的静态构造函数,再是父类的静态构造函数,当然静态构造函数只会执行这一次

 public class SuperClass
    {
        static SuperClass()
        {
            Console.WriteLine("父类static构造函数执行了");
        }
        public SuperClass()
        {
            Console.WriteLine("父类public构造函数执行了");
        }
    }
    //子类
    public class ChildClass : SuperClass
    {
        static ChildClass()
        {
            Console.WriteLine("子类static构造函数执行了");
        }
        public ChildClass()
        {
            Console.WriteLine("子类public构造函数执行了");
        }
    }
    //客户程序
    class Program
    {
        static void Main(string[] args)
        {
            ChildClass cc = new ChildClass();
            Console.ReadKey();
        }
    }
    /*执行结果如下:
     *父类static构造函数执行了
     *父类public构造函数执行了
     *子类public构造函数执行了
     *子类static构造函数执行了
     */

如下两个代码段看下:

public class ParentClass
    {
        public ParentClass()
        {
            Console.WriteLine("Parent无参数构造函数执行了");
        }
        public ParentClass(string param)
        {
            Console.WriteLine("Parent有参数构造函数执行了:" + param);
        }
    }
    //子类
    public class ChildClass : ParentClass
    {
        public ChildClass()
        {
            Console.WriteLine("Child无参数构造函数执行了:" );
        }
        public ChildClass(string param) 
        {
            Console.WriteLine("Child有参数构造函数执行了:" + param);
        }
    }
    //客户程序
    class Program
    {
        static void Main(string[] args)
        {
            ChildClass cc = new ChildClass("param");
            Console.ReadKey();
        }
    }
    /*注意父类中执行的是无参数的构造函数,即时子类执行的是有参构造函数,
     * 如果我们想让父类执行有参数构造函数,就需要继承父类的有参数构造函数,放在下一个代码片段中查看*/

    /*执行结果为: 
     Parent无参数构造函数执行了
     Child有参数构造函数执行了:param
    */
public class ParentClass
    {
        public ParentClass()
        {
            Console.WriteLine("Parent无参数构造函数执行了");
        }
        public ParentClass(string param)
        {
            Console.WriteLine("Parent有参数构造函数执行了:" + param);
        }
    }
    //子类
    public class ChildClass : ParentClass
    {
        public ChildClass()
        {
            Console.WriteLine("Child无参数构造函数执行了:" );
        }
        public ChildClass(string param) :base(param)
        {
            Console.WriteLine("Child有参数构造函数执行了:" + param);
        }
        
    }
    //客户程序
    class Program
    {
        static void Main(string[] args)
        {
            ChildClass cc = new ChildClass("param");
            Console.ReadKey();
        }
    }
    /*这里继承了父亲类的有参数构造函数,执行结果如下*/
    /* 
     Parent有参数构造函数执行了:param
    Child有参数构造函数执行了:param
    */

我们用:this(param)可以执行两个构造函数(只是说从某种意义上讲)

public class Base
    {
        public Base()
        {
            Console.WriteLine("父类的无参数构造函数被调用了");
        }
        public Base(string param)
        {
            Console.WriteLine("父类的有参数参数构造函数被调用了:"+ param);
        }
    }
    public class Child : Base
    {
        public Child() : this("hello")
        {
            Console.WriteLine("子类的无参数构造函数被调用了");
        }
        //public Child(string param) : base()
        public Child(string param):base(param) 
        {
            Console.WriteLine("子类的有参数构造函数被调用了:" + param);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Child a = new Child();
            Console.ReadLine();
        }
    }
    /*执行结果为: 
     父类的有参数参数构造函数被调用了:hello
     子类的有参数构造函数被调用了:hello
     子类的无参数构造函数被调用了
     */

猜你喜欢

转载自blog.csdn.net/qq_37326058/article/details/85218881