简单使用
封装
这里写的只是视频中的简单操作,更详细的在本质论中
属性
定义与使用
每个成员变量都对应一份属性,用于设置和取得成员变量的值
此外,可以通过只实现set或get其中一个来设置字段的只写和只读
private int _Age;
public int Age
{
set
{
Console.WriteLine("set被调用");
Console.WriteLine("value" + value);
//value是外部给的
}
get
{
Console.WriteLine("get被调用");
return 100;
}
}
//另一种定义方式:自动实现
public string FirstName{get;set;}
public string LastName{get;set;}
使用方式
//使用set
Vector3 v = new Vector3();
v.Age = 200;
//使用get
int t = v.Age;
Console.WriteLine(t);
结果
使用规范
- 字段前附加 “_” 前缀,但不要用下划线
- 用名称、名称短语、形容词来命名属性
- 优先使用自动实现的属性而不是字段
- 如果没有额外的实现逻辑,优先使用自动实现的属性,而不是自己的
- 在类中,应使用属性而不是字段,只在属性内部访问属性的支持字段
构造器
定义
class Employee
{
public Employee(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string FirstName{get;set;}
public string LastName{get;set;}
//应避免声明时赋值,又在构造器中赋值
public string Salary{get;set;} = "Not Enough";
//先执行声明时赋值,再是构造器
}
对象初始化列表:续构造器
在使用构造器时,可以在后面加一对大括号进行复赋值操作
Employee employee1 = new Employee("Inigo","Montoya")
{ Title = "Computer Nerd", Salary = "Not enough"};
静态static
用于定义由多个实例共享的数据,即所以实例共享一个储存位置
静态字段
静态字段作用域是类
class Employee
{
//
public static int NextId;
//
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Salary { get; set; } = "Not Enough";
public Employee(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
//
Id = NextId;
NextId++;
//
}
}
//下面是外部访问方式(两种相同),使用类名Employee
Employee.NextId = 100000;
Console.WriteLine($"NextId = {Employee.NextId}");
Console.WriteLine("NextId = {0}", Employee.NextId);
静态方法
- 与字段相同,直接使用类名访问静态方法,如Console.ReadLine()
- this关键字不能在静态方法中使用
静态构造器
对类(不是类的实例)进行初始化
在第一次用到这个类时调用,可以是调用普通构造器时,访问静态方法或字段时
静态构造器只能用静态字段,把静态数据初始化为特定的值
尤其是无法通过声明的简单赋值来获得初始值的字段
static Employee()
{
Random randomGenerator = new Random();
//NextId在上面
NextId = randomGenerator.Next(101, 999);
}
静态属性
静态属性几乎比公共静态字段好,毕竟提供了一定的封装
public static int NextId
{
get
{
return _NextId;
}
private set
{
_NextId = value;
}
}
public static int _NextId = 42;
//相当于
public static int NextId { get; private set; } = 42;
静态类
- 该类无法实例化,且里面的字段和方法都是static
- 不可扩展,即不能派生出其他类
using static SimpleMath;
//加了这行后,就不需要使用SimpleMath前缀,可以直接调用内部方法
public static class SimpleMath
{
public static int Max(int[ ] numbers)
{
int result;
result = numbers[0];
foreach(int number in numbers)
{
if(number>result)
{
result = number;
}
}
return result;
}
public static int Min(int[] numbers)
{
int result;
result = numbers[0];
foreach (int number in numbers)
{
if (number < result)
{
result = number;
}
}
return result;
}
}
//使用方式
Console.WriteLine($@"Longest argument length = {SimpleMath.Max(numbers)}");
Console.WriteLine($@"Longest argument length = {SimpleMath.Min(numbers)}");
继承
一个类只能继承自一个类,不能多继承,这是与C++的关键区别之一
基类与派生类的互相赋值
- 可以把派生类的值直接给基类,称为隐式转换
- 基类的值给派生类称为显式转换,有限制,不一定成功
protected关键字
class PdaItem
{
public string Name { get; set; }
public DateTime LastUpdated { get; set; }
}
class Contact:PdaItem
{
public string Address { get; set; }
public string Phone { get; set; }
}
static void Main(string[] args)
{
Contact contact = new Contact();
PdaItem item = contact;
contact = (Contact)item;
}
密封(sealed)
密封类
使用sealed关键字的类,不能从该类派生
public sealed class CommandLineParser
{
//……
}
密封方法
class A
{
public virtual void Method()
{
}
}
class B : A
{
public override sealed void Method()
{
}
}
//可以继承A进行重写,但不能继承B进行重写
class C : A
{
public override void Method()
{
}
}
基类的重写
virtual、override关键字
用virtual在父类中声明,用override在子类中重写
使用时,会调用最新的、最远的重写方法
用override修饰符的方法都会自动成为虚方法,其他派生类可以进一步“特化”其作用
但是,当使用基类实例化基类,而不是用派生类的值赋予基类,那么这个基类使用的方法仍是基类中的,而不是重写后的
class PdaItem
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Salary { get; set; } = "Not Enough";
public virtual string Name
{
get
{
return $"{FirstName}{LastName}";
}
set
{
Console.WriteLine("PdaItem");
string[] names = value.Split(" ");
FirstName = names[0];
LastName = names[1];
}
}
}
class Contact:PdaItem
{
public override string Name
{
get
{
return $"{FirstName}{LastName}";
}
set
{
Console.WriteLine("Contact");
string[] names = value.Split(" ");
FirstName = names[0];
LastName = names[1];
}
}
public new string FirstName { get; set; }
public new string LastName { get; set; }
}
static void Main(string[] args)
{
Contact contact = new Contact();
PdaItem item = new PdaItem();
//PdaItem item = contact ;
item.Name = "Inigo Montoya";
contact.Name = "Inigo Montoya2";
Console.WriteLine($"{item.FirstName }{item.LastName }");
Console.WriteLine($"{contact.FirstName }{contact.LastName }");
}
结果:
若是用派生类给基类赋值
static void Main(string[] args)
{
Contact contact = new Contact();
//PdaItem item = new PdaItem();
PdaItem item = contact ;
item.Name = "Inigo Montoya";
//contact.Name = "Inigo Montoya2";
Console.WriteLine($"{item.FirstName }{item.LastName }");
Console.WriteLine($"{contact.FirstName }{contact.LastName }");
}
结果:
注意:不要在编写构造器时使用会影响构造对象的虚方法
假如这个虚方法在当前要实例化的派生类中被重写,就会调用重写后的方法,而在这时候,字段还未完全初始化,那么就有可能发生无法预测的后果
另外:只有实例成员才能使用virtual
new关键字
new关键字实际上不是重写,而是另写一个同名的方法,可以说是用来移除报错的
public class BaseClass
{
public void DisplayName()
{
Console.WriteLine("BaseClass");
}
}
public class DerivedClass : BaseClass
{
public virtual void DisplayName()
{
Console.WriteLine("DerivedClass");
}
}
public class SubDerivedClass:DerivedClass
{
public override void DisplayName()
{
Console.WriteLine("SubDerivedClass");
}
}
public class SuperSubDerivedClass : SubDerivedClass
{
public new void DisplayName()
{
Console.WriteLine("SuperSubDerivedClass");
}
}
public static void Main()
{
SuperSubDerivedClass superSubDerivedClass = new SuperSubDerivedClass();
SubDerivedClass subDerivedClass = superSubDerivedClass;
DerivedClass derivedClass = superSubDerivedClass;
BaseClass baseClass = superSubDerivedClass;
superSubDerivedClass.DisplayName();
subDerivedClass.DisplayName();
derivedClass.DisplayName();
baseClass.DisplayName();
}
结果:
继承后的构造器
实例化派生类时,会先调用基类的构造器,对基类进行初始化,用base指定使用哪个构造器,基类构造器中的参数必须在base后面都写上
- 基类无参,派生类有参
class Pdaaitem
{
public Pdaaitem()
{
//Name = name;
}
public string Name { get; set; }
}
class Contact : Pdaaitem
{
public Contact(string name)//:base(name)
{
Name = name;
}
public new string Name { get; set; }
}
- 基类有参,派生类有参,派生类参数不少于基类,
class Pdaaitem
{
public Pdaaitem(string name)
{
Name = name;
}
public string Name { get; set; }
}
class Contact : Pdaaitem
{
//public Contact(string name,int age):base(name)
public Contact(string name):base(name)
{
Name = name;
}
public string Name { get; set; }
}
- 基类无参,派生类无参
class Pdaaitem
{
public Pdaaitem()
{
//Name = name;
}
public string Name { get; set; }
}
class Contact : Pdaaitem
{
public Contact()//:base(name)
{
//Name = name;
}
public string Name { get; set; }
}
多态
相同签名的成员在不同的类有不同的实现称为多态性
一个类可以同时继承一个基类和多个接口,基类在前
抽象类
抽象类中定义了派生类应该包含什么,但不包含如何实现,所以无法使用抽象类实例化对象
要从抽象类中派生,必须对抽象方法提供具体的实现
抽象类中的方法可以使用abstract,也可以不用
abstract class Pdaaitem
{
public int I { set; get; }
public abstract void Test();
public virtual string Name { get; set; }
}
class Contact : Pdaaitem
{
public override void Test()
{
}
public override string Name
{
set
{
Name = "siki";
}
get
{
return "siki";
}
}
如果不用abstract修饰方法,而是只是抽象类
abstract class Pdaaitem
{
public int I { set; get; }
public void Test()
{
Console.WriteLine("Pdaaitem");
}
public virtual string Name { get; set; }
}
class Contact : Pdaaitem
{
public new void Test()
{
Console.WriteLine("Contact");
}
public override string Name
{
set
{
Name = "siki";
}
get
{
return "siki";
}
}
}
private static void Main()
{
//下面这一条会报错
//Pdaaitem pdaaitem2 = new Pdaaitem();
Pdaaitem pdaaitem = new Contact();
pdaaitem.Test();
Contact contact = new Contact();
contact.Test();
}
结果:
接口(interface关键字)
- 接口将实现细节和功能分开,用各种派生类中的不同实现来实现多态
- 接口不包含任何实现,也就是只有函数头
- 接口不能有字段,如果接口要求派生类包含特定数据,会声明属性而不是字段
- 接口成员不能有访问修饰符,全部自动定义为public
- 接口没有构造器和终结
- 接口不能包含静态成员
- 接口成员不能用abstract修饰符
- 命名规范:用大写字母“I"作为前缀