爱是迷迷糊糊
天地初开的时候
那已经盛放的玫瑰,好好好,好歌,接着写我的。
我们有时候会有不得已的时候,要为一个主类里做一堆事,所有人以它马首是瞻。但是问题就是程序代码越写越长,篇幅往下拉都能拉出太平洋了,于是为了能把这个类给截断,C#加入了一个叫做部分类的概念。它使用关键字partial关键字允许把类、结构、方法或接口放在多个文件中。但是要注意,我们并不认可写这么写一个巨无霸出来,更多时候还是希望能按照具体的功能拆分成多个小类。其实我曾经就写过这么一个巨无霸O(∩_∩)O哈哈~。
// 使用部分方法
partial class SampleClass
{
public void MethodOne()
{
DO.......
// 这里调用了在写在别处的属于这个类的方法
AprtialMethod();
}
public partial void AprtialMethod();
}
partial class SampleClass
{
// 一定要注意,部分方法一定是void类型的
public void AprtialMethod()
{
DO.......
}
}
上面讲了部分方法,现在讲拓展方法。我们需要给对象增加功能的时候,我们往往会选择去继承它。还有另一种就是当你对不能继承的类进行功能拓展时,可以使用拓展方法去给对象增加功能。例如String类是密封的无法被继承而可以拓展新方法。
定义: | 1、声明扩展方法的类必须为static类; | |||||||||||||||||
2、扩展方法本身也必须声明为static,扩展方法也可以重载; | ||||||||||||||||||
3、扩展方法必须包含关键字this作为第一个参数类型,并在后面跟着它所扩展的类型的名称; | ||||||||||||||||||
4、最好保证扩展方法和调用方法在同一个命名空间下; | ||||||||||||||||||
5、扩展方法不能和调用的方法放到同一个类中(如拓展方法不能和调用的Main方法放在同一个类中)。 | ||||||||||||||||||
意义: | 1、将静态方法转成扩展方法,扩展方法本质上是静态方法; | |||||||||||||||||
2、编写帮助类; | ||||||||||||||||||
3、为 Linq 服务,实现链式编程。 | ||||||||||||||||||
4、简化代码,不需要将数据转来转去,例如你无法继承string类,但是拓展方法可以让你的string类多一个功能。如果你用普通方法写一个类,那么这方法需要就需要多处调用。 |
其实它的使用很简单的,如:
// 我们为string类添加一个功能,使用拓展方法
// 这个功能叫做您有事说话我反馈,您不说我来说
public static string GetWoking(this string str)
{
if(string.IsNullOrEmpty(str))
{
return "Think about......,NO";
}
else
{
return "I think....,so..."
}
}
// 然后就可以验证了是不是给string添加了功能
string S = "老板开会说了不涨工资!";
// 为何不用传参数,因为S本身就是作为参数传进方法里的
string S = S.GetWoking();
一定要记住,如果已经有一个同名的实例方法了,那么这拓展方法永远不会被使用。而且不能对同一个拓展类定义几个一模一样名称的拓展方法。
除了能给类添加拓展方法,也可以给.NET内置的对象或者自定义的对象添加拓展方法。
又要讲好久了。面向对象的三个重要概念是继承、封装和多态性,每种语言在继承这方面都有自己个性,我们记录下C#自己在这方面支持和不支持哪些。
单继承:一个类可以派生自一个基类。C#是支持的,采用的也是这种继承。
多重继承:一个类可以派生自多个基类(这些基类是同一层的)。C#是不支持的,但是接口支持。
多层继承:就是一个类可以继承它爸爸也可以继承它爷爷,只要是有层次血缘关系的。C#是支持的。
结构虽然不支持继承,但是结构却可以实现接口,接口可以去继承。
// 千万不能说结构支持继承,它只是接口的一种实现
public struct MyStruct: Interface1,Interface2
{
// 结构体
}
实现继承这里就可以引出一个概念,叫做——虚方法,当你把一个基类的方法声明为virtual,就可以在派生类中重写该方法,你也可以给基类的属性使用virtual关键字。在c#中如果你不讲基类的方法声明为virtual,你是无法重写它。
// 声明一个父类
public class Program
{
static void Main(string[] args)
{
Class1 class1 = new Class1();
class1.AA(); // 啥也不输出,因为重写已经覆盖了父类的该方法
}
// 实例方法
public virtual void AA()
{
Console.WriteLine($"多多努力!!");
}
}
// 声明一个子类
public class Class1: Program
{
// 重写父类方法
public virtual void AA()
{
}
}
要注意,重写基类成员时,子类在签名(即所有参数和方法名)和返回类型必须要和父类的完全一致,否则你创建的新成员就不会覆盖基类成员,就是说你自重写的东西就没有用了。
再回来谈,就如同上面的代码,如果我们在派生类的AA()方法中写了代码,那么基类的就会被覆盖,这可能不是我们想要的结果。这是c#就有一种特殊的语法——调用方法的基类版本,base.AA()。就是你的重写并不是要推翻全部,而是在基类的基础上对方法进行功能拓展,我们就用这种语法实现,c#里的base和Java里的super一样。如:
// 声明一个子类
public class Class1: Program
{
// 重写父类方法
public virtual void AA()
{
base.BB();
Console.WriteLine($"不能忽视基础的底层原理!!");
}
} // 到时候输出"多多努力!!不能忽视基础的底层原理!!"
实现继承还可以引出一个多态性,可以动态地定义调用的方法,我不是在编译的期间,它的具体体现就是重载。
还有一个概念叫做——隐藏方法,所谓的隐藏方法就是子类在签名(即所有参数和方法名)和返回类型必须要和父类的完全一致后,但是双方却不声明为virtual和override,那么子类方法就会隐藏基类方法。
下面就该过渡到抽象类和抽象方法了。C# 允许把类和方法声明为abstract即抽象。抽象类是不能进行实例化的(即new一个对象出来),抽象类必须在非抽象类的派生类中重写。显然抽象类本身就是虚拟的,里面的抽象方法不需要你为它提供virtual来实现派生类的重写。
抽象方法就是在抽象类中自有声明而没有实现的方法。如:
// 定义一个抽象类
public abstract class SS
{
// 定义一个抽象方法
public abstract void Method1(int i,string str);
}
// 抽象类要由它的子类去实现
public class SS001: SS
{
// 定义一个抽象方法
public override void Method1(int i,string str)
{
DO......
}
}
如果这个类它哪怕只有一个抽象方法,那么这个类都应该被声明为抽象类。
我们对于抽象类很熟悉,那么你知道密封类和密封方法吗,当你不希望你的类被其它的类去继承,那么你就应该把你的类声明为密封类,密封的关键字是sealed。联系上面我们讲的,如果你有需要为这个类拓展功能,那就只能使用拓展方法的写法来为该类拓展功能。其实密封类还有另一个好处,就是可以提升性能。如果你的类是密封类,编译器就知道它绝对不会被继承,因此用于虚拟方法的虚拟表可以缩短或者消除,从而达到性能的提升。
密封一个方法,就是派生类不能重写该基类方法。但是如果你在一个普通的类中写一个密封方法或者密封一个属性,那你必须先从基类上把它声明为要重写的方法或者属性。其实也就是说,密封属性或密封方法是对于已经重写了基类方法或属性的方法或属性来说的,是为了防止被该子类的子类继承。例如:
class MyClass1:BaseClass
{
// 密封属性应该是对已重写的方法来说,防止MyClass2重写
public sealed override void Method1();
}
class MyClass2:MyClass1
{
// 它无法去重写Method1()方法
}
我们记录完了派生类的普通函数了,我们来记录下派生类的构造函数。我们知道构造函数一般是用来进行初始化的,如果我们的基类有了自定义的构造函数了,我们的派生类又写了自己定义的构造函数,这个时候,我们就一定要注意层次结构进行构造的问题!
public class Program
{
public string a;
public int b ;
public Program()
{
a ="马爸爸";
b = 55;
}
Program class1 = new Class1(); // 此时a,b就是派生类中改变的a、b值
Program class2 = new Program();// 此时a,b还是基类中的a、b值
}
// 声明一个子类
public class Class1: Program
{
// 这:base ()有没有都可以,如果基类是多参构造,那么就可以:base (参数),用于指定使用哪个构造函数
public Class1():base ()
{
a = "曾欢";
b = 26;
}
}
如果一个类派生自一个接口,声明这个类就会实现某些函数。接口和抽象类很相似,但是抽象类是指拥有没有实现的抽象方法的类,也就是说不一定抽象类里面都是没有实现的方法。而接口不同,接口必须是不允许提供接口中任意成员实现方法。正常情况下,接口只能有方法、属性、索引器和事件的声明。
抽象类用abstract声明,接口用interface声明。接口不能有构造函数,因为接口是没有办法实例化的,也不能有字段,但是可以声明有接口类型的变量。接口还可以彼此继承。