委托和泛型

委托

委托类似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
    假设一个顾客要衣物工厂生产衣服

  1. in

    public delegate Cloth Produce(in ISource _source)
    函数的含义就是顾客给原材料,厂商生产衣服,但是厂商不能动顾客的原材料。顾客给的是什么,执行函数后还是原样。

  2. out

    public delegate void Produce(in ISource _source,out Cloth _cloth)
    函数的含义就是顾客不仅给原材料,还会给一个衣服。至于是什么样的衣服,或者是根本就没有衣服,厂商不仅不能动原材料,还要产出衣服给out参数。

  3. 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.装箱与拆箱。

  1. 值类型与引用类型
    C#和Java一样万物都是类,即使是值类型也是继承自ValueType->Object。但是编译器会对继承自ValueType的类特殊处理。值类型储存在栈上,引用类型储存在堆上。值类型是一般意义的非面向对象的数据结构,仅仅是服务于计算。引用类型服务于整个项目工程。

  2. 装箱与拆箱

    int i = 10;
    object j = i;
    Console.WriteLine(i + " " + j);

    考虑上述语句:虽然输出值是一样的,但i是值类型,存于栈中;j是引用类型,存于堆中。
    把值类型转化为引用类型的操作就是装箱,反正为拆箱。从栈到堆要重新申请地址填写元数据,等于就是又生成了一个变量,增大了性能开销。
    为了解决这种浪费问题,可以通过构造函数、自定义函数传参。但是这样的话,针对不同类型的变量就要写许许多多函数。泛型就是用来解决代码重复问题,也是loc控制反转、di依赖注入思想的体现。

  • 泛型类
  1. 继承

    public class Student : Person
    和一般类的继承是一样的,但是泛型的继承要求T是相同的类。还有种指定父类泛型的继承:

    public class Student : Person<T1,string>
    注意泛型个数的变化。可以理解为每一种不同T的泛型都是不同的,独立的类。比如静态属性:

  2. 静态属性

    Student.i = 10;
    Student.i= 6;
    Console.WriteLine(Student.i);
    互相独立无法影响。
    C#中无法访问属性一样,通过实例访问静态属性,因为静态属性是属于整个类的。

  3. 默认值

    public T name=default(T);
    值类型为0,引用为null

  • 泛型方法
    前两种准确来说并不是泛型方法
  1. 返回值是泛型

    public Person GetPerson()

  2. 参数是泛型

    public class Student : Person
    {
    static int j;
    public Person GetPerson(T a)
    {
    var temp = new Person();
    temp.name=a;
    return temp;
    }
    }
    还是一个原则T要相同。

  3. 泛型方法

    public Person GetPerson()
    {
    var temp = new Person();
    return temp;
    }
    泛型方法的目的也是为了减少代码重用。不同的泛型类调用不同的方法

  • 泛型接口
  1. 协变与抗变
    首先在看协变与抗变之前先回顾类与子类:

    Student s = new Student();
    Person p = s;

    Person i = new Person();
    Student j = i;
    前一种可以编译通过,因为可以把子类的地址赋给父类的变量。但反过来就不可以了。这就是一种简单的协变与抗变。因为子类是从父类派生的。
    方法也有协变与抗变:方法的参数是协变的,可以给父类参数赋子类对象的值,这也是依赖注入的实现方式。方法的返回值是抗变的,返回值最好是返回精确的类型。输入可以不精确,但是输出一定要确定。

    public interface IMyInterface
    这样定义的泛型类就可以对参数协变
    public interface IMyInterface
    这样的

  • 泛型结构
    最常用的泛型结构就是可空类型Nullable
posted @ 2021-09-26 17:54  none323  阅读(133)  评论(0)    收藏  举报