C#面向对象编程
接着上一篇《C#总结之复杂变量与函数》,来继续总结C#的基础语法,这篇主要总结面向对象相关基础语法,包括类,字段,属性,接口,事件,对象及生命周期,继承与多态等老生常谈的知识点。还有一些新的语法糖。
文章目录
1. 类的定义
类定义中的访问修饰符级别
修饰符 | 定义范围 |
---|---|
无或者internal | 只能在当前项目中访问类 |
public | 可以在任何地方访问类 |
abstract 或internal abstract | 只能在当前项目中访问,不能实例化,只能被继承 |
public abstract | 类可以在任何地方访问,不能实例化,只能被继承 |
sealed或internal sealed | 只能在当前项目中访问,不能被继承,只能实例化 |
public abstract | 类可以在任何地方访问,不能被继承,只能实例化 |
其中只包含静态成员且不能拥有实例化对象的类为静态类。
// 抽象基类
public abstract class MyBase { }
// 内部类继承自抽象基类
internal class MyClass : MyBase { }
// 基类接口
public interface IMyBaseInterface { }
// 内部基类接口
internal interface IMyBaseInterface2 { }
// 内部接口继承两个基类接口
internal interface IMyInterface : IMyBaseInterface, IMyBaseInterface2 { };
// 密封类继承父类和接口
internal sealed class MyComplexClass : MyClass, IMyInterface { }
class Program
{
/// <summary>
/// 主入口函数
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
MyComplexClass myObj = new MyComplexClass();
WriteLine(myObj.ToString());
if(myObj.GetType()== typeof(MyComplexClass))
{
WriteLine("True");
}
ReadKey();
}
}
2. 构造函数与析构函数
一个类只有能有一个静态构造函数,且不能有访问修饰符,也不能带任何参数,还不能直接调用,只能在创建包含静态构造函数的类实例时,和访问包含静态构造函数的类的静态成员时,才能被执行。
public class MyBaseClass
{
// 无参默认构造函数
public MyBaseClass()
{
}
// 有参构造函数
public MyBaseClass(int myInt)
{
}
// 析构函数
~MyBaseClass()
{
}
}
// 构造函数初始化器
public class MyDerivedClass:MyBaseClass
{
public MyDerivedClass(int i,int j) : base(i)
{
}
public MyDerivedClass():this(5,6)
{
}
}
3. 成员定义
访问级别
修饰符 | 定义范围 |
---|---|
public | 成员可以由任何代码访问 |
private | 只能有类中的代码访问,默认的级别 |
internal | 只能由定义它的程序集内部的代码访问 |
protected | 只能由类或者派生类中的代码访问 |
静态成员:使用关键字static来声明的成员。可以看成类的全局对象,可以使用静态属性跟踪类创建了多少实例。
3.1 字段
public int MyInt;
// 只能在初始化和构造函数中赋值
public readonly int MyReadInt = 17;
// 静态全局字段
public static int MyStaticInt;
// 常量
public const double PI = 3.141592653589793;
3.2 方法
定义方法的关键字
关键字 | 描述 |
---|---|
virtual | 方法可以重写 |
abstract | 方法必须在非抽象的派生类中重写(只能用于抽象类) |
override | 方法重写了一个基类方法(如果方法被重写,必须使用该关键字) |
extern | 方法定义放在其他地方 |
// 定义方法
public string GetString() => "Here is a string";
// 重写方法示例
public class MyBaseClass
{
public virtual void DoSomething()
{}
public virtual void DoOtherSomething()
{}
}
public class MyDerivedClass : MyBaseClass
{
public override void DoSomething()
{
base.DoSomething();
}
public sealed override void DoOtherSomething()
{
base.DoOtherSomething();
}
}
3.3 属性
class MyClass
{
// 可空类型属性
private int? myInt;
public int? MyIntProp
{
get { return myInt; }
set { value = myInt ?? 0; }
}
// 属性示例
private int myValue;
public int MyValueProp
{
get { return myValue; }
protected set
{
if(value>=0 && value <= 10)
{
myValue = value;
}
}
}
// 自动生成的
private string myString;
public string MyString
{
get => myString;
set => myString = value;
}
// 自动属性
public int myIntProp
{
get;set;
}
public int MyIntProp2 { get; private set; }
// 只读的自动属性
public int MyIntProp3 { get; }
// 自动属性初始化器
public int MyIntProp { get; } = 9;
// 基于表达式的属性
private int myDoubledInt = 5;
public int MyDoubledIntProp => (myDoubledInt * 2);
}
属性综合示例
public class MyClass
{
public readonly string Name;
private int intVal;
public int Val
{
get { return intVal; }
set
{
if (value >= 0 && value <= 10)
{
intVal = value;
}
else
{
throw (
new ArgumentOutOfRangeException("Val", value, "Val must be assigned a value between 0 and 10")
);
}
}
}
public override string ToString() => "Name:" + Name + "\nVal:" + Val;
private MyClass():this("Default Name") { }
public MyClass(string newName)
{
Name = newName;
intVal = 0;
}
private int myDoubledInt = 5;
public int myDoubledIntProp => (myDoubledInt * 2);
}
/// <summary>
/// 主入口函数
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
WriteLine("Creating object myObj....");
MyClass myObj = new MyClass("My Object");
WriteLine("myObj created!");
for(int i = -1; i <= 0; i++)
{
try
{
WriteLine($"\nAttempting to assign {i} to myObj.Val..");
myObj.Val = i;
WriteLine($"Value {myObj.Val} assigned to myObj.Val.");
}
catch(Exception e)
{
WriteLine($"Exception {e.GetType().FullName} throw");
WriteLine($"Message:\n \"{e.Message}\"");
}
}
WriteLine("\nOutputting myObj.ToString()...");
WriteLine(myObj.ToString());
WriteLine("myObj.ToString() Output.");
WriteLine("\nmyDoubledIntProp=5...");
WriteLine($"Getting myDoubledIntProp of 5 is {myObj.myDoubledIntProp}");
ReadKey();
}
3.4 元组析构
// 元组析构示例
public class Location
{
public Location(double latitude, double longitude)
=> (Latitude, Longitude) = (latitude, longitude);
// 维度
public double Latitude { get; }
// 经度
public double Longitude { get; }
// 添加Deconstruct()函数可以实现元组析构
public void Deconstruct(out double latitude, out double longitude)
=> (latitude, longitude) = (Latitude, Longitude);
}
static void Main(string[] args)
{
var numbers = ( 1, 2, 3, 4, 5 );
// 元组析构
var location = new Location(48.13546875, 11.56987536);
(double latitude,double longitude) = location;
WriteLine($"{latitude},{longitude}");
ReadKey();
}
4. 类成员
4.1 隐藏基类方法
public class MyBaseClass
{
public void DoSomething() { }
}
public class MyDerivedClass:MyBaseClass
{
// 隐藏基类的成员,不产生警告
new public void DoSomething() { }
}
public class MyBaseClass2
{
public virtual void DoSomething() => WriteLine("base");
}
public class MyDerivedClass2 : MyBaseClass2
{
// 重写基类的成员
public override void DoSomething() => WriteLine("derived");
}
public class MyDerivedClass3 : MyBaseClass2
{
// 隐藏了基类方法,但仍然可以使用基类调用它
new public void DoSomething() => WriteLine("derived3");
}
4.2 嵌套类
//嵌套类可以访问其包含的类的私有和受保护成员
public class ClassA
{
private int state = -1;
public int State => state;
// 嵌套类
public class ClassB
{
public void SetPrivateState(ClassA target,int newState)
{
target.state = newState;
}
}
}
/// <summary>
/// 主入口函数
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
ClassA myObject = new ClassA();
WriteLine($"myObject.State={myObject.State}");
ClassA.ClassB myOtherObject = new ClassA.ClassB();
myOtherObject.SetPrivateState(myObject, 999);
WriteLine($"myObject.State={myObject.State}");
ReadKey();
}
4.3 部分类
部分类可以把类定义在多个文件中,部分方法可以在一个部分类中定义,在另一个部分类中实现,部分方法是私有的,且不能有返回值和其他关键字修饰符
// 部分类定义
interface IMyInterface1
{
void DoSomething();
}
interface IMyInterface2
{
void DoOtherThings();
}
public partial class MyClass : IMyInterface1
{
// 部分方法
partial void DoAnOther();
public void DoSomething()
{
throw new NotImplementedException();
}
}
public partial class MyClass : IMyInterface2
{
partial void DoAnOther() => WriteLine("Do AnOther things!");
public void DoOtherThings()
{
throw new NotImplementedException();
}
}
5. 接口
5.1 定义接口
interface IMyBaseInterface
{
void DoSomething();
}
// 隐藏从基类接口中继承的成员
interface IMyDerivedInterface:IMyBaseInterface
{
new void DoSomething();
}
// 接口中定义属性
interface IMyInterface
{
int MyInt { get; set; }
}
5.2 实现接口
一般为隐式实现接口,但如果几个接口中有同名方法,这个时候就用到了接口的显示实现。
- 隐示实现接口
public interface IMyInterface
{
void DoSomething();
void DoSomethingElse();
}
public class MyBaseClass
{
public void DoSomething() { }
}
public class MyDerivedClass : MyBaseClass, IMyInterface
{
public void DoSomethingElse()
{
throw new NotImplementedException();
}
}
// 可以使用关键字virtual或 abstract来实现接口成员
public class MyBaseClass2 : IMyInterface
{
public virtual void DoSomething()
{
throw new NotImplementedException();
}
public virtual void DoSomethingElse()
{
throw new NotImplementedException();
}
}
public class MyDerivedClass2 : MyBaseClass2
{
public override void DoSomething()
{
base.DoSomething();
}
}
- 显示实现接口
// 显示实现接口
interface Interface1
{
void Say();
}
interface Interface2
{
void Say();
}
public class P1 : Interface1, Interface2
{
public void Say()
{
Console.WriteLine("哈哈哈");
}
//接口显示实现 默认访问修饰符为private 且只能是private
void Interface2.Say()
{
Console.WriteLine("嘿嘿嘿");
}
}
static void Main(string[] args)
{
P1 p1 = new P1();
p1.Say();//第一个say方法的调用
Interface2 interface2 = new P1();
interface2.Say();//第二个say方法的调用
ReadKey();
}
6. 事件
事件类似于异常,都是由对象引发的,区别在于必须订阅他们在事件发生时执行的代码,称为事件处理程序。
单个事件可供多个处理程序订阅。在该事件发生时,这些事件处理程序都会被调用,其中包括引发该事件的对象所在的类中的事件处理程序,但事件处理程序也可能在其他类中。
对事件处理方法的唯一限制就是它必须匹配事件所要求的返回类型和参数,这个限制是事件定义的一部分,由一个委托指定。
static int counter = 0;
static string displayString = "This string will appear one letter at a time.";
static void Main(string[] args)
{
Timer myTimer = new Timer(100);
// myTimer.Elapsed += new ElapsedEventHandler(WriteChar);
myTimer.Elapsed += WriteChar;
myTimer.Start();
System.Threading.Thread.Sleep(200);
ReadKey();
}
static void WriteChar(object source,ElapsedEventArgs e)
{
Write(displayString[counter++ % displayString.Length]);
}
6.1 定义事件
public delegate void MessageHandler(string messageText);
public class Connection
{
public event MessageHandler MessageArrived;
private Timer pollTimer;
public Connection()
{
pollTimer = new Timer(100);
pollTimer.Elapsed += new ElapsedEventHandler(CheckForMessage);
}
public void Connect() => pollTimer.Start();
public void DisConnect() => pollTimer.Stop();
private static Random random = new Random();
private void CheckForMessage(object sender, ElapsedEventArgs e)
{
WriteLine("Checking for new messages.");
if((random.Next(9)==0) && (MessageArrived != null))
{
MessageArrived("Hello Dahlin!");
}
}
}
public class Display
{
public void DisplayMessage(string message)
=> WriteLine($"Message arrived:{message}");
}
static void Main(string[] args)
{
Connection myConnection = new Connection();
Display myDisplay = new Display();
myConnection.MessageArrived += new MessageHandler(myDisplay.DisplayMessage);
myConnection.Connect();
ReadKey();
}
6.2 通用事件
多用途的通用事件处理程序包括两个参数:
- object source 引发事件的对象的引用
- EventArgs e 由事件传送的参数,为此.Net提供了两个委托类型 EventHandler和EventHandler,以便定义事件。
public class MessageArrivedEventArgs:EventArgs
{
private string message;
public string Message
{
get { return message; }
}
public MessageArrivedEventArgs() => message = "No message sent.";
public MessageArrivedEventArgs(string newMessage) => message = newMessage;
}
public class Connection
{
public event EventHandler<MessageArrivedEventArgs> MessageArrived;
public string Name { get; set; }
private Timer pollTimer;
public Connection()
{
pollTimer = new Timer(500);
pollTimer.Elapsed += new ElapsedEventHandler(CheckForMessage);
}
public void Connect() => pollTimer.Start();
public void DisConnect() => pollTimer.Stop();
private static Random random = new Random();
private void CheckForMessage(object sender, ElapsedEventArgs e)
{
WriteLine("Checking for new messages.");
if((random.Next(9)==0) && (MessageArrived != null))
{
MessageArrived(this,new MessageArrivedEventArgs("Hello Dahlin!"));
}
}
}
public class Display
{
public void DisplayMessage(object source,MessageArrivedEventArgs e)
{
WriteLine($"Message arrived from:{((Connection)source).Name}");
WriteLine($"Message Text:{e.Message}");
}
}
static void Main(string[] args)
{
Connection myConnection1 = new Connection();
myConnection1.Name = "First connection";
Connection myConnection2 = new Connection();
myConnection2.Name = "Second connection";
Display myDisplay = new Display();
myConnection1.MessageArrived += myDisplay.DisplayMessage;
myConnection2.MessageArrived += myDisplay.DisplayMessage;
myConnection1.Connect();
myConnection2.Connect();
ReadKey();
}
6.3 匿名方法
delegate(parameters)
{
// do something
}
myConnection2.MessageArrived += delegate (object source, MessageArrivedEventArgs e)
{
WriteLine($"Message arrived from:{((Connection)source).Name}");
WriteLine($"Message Text:{e.Message}");
};