C#基础 构造函数(方法)
构造函数
构造函数分为:实例构造函数,** 静态构造函数,**私有构造函数。
实例构造函数
1、构造函数的名字与类名相同。
2、使用 new 表达式创建类的对象或者结构(例如int)时,会调用其构造函数。并且通常初始化新对象的数据成员。
3、除非类是静态的,否则会为没有构造函数的类,自动生成一个默认构造函数,并使用默认值来初始化对象字段。
4、构造函数可以有参数,可以以多态的形式存在多个构造函数。
例:
class CoOrds
{
public int x, y;
// 实例构造函数(默认构造函数)
public CoOrds()
{
x = 0;
y = 0;
}
// 具有两个参数的构造函数
public CoOrds(int x, int y)
{
this.x = x;
this.y = y;
}
// 重写toString方法
public override string ToString()
{
return (String.Format("({0},{1})", x, y));
}
static void Main(string[] args)
{
CoOrds p1 = new CoOrds();
CoOrds p2 = new CoOrds(5, 3);
// 使用重写ToString方法显示结果
Console.WriteLine("CoOrds #1 at {0}", p1);
Console.WriteLine("CoOrds #2 at {0}", p2);
Console.ReadKey();
}
}
/* Output:
CoOrds #1 at (0,0)
CoOrds #2 at (5,3)
*/
其中CoOrds()是构造函数,诸如此类不带参数的构造函数称为“默认构造函数”。
CoOrds(int x, int y)同样也是构造函数,构造函数可以有参数,允许多态。
回到顶部
静态构造函数
静态构造函数具有以下属性:
- 静态构造函数不使用访问修饰符或不具有参数。
- 在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数以初始化类。
- 不能直接调用静态构造函数。
- 用户无法控制在程序中执行静态构造函数的时间。
- 静态构造函数的一种典型用法是在类使用日志文件且将构造函数用于将条目写入到此文件中时使用。
- 静态构造函数对于创建非托管代码的包装类也非常有用,这种情况下构造函数可调用
LoadLibrary
方法。 - 如果静态构造函数引发异常,运行时将不会再次调用该函数,并且类型在程序运行所在的应用程序域的生存期内将保持未初始化。
构造函数与静态构造函数:
class TestClass
{
public static int x = 0;
//构造函数
TestClass()
{
x = 1;
}
//静态构造函数
static TestClass()
{
//第二步,执行x = 2
x = 2;
}
//第一步,程序入口Main最先执行。然后执行public static int x = 0 接着执行静态构造函数。
public static void Main(string[] args)
{
Console.WriteLine("x:{0}", x); //打印,x = 2
TestClass Test = new TestClass();//第三步执行构造函数,此时x = 1
Console.WriteLine("x:{0}", x); //打印 x = 1
Console.Read();
}
}
Main是程序入口,当执行Main的时候,最先执行public static int x = 0
接着执行静态构造函数,此时 x = 2
然后执行Main函数里面的内容,打印 x,此时 x = 2
初始化TestClass,然后会执行构造函数,此时 x = 1
打印 x = 1
那么,在调用某类的静态函数时真正的执行顺序:
1、静态变量 > 静态构造函数 > 静态函数
2、静态变量 > 静态构造函数 > 构造函数
C#高效编程改进C#代码的50个行之有效的办法(第2版)里说到这样一段话:
类型实例的完整过程。你需要理解这些操作的顺序,以及对象的默认初始化操作。你要保证在构造的过程中对每个成员变量仅初始化一次。实现这一点最好的方法就是,尽可能的早地进行初始化。
下面就是创建某个类型的第一个实例时,所进行的操作顺序为:
(1)静态变量设置为0
(2)执行静态变量初始化器
(3)执行基类的静态构造函数
(4)执行静态构造函数
(5)实例变量设置为0
(6)执行衯变量初始化器
(7)执行基类中合适的实例构造函数
(8)执行实例构造函数同样类型的第二个以及以后的实例将从第5步开始执行,因为类的构造器仅会执行一次。此外,第6步和第7步将被优化,以便构造函数初始化器使编译器移除重复的指令。
使用静态构造函数的一个原因是:在第一次使用类之前,用静态构造函数来初始化类(或结构体)中一些静态字段或属性。比如对字段或属性进行一系列的操作进行初始化,而不希望每次实例化类的时候改变他,用静态构造函数比较方便。
如果要对静态成员初始化,但是没有显示的声明一个静态构造子的话,那么编译器会自动帮我们生成一个静态的构造器,并且把静态初始化代码放到静态构造器的第一行
静态构造函数只会在开始使用这个类之前 调用一次,仅仅调用一次。
public class MyClass
{
static MyClass()
{
\}
}
注意:静态构造函数没有访问修饰符,不能带任何参数,一个类只能有一个静态构造函数,只能访问类的静态成员(常量也是静态成员)他只是在第一次加载类的时候被调用。
C#中变量和常量:点击打开链接
如下所示:下面一个类有一个只读属性Day(只有get,没有set) 和一个静态构造函数,在静态构造函数中初始化该属性,返回今天是周几。
public class StaticConstructor
{
// 只读属性(只有get没有set)
public static int Day\{get;\}
static StaticConstructor()
\{
// DateTime 是system下的一个类,用来处理与时间相关,这里的Now是DateTime类的静态属性,用于返回本机时间(与你设定的日期时间一致)
DateTime now = DateTime.Now;
// DayOfWeek 属性返回一个DayOfWeek枚举值(0-6 分别对应 周日到周六)
Day = (int)now.DayOfWeek;
\}
}
DateTime.Now 详细输出:点击打开链接
注意:静态构造函数与无参数的实例构造函数可以同时存在,尽管参数列表相同,但是静态构造函数是在第一次加载类的时候调用,而实例构造函数是在创建类的实例对象时调用,所以他们不会有冲突。
一、定义类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace oop
{
class Student // 默认internal族类(项目内), 改为public后其它命名空间可引用并使用其方法
{
public int age;
static Student()
\{
Console.WriteLine("**静态构造方法**");
\}
二、定义子类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace oop
{
class **Person:Student** // 继承类
\{
public int age;
public void **Speak()**
\{
int age = **60;**
Console.WriteLine("Hello, I\`m \{0\} years old.", age);
\}
\}
}
三、测试方法调用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace oop
{
class Program
{
private static string School;
static void Main(string\[\] args)
\{
// 非静态类须实例化后调用
Person p = **new** **Person();**
p.age = **20;**
**p.Speak();**
\}
}
}
输出结果:
静态构造方法
Hello, I`m 60 years old.
分析:
Program类执行Main函数,从Person类生成实例化对象并赋值给p
在生成实例化对象之前,Person子类首先继承Student父类进行初始化
在继承Student类之前, Student类 执行静态构造方法 static Student 初始化自己,打印输出 :“静态构造方法”
Person子类完成继承父类的初始化
在Program类中生成 Person类的实例化p
实例化对象p调用Person类的方法Speak
Speak方法中给age赋值为60,再打印输出。
注意 p.age原来在Person类中初始值为0,Program中调用并赋值p.age=20,但后面speak方法中又把age赋值为了60
开始复习起来C#基础知识了!
静态构造函数
C#普通的构造函数大家应该都是相当了解的了,那么在静态构造函数中只需将函数声明关键字使用static即可,通常静态构造函数初始化类的静态字段。
来看看常见的问题
Q:静态构造函数与实例构造函数相同点?
A:1.静态构造函数的名称必须与类名相同。
2.构造函数不能返回值
Q:静态构造函数与实例构造函数不同点?
A:1.静态构造函数声明中使用static关键字
2.类只能有一个静态构造函数,并且不能带参数
3.静态构造函数不能有访问修饰符
Q:类中可以既有静态构造函数和实例构造函数么?
A:当然!静态构造函数是在第一次加载类的时候调用,而实例构造函数是在创建类的实例对象时调用
Q:静态构造函数可以访问所在类的实例成员么?
A:只能访问静态成员,因此也不能使用this访问器
Q:静态构造函数可以多次调用么?
A:最多调用一次!
Q:静态构造函数可以被继承么?
A:当然…不可以!
Q:静态构造函数是如何调用的呢?
A:我们在程序中不能显式调用静态构造函数,系统会自动调用它们。
Q:那系统会在什么时候调用呢?
A:1.类的任何实例被创建之前
2.类的任何静态成员被引用之前
接下来直接看两段代码了解一下具体
class Program
{
static void Main(string[] args)
{
Console.WriteLine(A.strText);
Console.WriteLine(B.strText);
Console.Read();
\}
\}
public class A
{
public static string strText;
public string Text;
static A()
{
strText = “AAA”;
}
public A()
{
Text = “AAAAAAAAAAAAAAAAAAAAAAAAAA”;
}
}
public class B : A
{
static B()
{
strText = “BBB”;
}
public B()
{
Text = “BBBBBBBBBBBBBBBBB”;
}
}
根据上面的常见问题解答 对于理解静态构造函数就方便多了。
我们首先要访问类A的静态成员strText,所以调用了类A的静态构造函数,给strText赋值为AAA,所以第一行就是AAA。接着看类B,由于B继承了A,所以调用A的静态构造函数,但是之前A已经调用过了,所以不再使用,而strText是A类的静态成员,所以B的静态构造函数不会调用,所以最终也是输出AAA
继续变形一下!
class Program
{
static void Main(string[] args)
{
B b = new B();
A a = new A();
Console.WriteLine(A.strText);
Console.WriteLine(B.strText);
Console.Read();
\}
\}
public class A
{
public static string strText;
public string Text;
static A()
{
strText = “AAA”;
}
public A()
{
Text = “AAAAAAAAAAAAAAAAAAAAAAAAAA”;
}
}
public class B : A
{
static B()
{
strText = “BBB”;
}
public B()
{
Text = “BBBBBBBBBBBBBBBBB”;
}
}
这次我们是将A B两个类实例化了对象,依次来分析一下
首先实例化了B的对象,所以我们要从B中调用静态构造函数,但是由于B继承自A类,所以先调用A类的静态构造函数,strText为AAA,调用完A类后再调用B类自身的静态构造函数,所以strText修改为BBB。接着我们再看A实例化了对象,要调用自身的静态构造函数,但是之前被B调用掉了(只能被调用一次)所以无法调用自身的静态构造函数了。所以很明显最后输出的是两行BBB
那肯定有人要问了,是不是因为你在代码里先实例化了B再实例化了A才这样的,当然可以做个尝试,改变一下顺序,先实例化A再实例化B也是同样的结果啦。分析一下:如果先实例化A,调用自身A的静态构造函数,将strText=AAA,然后实例化B,B继承自A,所以先调用A的静态构造函数,但是已经被A之前调用过了所以无法调用,接着再调用自身B的静态构造函数将strText==BBB。所以结果显而易见还是两行BBB
静态构造函数就复习到这了,如果有遗漏的或者有出错的麻烦指出,谢谢!
还没有评论,来说两句吧...