C#委托、委托的实例化、多播委托、委托的调用、内置委托、泛滥委托

ModelEngine·创作计划征文活动 10w+人浏览 1.4k人参与

*委托:

c#中的委托类似于c或者c++中函数的指针。

委托是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变(可以理解为是一种数据类型)

委托用于实现事件和回调方法

// 声明一个委托类型MyDelegate
// 委托是一种引用类型,用于封装具有特定参数列表和返回类型的方法
// 格式:访问修饰符 delegate 返回值类型 委托名称(参数列表)
// 这里声明的委托可以指向任何返回bool类型且带有一个string参数的方法
public delegate bool MyDelegate(string v);

// 命名空间:用于组织代码,避免命名冲突
namespace 委托
{
    // 程序主类
    internal class Program
    {
        // 程序入口点
        static void Main(string[] args)
        {
            // 演示委托的使用:将方法作为参数传递给另一个方法
            // 这里将MyFn方法作为参数传递给Test.TestFn
            Test.TestFn(MyFn);
            // 这里将MyFn1方法作为参数传递给Test.TestFn
            Test.TestFn(MyFn1);
        }

        // 定义一个符合MyDelegate委托签名的方法
        // 签名匹配:返回值为bool,参数为一个string
        static bool MyFn(string a)
        { Console.WriteLine("印度制造" + a);
            return true; }

        // 定义另一个符合MyDelegate委托签名的方法
        static bool MyFn1(string b)
        { Console.WriteLine("中国制造" + b);
            return true; }
    }
    // 测试类,用于演示委托的接收和使用
    class Test
    {
        // 定义一个静态方法,参数为MyDelegate类型
        // 这意味着该方法可以接受任何符合MyDelegate签名的方法作为参数
        public static void TestFn(MyDelegate f)
        {
            // 通过委托变量调用所引用的方法
            // 这里传递固定参数"苹果手机"给被委托的方法
            f("苹果手机");
        }
    }
}
•委托的实例化:
// 创建第一个委托类型MyFns
// 该委托可以指向:无返回值(void),参数为int和string的方法
delegate void MyFns(int v1, string v2);

// 创建第二个委托类型Number
// 该委托可以指向:返回值为int,参数为两个int的方法
delegate int Number(int v1, int v2);

// 命名空间:组织相关代码
namespace 委托的实例化
{
    // 程序主类
    internal class Program
    {
        // 程序入口方法
        static void Main(string[] args)
        {
            // 创建Test类的实例,会自动调用其构造函数
            new Test();
        }
    }

    // 测试类,用于演示委托的实例化和使用
    class Test
    {
        // Test类的构造函数
        public Test()
        {
            // 1. 完整语法实例化MyFns委托
            // 将Fn1方法作为参数传递给委托构造函数
            MyFns fns1 = new MyFns(Fn1);

            // 直接调用Fn1方法(普通方法调用)
            Fn1(1, "吴亦凡");

            // 通过委托变量调用方法(委托调用方式1)
            // 委托变量可以像方法一样直接调用
            fns1(1, "蔡徐坤");

            // 2. 实例化Number委托并使用
            // 将Sum方法关联到Number类型的委托变量
            Number number = new Number(Sum);

            // 直接调用Sum方法
            Console.WriteLine(Sum(10, 20));

            // 通过委托调用Sum方法
            Console.WriteLine(number(20, 30));

            // 3. 委托实例化的简写形式
            // 不需要显式使用new关键字,直接赋值方法名
            Number number1 = Sum;

            // 通过简写形式创建的委托调用方法
            Console.WriteLine(number1(5, 6));

            // 4. 使用Invoke方法调用委托(委托调用方式2)
            // 这是委托调用的标准写法,与直接调用等效
            Console.WriteLine(number1.Invoke(1, 3));
        }

        // 符合MyFns委托签名的方法
        // 无返回值,参数为int和string
        void Fn1(int a, string b)
        { Console.WriteLine($"a={a},b={b}"); }

        // 符合Number委托签名的方法(乘法)
        int Cheng(int a, int b)
        { return a * b; }

        // 符合Number委托签名的方法(加法)
        int Sum(int a, int b)
        { return a + b; }
    }
}
•多播委托:
// 声明一个委托类型MyDelegate
// 该委托可指向:无返回值,参数为string的方法
delegate void MyDelegate(string name);

// 命名空间:多播委托演示
namespace _多播委托
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // 多播委托:可以包含多个方法的委托实例
            // 当调用多播委托时,会按添加顺序依次执行所有包含的方法

            // 1. 基础多播委托示例
            // 创建委托实例并关联第一个方法Fn1
            MyDelegate fns = new MyDelegate(Fn1);
            // 使用+=运算符添加第二个方法(Test类的实例方法Fn2)
            fns += new MyDelegate(new Test().Fn2);
            // 调用多播委托:会依次执行Fn1和Fn2,并将参数"吴亦凡"传递给两个方法
            fns("吴亦凡");

            // 2. 多播委托简写形式
            // 简写方式创建委托并关联第一个方法
            MyDelegate fns1 = Fn1;
            // 简写方式添加第二个方法
            fns1 += new Test().Fn2;
            // 调用多播委托
            fns1("蔡徐坤");
            Console.WriteLine("-----------------");

            // 3. 多播委托移除方法的注意事项(错误示例)
            MyDelegate fns2 = Fn1;
            fns2 += new Test().Fn2;  // 添加一个匿名Test实例的Fn2方法
            fns2 -= new Test().Fn2;  // 尝试移除,但这里创建了新的Test实例,与添加时的实例不同
            // 实际执行时Fn2仍会被调用,因为移除的不是同一个实例的方法
            fns2.Invoke("范冰冰");

            // 4. 解释上述错误的原因
            MyDelegate fns4 = Fn1;
            Test t1 = new Test();  // 创建第一个Test实例
            Test t2 = new Test();  // 创建第二个Test实例(与t1不是同一个对象)
            fns4 += t1.Fn2;        // 添加t1实例的Fn2
            fns4 -= t2.Fn2;        // 尝试移除t2实例的Fn2(与添加的不是同一个方法引用)
            // 结果:t1.Fn2仍会被执行,因为移除的不是同一个方法

            // 5. 正确移除实例方法的解决方案
            Console.WriteLine("---------------------");
            MyDelegate fns3 = Fn1;
            Test t = new Test();    // 创建一个Test实例并保存引用
            fns3 += t.Fn2;          // 添加该实例的Fn2
            fns3 -= t.Fn2;          // 移除同一个实例的Fn2(正确方式)
            fns3.Invoke("abc");     // 此时只会执行Fn1

            // 6. 移除静态方法的示例(静态方法无需实例,直接通过类名访问)
            Console.WriteLine("---------------------");
            MyDelegate fns5 = Fn1;
            fns5 += Test.Fn3;       // 添加静态方法Fn3
            fns5 -= Test.Fn3;       // 移除静态方法Fn3(静态方法移除无需考虑实例)
            fns5("cba");            // 此时只会执行Fn1
        }

        // 符合MyDelegate委托签名的静态方法
        public static void Fn1(string v)
        { Console.WriteLine($"这是Fn1中的v:{v}"); }
    }

    // 测试类,包含实例方法和静态方法
    class Test
    {
        // 实例方法:必须通过类的实例调用
        public void Fn2(string v)
        { Console.WriteLine($"这是Fn2中的v:{v}"); }

        // 静态方法:可直接通过类名调用,无需实例
        public static void Fn3(string v)
        { Console.WriteLine($"这是Fn3中的v:{v}"); }
    }
}
•委托的调用:
// 命名空间:演示委托的各种调用方式
namespace 委托的调用
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // 创建Test类的实例
            Test t = new Test();
            // 调用Run方法,该方法中包含委托调用的演示逻辑
            t.Run();
        }
    }

    class Test
    {
        // 声明一个委托类型MyDelegate
        // 该委托可指向:返回值为int,参数为int的方法
        public delegate int MyDelegate(int x);

        // 声明一个MyDelegate类型的属性,用于存储委托方法
        public MyDelegate Fns { get; set; }

        // 构造函数:初始化委托并添加方法
        public Test()
        {
            // 向委托属性中添加方法(多播委托)
            Fns += Fn1;  // 关联Fn1方法
            Fns += Fn2;  // 关联Fn2方法
        }

        // 符合MyDelegate委托签名的方法1
        public int Fn1(int v)
        {
            Console.WriteLine(v * 10);  // 输出输入值的10倍
            return v * 10;              // 返回计算结果
        }

        // 符合MyDelegate委托签名的方法2
        public int Fn2(int v)
        {
            Console.WriteLine(v * v);   // 输出输入值的平方
            return v * v;               // 返回计算结果
        }

        // 声明一个People类型的字段(未初始化,默认值为null)
        People p;

        public void Run()
        {
            // 1. 直接调用委托(多播委托会按添加顺序执行所有方法)
            // Fns(20);  // 此行与下面的Invoke调用等效

            // 2. 安全调用委托的方式1:判断委托是否为null
            /*if (Fns != null)
            {
                Fns(20);  // 当委托不为null时才调用,避免NullReferenceException
            }*/

            // 3. 安全调用委托的方式2:使用空值条件运算符?.
            // 当Fns为null时,不执行Invoke,也不会报错
            Fns?.Invoke(20);


            // 空值条件运算符?.的其他用法:访问可能为null的对象的成员
            // 当p为null时,p?.Name会返回null,不会抛出异常
            Console.WriteLine(p?.Name == null);  // 此时p为null,输出True

            // 初始化p并赋值
            p = new People();
            p.Name = "蔡徐坤";
            
            // 当p不为null时,正常访问Name属性
            Console.WriteLine(p?.Name);  // 输出"蔡徐坤"
        }
    }

    // 辅助类:用于演示空值运算符的使用
    class People
    { public string Name { get; set; }  // Name属性
    }
}
•内置委托:
// 命名空间:演示委托的各种调用方式
namespace 委托的调用
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // 创建Test类的实例
            Test t = new Test();
            // 调用Run方法,该方法中包含委托调用的演示逻辑
            t.Run();
        }
    }

    class Test
    {
        // 声明一个委托类型MyDelegate
        // 该委托可指向:返回值为int,参数为int的方法
        public delegate int MyDelegate(int x);

        // 声明一个MyDelegate类型的属性,用于存储委托方法
        public MyDelegate Fns { get; set; }

        // 构造函数:初始化委托并添加方法
        public Test()
        {
            // 向委托属性中添加方法(多播委托)
            Fns += Fn1;  // 关联Fn1方法
            Fns += Fn2;  // 关联Fn2方法
        }

        // 符合MyDelegate委托签名的方法1
        public int Fn1(int v)
        {
            Console.WriteLine(v * 10);  // 输出输入值的10倍
            return v * 10;              // 返回计算结果
        }

        // 符合MyDelegate委托签名的方法2
        public int Fn2(int v)
        {
            Console.WriteLine(v * v);   // 输出输入值的平方
            return v * v;               // 返回计算结果
        }

        // 声明一个People类型的字段(未初始化,默认值为null)
        People p;

        public void Run()
        {
            // 1. 直接调用委托(多播委托会按添加顺序执行所有方法)
            // Fns(20);  // 此行与下面的Invoke调用等效

            // 2. 安全调用委托的方式1:判断委托是否为null
            /*if (Fns != null)
            {
                Fns(20);  // 当委托不为null时才调用,避免NullReferenceException
            }*/

            // 3. 安全调用委托的方式2:使用空值条件运算符?.
            // 当Fns为null时,不执行Invoke,也不会报错
            Fns?.Invoke(20);


            // 空值条件运算符?.的其他用法:访问可能为null的对象的成员
            // 当p为null时,p?.Name会返回null,不会抛出异常
            Console.WriteLine(p?.Name == null);  // 此时p为null,输出True

            // 初始化p并赋值
            p = new People();
            p.Name = "蔡徐坤";
            
            // 当p不为null时,正常访问Name属性
            Console.WriteLine(p?.Name);  // 输出"蔡徐坤"
        }
    }

    // 辅助类:用于演示空值运算符的使用
    class People
    { public string Name { get; set; }  // Name属性
    }
}
•泛滥委托:
// 声明泛型委托MyDelegate<T>
// 泛型委托可以适用于多种数据类型,增强了代码的复用性
// 该委托接受一个T类型的参数,返回bool类型
delegate bool MyDelegate<T>(T v);

// 命名空间:泛型委托演示
namespace 泛型委托
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // 字符串数组示例
            string[] strings = { "徐坤1", "蔡徐坤", "吴亦凡" };
            // 注释:使用泛型方法筛选以"蔡"开头的字符串
            /*string s = FnList<string>(strings, Fn1);
            Console.WriteLine(s);*/

            // 整数数组示例:使用泛型委托筛选数组中的偶数
            int[] ints = { 1, 2, 3, 4, 5, 6 };
            // 调用泛型方法FnList,传入int数组和筛选方法Fn2
            List<int> resultList = FnList(ints, Fn2);
            
            // 输出筛选结果
            foreach (var item in resultList)
            { Console.WriteLine(item); }
        }

        // 字符串筛选方法:判断字符串是否以"蔡"开头
        // 符合MyDelegate<string>或Func<string, bool>的签名
        static bool Fn1(string name)
        { return name.StartsWith("蔡"); }

        // 整数筛选方法:判断整数是否为偶数
        // 符合MyDelegate<int>或Func<int, bool>的签名
        static bool Fn2(int value)
        { return value % 2 == 0; }

        // 泛型方法:使用泛型委托筛选数组元素
        // T:泛型类型参数,可代表任意数据类型
        // 参数1:T类型的数组,需要筛选的数据源
        // 参数2:Func<T, bool>委托,用于定义筛选条件(也可使用自定义的MyDelegate<T>)
        public static List<T> FnList<T>(T[] Arrs, Func<T, bool> filterFunc)
        {
            // 创建一个列表用于存储筛选结果
            List<T> resultList = new List<T>();
            
            // 遍历数组中的每个元素
            for (int i = 0; i < Arrs.Length; i++)
            {
                // 调用委托方法判断当前元素是否符合筛选条件
                // 如果符合条件(返回true),则添加到结果列表
                if (filterFunc(Arrs[i]))
                { resultList.Add(Arrs[i]); }
            }
            
            // 返回筛选后的结果列表
            return resultList;
        }
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值