委托
委托类似C++的函数指针,但是函数指针不安全。C#中的委托就是一个新类,每次定义的时候都等于声明一个类。可以这么理解:委托类中储存函数元数据(返回值,参数,函数名),初始化的过程就是给函数具体的内容(就是存的内容是函数指针)。
-
定义和初始化委托
public delegate string GetString(); class Program { static void Main(string[] args) { var a = 10; GetString getString = new GetString(a.ToString); Console.WriteLine(getString()); } }
调用实例方法时,可以调用实例的属性。
- 匿名委托
委托中需要储存的有返回值和参数,考虑用泛型:Action<T>
void,返回值由T来定Fuction<T>
eg:Fuction<int,int,bool>
最后一个参数为返回值 - 参数修饰符 in out ref
假设一个顾客要衣物工厂生产衣服
-
in
public delegate Cloth Produce(in ISource _source)
函数的含义就是顾客给原材料,厂商生产衣服,但是厂商不能动顾客的原材料。顾客给的是什么,执行函数后还是原样。
-
out
public delegate void Produce(in ISource _source,out Cloth _cloth)
函数的含义就是顾客不仅给原材料,还会给一个衣服。至于是什么样的衣服,或者是根本就没有衣服,厂商不仅不能动原材料,还要产出衣服给out参数。
-
ref
public delegate void Produce(in ISource _source,ref ISource _otherSource,out Cloth _cloth)
函数的含义就是顾客给两种原材料,in的不能动,ref的可以修改,返回Cloth
-
多播委托
委托重构了+、+=、-、-=。但是调用函数指针的顺序却没有定,且一个函数抛出异常整个迭代过程都会停止。GetString getString1=()=>Console.WriteLine("nihao"); GetString getString2 = () => Console.WriteLine("woshi"); GetString getString3 = () => Console.WriteLine("robot"); GetString getString = getString1 + getString2 + getString3; getString();
可以用Delegate类中的GetInvocationList()
Delegate[] delegates = getString.GetInvocationList(); foreach (GetString i in delegates) { try { i(); } catch (Exception e) { Console.WriteLine("wrong"); } }
泛型
泛型没什么好说的,首先要了解两个概念:1.值类型与引用类型。2.装箱与拆箱。
-
值类型与引用类型
C#和Java一样万物都是类,即使是值类型也是继承自ValueType->Object。但是编译器会对继承自ValueType的类特殊处理。值类型储存在栈上,引用类型储存在堆上。值类型是一般意义的非面向对象的数据结构,仅仅是服务于计算。引用类型服务于整个项目工程。 -
装箱与拆箱
int i = 10; object j = i; Console.WriteLine(i + " " + j);
考虑上述语句:虽然输出值是一样的,但i是值类型,存于栈中;j是引用类型,存于堆中。
把值类型转化为引用类型的操作就是装箱,反正为拆箱。从栈到堆要重新申请地址填写元数据,等于就是又生成了一个变量,增大了性能开销。
为了解决这种浪费问题,可以通过构造函数、自定义函数传参。但是这样的话,针对不同类型的变量就要写许许多多函数。泛型就是用来解决代码重复问题,也是loc控制反转、di依赖注入思想的体现。
- 泛型类
-
继承
public class Student<T> : Person<T>
和一般类的继承是一样的,但是泛型的继承要求T是相同的类。还有种指定父类泛型的继承:
public class Student<T1> : Person<T1,string>
注意泛型个数的变化。可以理解为每一种不同T的泛型都是不同的,独立的类。比如静态属性:
-
静态属性
Student<int>.i = 10; Student<string>.i= 6; Console.WriteLine(Student<int>.i);
互相独立无法影响。
C#中无法访问属性一样,通过实例访问静态属性,因为静态属性是属于整个类的。 -
默认值
public T name=default(T);
值类型为0,引用为null
- 泛型方法
前两种准确来说并不是泛型方法
-
返回值是泛型
public Person<T> GetPerson()
-
参数是泛型
public class Student<T> : Person<T> { static int j; public Person<T> GetPerson(T a) { var temp = new Person<T>(); temp.name=a; return temp; } }
还是一个原则T要相同。
-
泛型方法
public Person<T> GetPerson<T>() { var temp = new Person<T>(); return temp; }
泛型方法的目的也是为了减少代码重用。不同的泛型类调用不同的方法
- 泛型接口
-
协变与抗变
首先在看协变与抗变之前先回顾类与子类:Student<string> s = new Student<string>(); Person<string> p = s; Person<string> i = new Person<string>(); Student<string> j = i;
前一种可以编译通过,因为可以把子类的地址赋给父类的变量。但反过来就不可以了。这就是一种简单的协变与抗变。因为子类是从父类派生的。
方法也有协变与抗变:方法的参数是协变的,可以给父类参数赋子类对象的值,这也是依赖注入的实现方式。方法的返回值是抗变的,返回值最好是返回精确的类型。输入可以不精确,但是输出一定要确定。public interface IMyInterface<out T>
这样定义的泛型类就可以对参数协变
public interface IMyInterface
这样的
- 泛型结构
最常用的泛型结构就是可空类型Nullable