本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
把C#部分的知识点记录下来,以供日后复习。
菜鸟教程
可空类型
总结:
声明类型后面加?可以让不能为空的值为空。
??和?.使用方法看代码示例。
可空类型
示例代码:
int? a;
a = null;
b = a??1;//如果a为空则返回1
//如果testClass为空则不会调用DoSomething方法
//省掉了判断testClass是否为空的步骤
testClass?.DoSomething();
继承
总结:
C#不支持多重继承,但是可以继承多个接口。
实例只能向上兼容(子实父)。
派生类必须继承父类的构造函数,默认继承的无参构造函数。派生类继承构造函数时需要传入父类构造函数所需的参数,具体看代码示例。
继承
示例代码:
class Father
{
public Father(int a)
{
Console.WriteLine("Father:{0}",a);
}
public virtual void DoFather()
{
Console.WriteLine("Father");
}
}
class Son:Father
{
public Son(int a) : base(a) // a都是这里的int a
{
Console.WriteLine("Son:{0}", a);
}
public override void DoFather()
{
Console.WriteLine("Son");
}
}
class Program
{
static void Main(string[] args)
{
Father father = new Father(1);
father.DoFather(); //输出 Father:1 Father
Son son = new Son(1);
son.DoFather(); //输出 Father:1 Son1 Son
//Error 子类无法用父类实例
//Son sf = new Father() as Son;
//sf.DoFather();
Father fs = new Son(1);
fs.DoFather(); //输出 Father1 Son1 Son 和上一个相同
}
}
多态
总结:
1.多态是同一个行为具有多个不同表现形式或形态的能力。实现多态的手段:抽象类、虚方法、接口。
2.abstract类中可以包含抽象字段,抽象方法。也可以包含实现了的方法和字段(非abstract)。
3.abstract和virtual的区别:abstract是抽象,只需要声明不需要实现部分,它必须被覆写。virtual是虚方法。必须有实现部分(即使是空的大括号),可以不被覆写。
4.new和override的区别:new是隐藏方法,根据声明的class调用方法,如果声明是父类,即使是用子类实例的,也调用的父类。override是重写方法,看用的哪个类实例调用哪个类。
5.方法重载(overload)有三个条件,方法名必须相同,参数列表必须不相同,返回值类型可以不相同。(二必须一可以)
多态
运算符重载
总结:
1.是静态方法,是重载不是覆写。
2.注意需要的参数和返回值。
3.使用operator关键字,后面跟上要重载的操作符。
4.不是所有操作符都可以重载,详细内容看教程。
运算符重载
示例代码:
public class Program
{
static void Main(string[] args)
{
OperatorTest a = new OperatorTest();
a.num = 5;
OperatorTest b = new OperatorTest();
b.num = 6;
OperatorTest c = a + b;
Console.WriteLine(c.num);
Console.ReadKey();
}
}
public class OperatorTest
{
public int num;
//运算符重载
public static OperatorTest operator +(OperatorTest a, OperatorTest b)
{
OperatorTest test = new OperatorTest();
test.num = a.num + b.num;
return test;
}
}
接口(Interface)
总结:
1.接口只是声明,不能有实现部分。继承类负责实现。
2.接口中可以定义事件,索引器,属性,方法。
3.默认访问权限为public。
4.接口之间可以继承。
5.通常接口命名以“I”开头。
6.is用于判断是否能转换,如if(a is b);as用于转换,不会报错,如果转换不了会返回null,如P p = a as P;
接口(Interface)
示例代码:
//简单使用
class Program
{
static void Main(string[] args)
{
IWorker james1 = new James1();
IWorker james2 = new James2();
james1.work("设计");
james2.work("编程");
//从这个例子我体会到了有接口的好处,可以想象如果又来了新的员工。
//如果不采用接口,而是每个员工都有一个单独的类,这样就会容易出错。
//如果有接口这种协议约束的话,那么只要实现了接口就肯定有接口里声明的方法,我们只需拿来调用。
}
}
public interface IWorker{ void work(string s); }
class James1 : IWorker
{
public void work(string s)
{
Console.WriteLine("我的名字是James1,我的工作是" +s);
}
}
class James2 : IWorker
{
public void work(string s)
{
Console.WriteLine("我的名字是James2,我的工作是"+s);
}
}
//接口继承
interface IParentInterface
{
void ParentInterfaceMethod();
}
interface IMyInterface : IParentInterface
{
void MethodToImplement();
}
class InterfaceImplementer : IMyInterface
{
static void Main()
{
InterfaceImplementer iImp = new InterfaceImplementer();
iImp.MethodToImplement();
iImp.ParentInterfaceMethod();
}
public void MethodToImplement()
{
Console.WriteLine("MethodToImplement() called.");
}
public void ParentInterfaceMethod()
{
Console.WriteLine("ParentInterfaceMethod() called.");
}
//接口中定义事件,索引器,属性,方法;
delegate void Delegate();
interface I<T>
{
event Delegate DE;
T this[int i]
{
get;
set;
}
List<T> age
{
get;
set;
}
void Say();
}
class c : I<c>
{
public event Delegate DE;
public c this[int i]
{
get { return age[i]; }
set { DE(); Say(); age[i] = value; }
}
public List<c> age
{
get;
set;
}
public void Say()
{
Console.WriteLine("123");
}
}
class b : I<c>
{
public event Delegate DE;
public c this[int i]
{
get { return age[i]; }
set { DE();Say(); age[i] = value; }
}
public List<c> age
{
get;
set;
}
public void Say()
{
Console.WriteLine("456");
}
}
class Program
{
static void Main(string[] args)
{
I<c> Ic = new c();
I<c> lb = new b();
Ic.DE += deC;
lb.DE += deB;
lb = Ic;
lb.age = new List<c>();
lb.age.Add(new c());
lb[0] = new c();
Console.WriteLine("S");
Console.ReadKey();
}
static void deC()
{
Console.WriteLine("c");
}
static void deB()
{
Console.WriteLine("B");
}
}
预处理指令
总结:
1.一行只能有一个预处理器指令。
2.#define只在当前声明类生效。
3.预处理指令一般用于程序的调试和运行。
4.Unity可以使用#if UNITY_EDITOR等判断运行环境来确定是否执行后续代码。
预处理指令
示例代码:
#define test
using System;
namespace 预处理指令
{
class Program
{
static void Main(string[] args)
{
#if (test)
Console.WriteLine("test is define");
#else
Console.WriteLine("test is not define");
#endif
//#region可以折叠代码
#region 折叠
Console.ReadKey();
#endregion
}
}
}
C# 特性(Attribute)
总结:
1.特性分为两种,预定义特性和自定义特性。
2.预定义特性有AttributeUsage、Conditional、Obsolete、DllImport(用来标记非.NET的函数,表明该方法在一个外部的DLL中定义)。
3.每个特性至少有一个构造函数。
4.具体使用看下面代码,详细讲解看菜鸟教程。
C# 特性(Attribute)
示例代码:
// 描述如何使用一个自定义特性 SomethingAttribute
//参数1:支持在什么类型上加的特性;参数2:是否允许重复加该特性;参数3:是否支持特性继承;
[AttributeUsage(AttributeTargets.All , AllowMultiple = true , Inherited = true )]
//********自定义特性SomethingAttribute**************//
public class SomethingAttribute : Attribute
{
private string name; // 名字
private string data; // 日期
public string Name
{
get { return name; }
set { name = value; }
}
public string Data
{
get { return data; }
set { data = value; }
}
public SomethingAttribute(string name)
{
this.name = name;
}
}
//实例化自定义
//自动忽略名字中的Attribute部分,但是当作类使用的时候需要全名
[Something("Amy", Data = "2018-06-18")]
[Something("Jack", Data = "2018-06-18")]
class Test { }
class Program
{
static void Main(string[] args)
{
//获取自定义特性的中的变量
Type t = typeof(Test);
//这里是全名
var something = t.GetCustomAttributes(typeof(SomethingAttribute), true);
foreach (SomethingAttribute each in something)
{
Console.WriteLine("Name:{0}", each.Name);
Console.WriteLine("Data:{0}", each.Data);
Program p = new Program();
p.TestConditional();
p.OldMethod();
}
//如果存在此预定义符号(#define TEST),则执行此方法,没有则不执行,有点类似#if
//只能用于方法或特性类
[Conditional("TEST")]
public void TestConditional()
{
Console.WriteLine("Conditional test");
}
//true直接报错 false只是提醒,默认为false
[Obsolete("这个方法过时了!",false)]
public void OldMethod()
{
Console.WriteLine("Old");
}
}
//引用第三方类库 比如unity接入IOS就有用到
[DllImport("User32.dll")]
public static extern int MessageBox(int hParent, string Message, string Caption, int Type);
}
C# 索引器(Indexer)
总结:
1.索引器的用法类似属性。
2.索引器可以带有多个参数,并且支持多种类型。
3.作用相当于根据类的索引取对应的值。
C# 索引器(Indexer)
示例代码:
class IndexedNames
{
private string[] namelist = new string[size];
static public int size = 10;
public IndexedNames()
{
for (int i = 0; i < size; i++)
{
namelist[i] = "N. A.";
}
}
public string this[int index]
{
get
{
string tmp;
if( index >= 0 && index <= size-1 )
{
tmp = namelist[index];
}
else
{
tmp = "";
}
return ( tmp );
}
set
{
if( index >= 0 && index <= size-1 )
{
namelist[index] = value;
}
}
}
public int this[string name]
{
get
{
int index = 0;
while(index < size)
{
if (namelist[index] == name)
{
return index;
}
index++;
}
return index;
}
}
static void Main(string[] args)
{
IndexedNames names = new IndexedNames();
names[0] = "Zara";
names[1] = "Riz";
names[2] = "Nuha";
names[3] = "Asif";
names[4] = "Davinder";
names[5] = "Sunil";
names[6] = "Rubic";
// 使用带有 int 参数的第一个索引器
for (int i = 0; i < IndexedNames.size; i++)
{
Console.WriteLine(names[i]);
}
// 使用带有 string 参数的第二个索引器
Console.WriteLine(names["Nuha"]);
Console.ReadKey();
}
}
//输出
//Zara
//Riz
//Nuha
//Asif
//Davinder
//Sunil
//Rubic
//N. A.
//N. A.
//N. A.
//2
C# 委托(Delegate)
总结:
1.起到存储方法的作用。
2.委托可指向一个与其具有相同标签的方法。
3.委托对象必须使用 new 关键字来创建,且与一个特定的方法有关。当创建委托时,传递到 new 语句的参数就像方法调用一样书写,但是不带有参数。
4.可以创建一个委托被调用时要调用的方法的调用列表。这被称为委托的 多播(multicasting),也叫组播,通过+=、-=删除。
5.定义一个委托相当于定义一个新类,所有可以定义类的地方都可以定义委托。
C# 委托(Delegate)
示例代码:
delegate int NumberChanger(int n);
namespace DelegateAppl
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}
public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}
static void Main(string[] args)
{
// 创建委托实例
NumberChanger nc;
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
nc = nc1;
nc += nc2;
// 调用多播
nc(5);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
}
C# 事件(Event)
总结:
1.用法与委托类似。
2.Event只能+=或-=,不允许=;委托允许使用=覆盖。
3.event 只能在类内定义,或者接口内定义,而delegate可以在类外类外定义,但不能在接口内,因为接口中只能包含成员。
4.委托定义的是一个类型,而事件定义的是一个成员。
C# 事件(Event)
示例代码:
class DelegateTest
{
public delegate void delegate_tz();
public event delegate_tz delegate_tz0;
public void start()
{
Console.WriteLine("启动事件");
delegate_tz0(); // 得调用该事件呀
Console.ReadKey();
}
}
class Program
{
static void Main(string[] args)
{
DelegateTest DelegateTest0 = new DelegateTest();
//DelegateTest0.delegate_tz0 += DelegateTest.delegate_tz(test); // 必须new一下才行,因为它是另外一个类呀
DelegateTest0.delegate_tz0 += new DelegateTest.delegate_tz(test);
DelegateTest0.start(); // 启动事件
}
static public void test()
{
Console.WriteLine("这是一个被注册的函数,按任意键继续...");
}
}
C# 泛型(Generic)
总结:
1.它有助于您最大限度地重用代码、保护类型的安全以及提高性能。
2.可以创建泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
3.可以对泛型类进行约束以访问特定数据类型的方法。
4.where指定对用作泛型类型、方法、委托或本地函数中类型参数的参数类型的约束。[where] (docs.microsoft.com/zh-cn/dotne…)
C# 泛型(Generic)
示例代码:
delegate T NumberChanger<T>(T n);
public class MyGenericArray<T> where T : notnull
{
private T[] array;
public MyGenericArray(int size)
{
array = new T[size + 1];
}
public T getItem(int index)
{
return array[index];
}
public void setItem(int index, T value)
{
array[index] = value;
}
}
C# 匿名方法
对方法的简写。不过基本被Lambda表达式代替,实际开发中很少使用(对我而言)。
C# 匿名方法
示例代码:
delegate void Test(int n);
Test t = delegate(int x)
{
Console.WriteLine(x);
};
C# Lambda表达式
匿名方法的升级版,更简单粗暴。
示例代码:
delegate void Test(int n);
Test t = _ => Console.WriteLine(_);
//_为参数
//执行代码只有一行时可以省略大括号,如果有多行执行代码需要加大括号。