C#基础之构造函数
构造函数的基础
我们构造对象的时候,对象的初始化过程是自动完成的,但是在初始化对象的过程中有的时候需要做一些额外的工作,例如需要初始化对象存储的数据,构造函数就是用于初始化数据的函数。
声明基本的构造函数的语法就是声明一个和所在类同名的方法,但是该方法没有返回类型。
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>
{
//
}