/**/
/***********************************************
* 静态变量
* 在所属类装载时被创建;
* 通过类(注意,是类,不是类的对象)进行访问;
* 自始自终只存在一个值.
***********************************************/
namespace 重要小知识点 // 静态变量
... {
class Program
...{
static int staNum = 0; //静态成员
public int pubNum = 10; //类成员
static void Main(string[] args)
...{
Program p = new Program();
p.pubNum = 100; //使用对象 访问类成员
Program.staNum = 999; //使用类名 访问静态成员
}
}
}
/**/ /***********************************************
* const 和 static readonly
* const 是在编译期间初始化
* static readonly 是在运行期间初始化
* 实验见 "C#基础概念二十五问"
***********************************************/
namespace 重要小知识点 // const 和 static readonly
... {
class Program
...{
public const int conNum = 100;
public static readonly int srNum = 10000;
static void Main(string[] args)
...{
Console.WriteLine(Program.conNum); //调用方法一样
Console.WriteLine(Program.srNum); //调用方法一样
}
}
}
/**/ /***********************************************
* abstract
* 此修饰可以用于类、方法、属性、事件和索引指示器.
* 下面一段程序中需要注意的
* 抽象类中定义了一个Attribute,但它并不需要知道这是谁的属性;
* 事件的定义方法,事件的响应函数,怎样触发一个事件;
* 索引,可以对"对象"进行索引,也可以针对"成员"进行索引.
***********************************************/
namespace 重要小知识点 // abstract
... {
class Program
...{
public abstract class AMedia //抽象类
...{
public abstract string Attribute //属性
...{
get;
set;
}
public abstract event EventHandler Play; //事件 注意写法
public abstract void Response();
public abstract string this[int Index] //索引 注意写法
...{
get;
}
}
public class Mp3 : AMedia
...{
//注意父抽象类,里面虽然有 Attribute属性,但却不需要定义 attribute变量
private string attribute;
public override string Attribute //属性
...{
get
...{
return attribute;
}
set
...{
attribute = value;
}
}
public override event EventHandler Play; //事件
public override void Response()
...{
if (Play != null)
...{
Play(this, new EventArgs());
}
}
public override string this[int Index] //索引
...{
get
...{
switch (Index)
...{
case 1:
return "one";
case 2:
return "two";
case 3:
return "three";
default:
return "rand";
}
}
}
}
static void OnPlay(object sender, EventArgs e)
...{
Console.WriteLine(" Mp3 is playing...");
}
static void Main(string[] args)
...{
Mp3 mp3 = new Mp3();
mp3.Attribute = " HelloWorld!"; //使用属性
Console.WriteLine(mp3.Attribute);
mp3.Play += new EventHandler(OnPlay); //与响应函数关联
mp3.Response();//触发事件
int i = 0;
for (i = 0; i < 4; i++)
...{
Console.WriteLine(mp3[i]); //索引(针对 对象)
}
for (i = 0; i < mp3.Attribute.Length; i++)
...{
Console.WriteLine(mp3.Attribute[i]); //索引(针对 属性)
}
}
}
}
/**/ /***********************************************
* sealed 密封
* 1) 用于类时,该类不能再被继承;(不能和abstract同用)
* 2) 用于方法和属性时,该方法和属性不能被继承,必须和override
* 连用,使用sealed修饰的方法肯定是基类中的 虚成员.
***********************************************/
namespace 重要小知识点 // sealed
... {
class Program
...{
static void Main(string[] args)
...{
A a = new A();
a.ShowMe();
B b = new B();
b.ShowMe();
}
}
sealed class SealSelf //此类无法再被继承
...{ }
class A
...{
public virtual void ShowMe()
...{
Console.WriteLine("I am A.");
}
}
class B : A
...{
//密封的肯定是基类中的虚成员
public sealed override void ShowMe() //密封
...{
Console.WriteLine("I am B.");
}
}
class C : B
...{
}
}
/**/ /***********************************************
* 索引指示器
* 可以像数组那样使用其实例后的对象,但与数组不同的是
* 索引指示器的参数类型不仅限于int.
***********************************************/
namespace 重要小知识点
... {
class Program
...{
private string secret;
static void Main(string[] args)
...{
Program pro = new Program();
pro.Secret = "ILOVEU";
for (int i = 0; i < pro.Secret.Length; i++) //对类成员的索引操作
...{
Console.WriteLine(pro.Secret[i]);
}
}
public string this[int Index] //不仅限于int,可以是string Index等等
...{
get
...{
return secret;
}
}
public string Secret
...{
get
...{
return secret;
}
set
...{
secret = value;
}
}
}
}
/**/ /***********************************************
* this
* 关注该程序是如何测试效率的!
* Environment.TickCount
***********************************************/
namespace 重要小知识点
... {
class Program
...{
private string value;
static void Main(string[] args)
...{
Program pro = new Program();
Console.WriteLine(pro.Test1());
Console.WriteLine(pro.Test2());
}
public string Test1()
...{
long vTickCnt = Environment.TickCount;
for (int i = 0; i < 10000000; i++)
...{
this.value = i.ToString(); //使用this调用成员
}
return string.Format("Have this.:: {0} MSEL", Environment.TickCount - vTickCnt);
}
public string Test2()
...{
long vTickCnt = Environment.TickCount;
for (int i = 0; i < 10000000; i++)
...{
value = i.ToString(); //不使用this调用成员
}
return string.Format("Have this.:: {0} MSEL", Environment.TickCount - vTickCnt);
}
}
}
/**/ /***********************************************
* 可以使用抽象函数重写基类中的虚函数吗?
* 答:可以. 需要使用 new 修饰符显式声明.
* new:隐藏了基类中该函数的实现.
* override:重新了基类中的函数.
***********************************************/
namespace 重要小知识点
... {
class Program
...{
static void Main(string[] args)
...{
}
}
class Base
...{
public virtual void Show()
...{
Console.WriteLine("I am Base...");
}
}
abstract class Son : Base
...{
public new abstract void Show();
}
}
/**/ /***********************************************
* 密封类可以有虚函数吗?
* 答:可以. 但基类中的虚函数隐式转换成非虚函数.
***********************************************/
namespace 重要小知识点
... {
class Program
...{
static void Main(string[] args)
...{
}
}
class Base
...{
public virtual void Show()
...{
Console.WriteLine("I am Base...");
}
}
sealed class Son : Base
...{
// Base中的虚函数Show()已隐式转换为非虚函数了.
//Son类不能自己再定义虚函数!
}
}
/**/ /***********************************************
* 类和结构的区别
* 类是引用类型,在堆上分配,类的实例进行赋值其实只是复
* 制了引用;
* 结构是值类型,在栈上分配,结构的赋值操作将产生一个新
* 对象.
*
* 堆和栈
* 堆:需要手动释放. 如类,如new
* 栈:系统自动释放. 如函数里的变量
***********************************************/
namespace 重要小知识点
... {
class Program
...{
static void Main(string[] args)
...{
OwnClass oc = new OwnClass("Hello");
OwnStruct os = new OwnStruct("Hello");
}
}
class OwnClass
...{
private string secret;
public OwnClass(string str)
...{
//注意,这里的赋值操作. str 是一个引用类型,
//所以不要再分配内存空间.
this.secret = str;
}
}
struct OwnStruct
...{
private string secret;
//结构也可以有构造函数,但必须是有参数的构造函数.
public OwnStruct(string str)
...{
//注意,这里的赋值操作. str 并非是一个引用类型,
//所以得为它再分配内存空间.
this.secret = str;
}
}
}
/**/ /***********************************************
* 接口的多继承会带来哪些问题?
* 答:如果两个或多个父接口中存在同名成员,就产生二义性.
* 此时,最好使用显式声明.
***********************************************/
namespace 重要小知识点
... {
class Program : Video, Audio
...{
void Video.Play() //显式声明
...{
Console.WriteLine("Video is working...");
}
void Audio.Play() //显式声明
...{
Console.WriteLine("Audio is working...");
}
static void Main(string[] args)
...{
Program pro = new Program();
//注意如何调用
Video v = (Video)pro; //显示转换
v.Play();
Audio a = (Audio)pro; //显示转换
a.Play();
}
}
interface Video
...{
void Play();
}
interface Audio
...{
void Play();
}
}
/**/ /***********************************************
* 抽象类和接口的区别:
* 抽象类可以包含 定义 和 实现;
* 接口只可以包含 定义.
*
* 抽象类是从一系列对象中抽象出来的概念,因此反映事物
* 的内部共性;
* 接口是为了满足外部调用而定义的一个功能约定,因此反
* 映的是事物的外部特性.
*
* 分析对象,提炼内部共性形成抽象类,用以表示对象本质,
* 即"是什么";
* 为外部提供调用或功能需要扩充时优先使用接口.
***********************************************/
namespace 重要小知识点
... {
class Program
...{
static void Main(string[] args)
...{
}
}
abstract class Base
...{
public abstract void Show();
public void Hide() //抽象类可以有已实现的成员
...{
Console.WriteLine("have hided...");
}
}
class Son : Base
...{
public override void Show()
...{
Console.WriteLine("I am Base's son...");
}
}
}
/**/ /***********************************************
* using 别名指示符
* 包含其它文件,为某个类型起个别名;
* 只在一个单元文件内起作用.
***********************************************/
namespace 重要小知识点
... {
class Program
...{
static void Main(string[] args)
...{
T1.Test1 t1 = new T1.Test1(); //T1 是 Test1.cs 的别名
t1.Show();
T2.Test2 t2 = new T2.Test2();
t2.Show();
}
}
}
/**/ /***********************************************
* StringBuilder和String的区别
* String虽然是引用类型,但在赋值时却会产生新的对象;
* StringBuilder则不会,所以在处理大量字符串时,它是首选.
***********************************************/
namespace 重要小知识点
... {
class Program
...{
static void Main(string[] args)
...{
Program pro = new Program();
pro.Test1();
pro.Test2();
}
public void Test1()
...{
long vTickCnt = Environment.TickCount;
string str = null;
for (int i = 0; i < 20000; i++)
...{
str += i.ToString();
}
Console.WriteLine("string 字符串相加需要 {0} 毫秒", Environment.TickCount - vTickCnt);
}
public void Test2()
...{
long vTickCnt = Environment.TickCount;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 20000; i++)
...{
sb.Append(i);
}
Console.WriteLine("StringBuilder 字符串相加需要 {0} 毫秒", Environment.TickCount - vTickCnt);
}
}
}
/**/ /***********************************************
* params
* 该关键字用在方法的参数列表中,为该方法参数提供了可
* 变的能力.它只能出现一次,且只能定义在所有参数最后.
***********************************************/
namespace 重要小知识点
... {
class Program
...{
//params 定义的参数后面不可以再定义其它参数
public static void UseParams(string str, params object[] list)
...{
Console.WriteLine(str);
for (int i = 0; i < list.Length; i++)
...{
Console.WriteLine(list[i]);
}
}
static void Main(string[] args)
...{
String[] str = new string[] ...{ "I", "LOVE", "YOU" };
Program.UseParams("Hello", str);
}
}
}
* 静态变量
* 在所属类装载时被创建;
* 通过类(注意,是类,不是类的对象)进行访问;
* 自始自终只存在一个值.
***********************************************/
namespace 重要小知识点 // 静态变量
... {
class Program
...{
static int staNum = 0; //静态成员
public int pubNum = 10; //类成员
static void Main(string[] args)
...{
Program p = new Program();
p.pubNum = 100; //使用对象 访问类成员
Program.staNum = 999; //使用类名 访问静态成员
}
}
}
/**/ /***********************************************
* const 和 static readonly
* const 是在编译期间初始化
* static readonly 是在运行期间初始化
* 实验见 "C#基础概念二十五问"
***********************************************/
namespace 重要小知识点 // const 和 static readonly
... {
class Program
...{
public const int conNum = 100;
public static readonly int srNum = 10000;
static void Main(string[] args)
...{
Console.WriteLine(Program.conNum); //调用方法一样
Console.WriteLine(Program.srNum); //调用方法一样
}
}
}
/**/ /***********************************************
* abstract
* 此修饰可以用于类、方法、属性、事件和索引指示器.
* 下面一段程序中需要注意的
* 抽象类中定义了一个Attribute,但它并不需要知道这是谁的属性;
* 事件的定义方法,事件的响应函数,怎样触发一个事件;
* 索引,可以对"对象"进行索引,也可以针对"成员"进行索引.
***********************************************/
namespace 重要小知识点 // abstract
... {
class Program
...{
public abstract class AMedia //抽象类
...{
public abstract string Attribute //属性
...{
get;
set;
}
public abstract event EventHandler Play; //事件 注意写法
public abstract void Response();
public abstract string this[int Index] //索引 注意写法
...{
get;
}
}
public class Mp3 : AMedia
...{
//注意父抽象类,里面虽然有 Attribute属性,但却不需要定义 attribute变量
private string attribute;
public override string Attribute //属性
...{
get
...{
return attribute;
}
set
...{
attribute = value;
}
}
public override event EventHandler Play; //事件
public override void Response()
...{
if (Play != null)
...{
Play(this, new EventArgs());
}
}
public override string this[int Index] //索引
...{
get
...{
switch (Index)
...{
case 1:
return "one";
case 2:
return "two";
case 3:
return "three";
default:
return "rand";
}
}
}
}
static void OnPlay(object sender, EventArgs e)
...{
Console.WriteLine(" Mp3 is playing...");
}
static void Main(string[] args)
...{
Mp3 mp3 = new Mp3();
mp3.Attribute = " HelloWorld!"; //使用属性
Console.WriteLine(mp3.Attribute);
mp3.Play += new EventHandler(OnPlay); //与响应函数关联
mp3.Response();//触发事件
int i = 0;
for (i = 0; i < 4; i++)
...{
Console.WriteLine(mp3[i]); //索引(针对 对象)
}
for (i = 0; i < mp3.Attribute.Length; i++)
...{
Console.WriteLine(mp3.Attribute[i]); //索引(针对 属性)
}
}
}
}
/**/ /***********************************************
* sealed 密封
* 1) 用于类时,该类不能再被继承;(不能和abstract同用)
* 2) 用于方法和属性时,该方法和属性不能被继承,必须和override
* 连用,使用sealed修饰的方法肯定是基类中的 虚成员.
***********************************************/
namespace 重要小知识点 // sealed
... {
class Program
...{
static void Main(string[] args)
...{
A a = new A();
a.ShowMe();
B b = new B();
b.ShowMe();
}
}
sealed class SealSelf //此类无法再被继承
...{ }
class A
...{
public virtual void ShowMe()
...{
Console.WriteLine("I am A.");
}
}
class B : A
...{
//密封的肯定是基类中的虚成员
public sealed override void ShowMe() //密封
...{
Console.WriteLine("I am B.");
}
}
class C : B
...{
}
}
/**/ /***********************************************
* 索引指示器
* 可以像数组那样使用其实例后的对象,但与数组不同的是
* 索引指示器的参数类型不仅限于int.
***********************************************/
namespace 重要小知识点
... {
class Program
...{
private string secret;
static void Main(string[] args)
...{
Program pro = new Program();
pro.Secret = "ILOVEU";
for (int i = 0; i < pro.Secret.Length; i++) //对类成员的索引操作
...{
Console.WriteLine(pro.Secret[i]);
}
}
public string this[int Index] //不仅限于int,可以是string Index等等
...{
get
...{
return secret;
}
}
public string Secret
...{
get
...{
return secret;
}
set
...{
secret = value;
}
}
}
}
/**/ /***********************************************
* this
* 关注该程序是如何测试效率的!
* Environment.TickCount
***********************************************/
namespace 重要小知识点
... {
class Program
...{
private string value;
static void Main(string[] args)
...{
Program pro = new Program();
Console.WriteLine(pro.Test1());
Console.WriteLine(pro.Test2());
}
public string Test1()
...{
long vTickCnt = Environment.TickCount;
for (int i = 0; i < 10000000; i++)
...{
this.value = i.ToString(); //使用this调用成员
}
return string.Format("Have this.:: {0} MSEL", Environment.TickCount - vTickCnt);
}
public string Test2()
...{
long vTickCnt = Environment.TickCount;
for (int i = 0; i < 10000000; i++)
...{
value = i.ToString(); //不使用this调用成员
}
return string.Format("Have this.:: {0} MSEL", Environment.TickCount - vTickCnt);
}
}
}
/**/ /***********************************************
* 可以使用抽象函数重写基类中的虚函数吗?
* 答:可以. 需要使用 new 修饰符显式声明.
* new:隐藏了基类中该函数的实现.
* override:重新了基类中的函数.
***********************************************/
namespace 重要小知识点
... {
class Program
...{
static void Main(string[] args)
...{
}
}
class Base
...{
public virtual void Show()
...{
Console.WriteLine("I am Base...");
}
}
abstract class Son : Base
...{
public new abstract void Show();
}
}
/**/ /***********************************************
* 密封类可以有虚函数吗?
* 答:可以. 但基类中的虚函数隐式转换成非虚函数.
***********************************************/
namespace 重要小知识点
... {
class Program
...{
static void Main(string[] args)
...{
}
}
class Base
...{
public virtual void Show()
...{
Console.WriteLine("I am Base...");
}
}
sealed class Son : Base
...{
// Base中的虚函数Show()已隐式转换为非虚函数了.
//Son类不能自己再定义虚函数!
}
}
/**/ /***********************************************
* 类和结构的区别
* 类是引用类型,在堆上分配,类的实例进行赋值其实只是复
* 制了引用;
* 结构是值类型,在栈上分配,结构的赋值操作将产生一个新
* 对象.
*
* 堆和栈
* 堆:需要手动释放. 如类,如new
* 栈:系统自动释放. 如函数里的变量
***********************************************/
namespace 重要小知识点
... {
class Program
...{
static void Main(string[] args)
...{
OwnClass oc = new OwnClass("Hello");
OwnStruct os = new OwnStruct("Hello");
}
}
class OwnClass
...{
private string secret;
public OwnClass(string str)
...{
//注意,这里的赋值操作. str 是一个引用类型,
//所以不要再分配内存空间.
this.secret = str;
}
}
struct OwnStruct
...{
private string secret;
//结构也可以有构造函数,但必须是有参数的构造函数.
public OwnStruct(string str)
...{
//注意,这里的赋值操作. str 并非是一个引用类型,
//所以得为它再分配内存空间.
this.secret = str;
}
}
}
/**/ /***********************************************
* 接口的多继承会带来哪些问题?
* 答:如果两个或多个父接口中存在同名成员,就产生二义性.
* 此时,最好使用显式声明.
***********************************************/
namespace 重要小知识点
... {
class Program : Video, Audio
...{
void Video.Play() //显式声明
...{
Console.WriteLine("Video is working...");
}
void Audio.Play() //显式声明
...{
Console.WriteLine("Audio is working...");
}
static void Main(string[] args)
...{
Program pro = new Program();
//注意如何调用
Video v = (Video)pro; //显示转换
v.Play();
Audio a = (Audio)pro; //显示转换
a.Play();
}
}
interface Video
...{
void Play();
}
interface Audio
...{
void Play();
}
}
/**/ /***********************************************
* 抽象类和接口的区别:
* 抽象类可以包含 定义 和 实现;
* 接口只可以包含 定义.
*
* 抽象类是从一系列对象中抽象出来的概念,因此反映事物
* 的内部共性;
* 接口是为了满足外部调用而定义的一个功能约定,因此反
* 映的是事物的外部特性.
*
* 分析对象,提炼内部共性形成抽象类,用以表示对象本质,
* 即"是什么";
* 为外部提供调用或功能需要扩充时优先使用接口.
***********************************************/
namespace 重要小知识点
... {
class Program
...{
static void Main(string[] args)
...{
}
}
abstract class Base
...{
public abstract void Show();
public void Hide() //抽象类可以有已实现的成员
...{
Console.WriteLine("have hided...");
}
}
class Son : Base
...{
public override void Show()
...{
Console.WriteLine("I am Base's son...");
}
}
}
/**/ /***********************************************
* using 别名指示符
* 包含其它文件,为某个类型起个别名;
* 只在一个单元文件内起作用.
***********************************************/
namespace 重要小知识点
... {
class Program
...{
static void Main(string[] args)
...{
T1.Test1 t1 = new T1.Test1(); //T1 是 Test1.cs 的别名
t1.Show();
T2.Test2 t2 = new T2.Test2();
t2.Show();
}
}
}
/**/ /***********************************************
* StringBuilder和String的区别
* String虽然是引用类型,但在赋值时却会产生新的对象;
* StringBuilder则不会,所以在处理大量字符串时,它是首选.
***********************************************/
namespace 重要小知识点
... {
class Program
...{
static void Main(string[] args)
...{
Program pro = new Program();
pro.Test1();
pro.Test2();
}
public void Test1()
...{
long vTickCnt = Environment.TickCount;
string str = null;
for (int i = 0; i < 20000; i++)
...{
str += i.ToString();
}
Console.WriteLine("string 字符串相加需要 {0} 毫秒", Environment.TickCount - vTickCnt);
}
public void Test2()
...{
long vTickCnt = Environment.TickCount;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 20000; i++)
...{
sb.Append(i);
}
Console.WriteLine("StringBuilder 字符串相加需要 {0} 毫秒", Environment.TickCount - vTickCnt);
}
}
}
/**/ /***********************************************
* params
* 该关键字用在方法的参数列表中,为该方法参数提供了可
* 变的能力.它只能出现一次,且只能定义在所有参数最后.
***********************************************/
namespace 重要小知识点
... {
class Program
...{
//params 定义的参数后面不可以再定义其它参数
public static void UseParams(string str, params object[] list)
...{
Console.WriteLine(str);
for (int i = 0; i < list.Length; i++)
...{
Console.WriteLine(list[i]);
}
}
static void Main(string[] args)
...{
String[] str = new string[] ...{ "I", "LOVE", "YOU" };
Program.UseParams("Hello", str);
}
}
}