【写给Cpp选手的C#教程】委托篇

2021/10/5 11:11:18

本文主要是介绍【写给Cpp选手的C#教程】委托篇,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

委托的简单使用

本人对委托的理解:C中的函数指针。用一个变量存储函数,方便传递和使用。

按照如下方法使用:

delegate int Dele(int a);
class Program
{
    static int pow(int a) { return a * a; }
    static void Main(string[] args)
    {
        //全写为dele myPow = new dele(pow);
        Dele myPow = pow;
        //全写为myPow.Invoke(3);
        myPow(3);
    }
}

很容易想到,我们可以往函数中传入委托,进行解耦。

参数或返回值为委托的函数被称为高阶函数(high-order function)

delegate int Dele(int x);
class Program
{
    static int Square(int x) { return x * x; }

    static void Main()
    {
        int[] values = { 1, 2, 3 };
        Transform(values, Square);
    }
    //Transform就是一个高阶函数
    static void Transform(int[] values, Dele t)
    {

        for (int i = 0; i < values.Length; i++)
            //values[i] = Square(values[i])
        { values[i] = t(values[i]); }
    }
}

多播委托

我们的委托可以存放一个函数列表,调用时调用整个列表。本质是new一个委托对象,然后重新赋值。

delegate int NumDele(int a);
class Program
{
    static int Pow2(int a) { return a * a; }
    static int Pow3(int a) { return a * a * a; }
    static int Pow4(int a) { return a * a * a * a; }​
        static void Main()
    {
        NumDele d = null;    
        //往函数列表中增加新的函数    
        d += Pow2; d += Pow3; d += Pow4;    
        //现在d中的内容:Pow2->Pow3->Pow4

        //删除指定函数  
        d -= Pow3;
        //现在d中的内容:Pow2->Pow4

        Console.Write(d(3));
        //返回值为3 * 3 * 3 * 3 = 81,Pow2的返回值被丢弃
    }
}

委托和对象

你若细看便会发现,上面给委托赋值的函数全部都是static静态函数。

如果我们将实例A的函数F赋值给委托C,那么委托C不仅需要考虑函数F,还需要考虑实例A

可以通过如下方法获取实例:

delegate int NumDele(int a);
class Source
{
    public int Pow2(int a) { return a * a; }
}
class Program
{
    static void Main()
    {
        Source s = new Source();
        NumDele d = s.Pow2;

        Console.WriteLine(d.Target);//TestSharp.Source
        Console.WriteLine(d.Target == s);//True
        Console.WriteLine(d.Method);//Int32 Pow2(Int32)
    }
}

委托和泛型

委托可以包括泛型类型参数:

public delegate T Dele<T>(T prop);

之后我们就可以进行一些诸如Cpp中algorithm库中的一些,泛型算法的操作(不过下面这个例子并不是泛型算法)

public delegate T Dele<T>(T val);

class Program
{
    static int square(int x) { return x * x; }
    static double devide(double x) { return x / 2; }
    
    static void changeArray<T>(T[] arr, Dele<T> opt)
    {
        for (int i = 0; i < arr.Length; i++)
            arr[i] = opt(arr[i]);
    }
    
    static void Main()
    {
        int[] iArr = { 1, 2, 3 };
        double[] dArr = { 5.4, 2.7, 9.8 };
        //对于int类型的数组,使用square函数对其进行加工
        changeArray(iArr, square); //iArr变成了[1 , 4 , 9]
        //对于double类型的数组,使用devide函数对其进行加工
        changeArray(dArr, devide); //dArr变成了[2.7 , 1.35 , 4.9]
    }
}

如果我们经常需要用到泛型委托,然后每次都需要自己进行定义和声明,那很明显很烦人。后面C#很贴心的减少了我们的套路活,整出了Func<>和Action<>。

对于Func<T1,T2,T3...>而言,最后一个T是返回值,其余的T都是参数。

对于Action<T1,T2,T3...>而言,所有的T都是参数。

//public delegate T Dele<T>(T val);
//static void changeArray<T>(T[] arr, Dele<T> opt)
static void changeArray<T>(T[] arr,Func<T,T> opt) //使用Func的写法,就不需要声明Dele了

委托的兼容性

①委托的类型不同,不能赋值

②委托指向相同函数,则认为其是等价的

public delegate void D1();
public delegate void D2();

class Program
{
    static void Func() {}
    static void Main()
    {
        D1 d1 = Func;
        //D2 d2 = d1;     	如果这么写会报错
        D2 d2 = new D2(d1); //这么写才行
        
        D1 d3 = Func;
        D2 d4 = Func;
        d3==d4  			//True
    }
}

③跟委托绑定的函数,其参数可以是委托的参数的父类、子类

public delegate void StringDele(string s);

class Program
{
    static void ObjectFunc(object o) { }
    static void Main()
    {
        StringDele s = ObjectFunc;
        s("hello world"); //string参数会隐式变换为object参数
    }
}

一般的,传子类是正常的多态行为。传父类被称为逆变。

④跟委托绑定的函数,其返回值可以是委托的返回值的父类、子类

public delegate object objectDele();

class Program
{
    static string strFuc() { return "hello"; }
    static void Main()
    {
        objectDele s = strFuc;
        s();
    }
}

objectDele想要返回object,但如果绑定的函数返回object的子类,那么也是可以的。

函数的返回值是委托返回值的子类,这种情况被称为协变。

⑤泛型委托相关

我们需要将只用于返回值的类型参数标记为协变(out),将只用于参数的类型参数标记为逆变(in)

其实,Func和Action的定义可以如下理解:

delegate TResult Func<in T1,in T2,...,out TResult>(T1 prop1,T2 prop2,...);
delegate void Action<in T1,in T2,...>(T1 prop1,T2 prop2,...);

然后吧,我们的泛型委托也可以如同之前定义的委托那样,完成逆变和协变了。



这篇关于【写给Cpp选手的C#教程】委托篇的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程