欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C#基础之构造函数

程序员文章站 2022-06-10 13:06:21
...

构造函数的基础

我们构造对象的时候,对象的初始化过程是自动完成的,但是在初始化对象的过程中有的时候需要做一些额外的工作,例如需要初始化对象存储的数据,构造函数就是用于初始化数据的函数。

声明基本的构造函数的语法就是声明一个和所在类同名的方法,但是该方法没有返回类型。

public class MyClass{
    public MyClass()
     {
        这个构造函数的函数体
	}
}

 

当我们使用new关键字创建类的时候,就会调用构造方法。

我们一般会使用构造方法进行初始化数据的一些操作。

构造函数可以进行重载,跟普通函数重载是一样的规则

注意:当我们不写,任何构造函数的时候,编译器会提供给我们一个默认的 无参的构造函数,但是如果我们定义了一个或者多个构造函数,编译器就不会再提供默认的构造函数

例:

class Vector3
    {
        private float x, y, z;
        public Vector3()//定义了构造函数,系统自定的不会被调用了
        {
            Console.WriteLine("构造函数1被调用了");
        }
        public Vector3(int x, int y, int z)
        {
            Console.WriteLine("构造函数2被调用了");
            this.x = x;
            this.y = y;
            this.z = z;
        }
        public double length()
        {
           return Math.Sqrt(x * x + y * y + z * z);
        }
   }
   static void Main(string[] args)
    {
        Vector3 v1 = new Vector3();
        Vector3 v2 = new Vector3(1,2,3);
        Console.WriteLine(v2.length());
        Console.ReadKey();
    }

 构造函数中将常遇到的bug

在实现功能时,常常遇到了一个bug,代码如下:

 public class EnemySpawn: MonoBehaviour
    {
        private EnemySpawn()
        {

        }
        private static EnemySpawn _instance = null;
        public static EnemySpawn Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new EnemySpawn();
                return _instance;
            }
        }
    }

现在实现一个敌人产生器的类,将其写作单例模式。

在调试过程中会发现,在Instance的get中,即便new EnemySpawn(),__instance单例对象仍然为null??!!

    public class TestManager: MonoBehaviour
    {
        public TestManager()
        {
            Debug.Log("You call me in TestManager");
        }
    }

当开始运行游戏时,Log输出了两次”You call me in TestManager”.

注意注意:

永远不要在继承MonoBehaviour的类中预设构造函数的调用

其实,在Unity的文档中有提到上述问题:

“避免使用构造函数 不要在构造函数中初始化任何变量,使用Awake或Start实现这个目的。即使是在编辑模式中Unity也自动调用构造函数,这通常发生在一个脚本被编译之后,因为需要调用构造函数来取向一个脚本的默认值。构造函数不仅会在无法预料的时刻被调用,它也会为预设或未**的游戏物体调用。

实际上,MonoBehaviour有两个生命周期,一个是作为C#对象的周期,一个是作为Component的周期。构造函数代表第一个,Awake代表第二个。Editor环境下Editor的代码和脚本代码在同一个AppDomain里,对象的生命周期会表现的跟Player环境下不一样。比如Editor中构造函数被调用的次数和时机跟build出来的游戏不一样,这样就不容易保证正确性。

而且,在编辑器模式下,脚本类就会被构造,在你进入游戏之后,也会被构造,你无法预期类的构造函数何时被调用,因此,凡是继承自MonoBehaviour的类,把它的Awake或者Update当做构造函数来初始化变量。

那么改造过后的代码如下:

public class EnemySpawn: MonoBehaviour {
       #region singleton
    private EnemySpawn() {}
    private static EnemySpawn_instance = null;
    public static EnemySpawn Instance {
        get {
            return _instance;
        }
    }
    #endregion

    private void Awake() {
        _instance = this;
    }
}

这样只是对它的可访问性进行了限制,并没有使用new EnemySpawn()。

但是,这种单例实现有个缺陷,就是你必须这种将脚本手动赋给一个GameObject(缺陷)。

public class Singleton<T> : MonoBehaviour where T : 
MonoBehaviour{
private static T instance;
public static T Instance {
    get {
        if(instance == null){
            GameObject obj = new GameObject();
            instance = obj.AddComponent<T>();
            obj.name = typeof(T).Name;
            //切换场景之后不销毁单例对象
            DontDestroyOnLoad(obj);
        }
        return instance;
    }
}


public class AudioManager : Singleton<AudioMananger>
{
    //
}