IT邦第二篇 就献给委派了
记得当年第一次看到 += 这东西的时候
问问前辈这是什麽
前辈只有跟我说 : 委派 很可怕 不要用~~~
真正深入了解之後才觉得相见恨晚啊!
先从委派最正规的方式写起(但我几乎不用这种方式...)
public delegate void DoSomething(int number);
先宣告一个委派的签章 这个签章代表晚点要赋予它的方法
必须符合 无回传值(void) 且 具有一个参数 int
换句话说 如果宣告成如下
public delegate int ParseSomething(string str);
就代表 该方法必须回传int 且 具有一个参数 string
public static void PrintNumber(int n)
{
Console.WriteLine(n);
}
public static void SquareAndPrintNumber(int n)
{
n *= n;
Console.WriteLine(n);
}
static void Main(string[] args)
{
var something = new DoSomething(PrintNumber);
something.Invoke(5);
something(6);
}
我宣告了两个方法 PrintNumber 跟 SquareAndPrintNumber
都符合DoSomething的签章 (无回传 且具有一个参数int)
在这个范例中 先使用PrintNumber
var something = new DoSomething(PrintNumber);就是建立一个委派 并把PrintNumber传入
something.Invoke(5);就是真正去执行这个委派 PrintNumber 所以最後会印出 5
something(6); 是执行委派的另外一个方式 所以会印出6
我们修改一下程序码 将 PrintNumber更换成 SquareAndPrintNumber
static void Main(string[] args)
{
var something = new DoSomething(SquareAndPrintNumber);
something.Invoke(5);
something(6);
}
这时候要执行的方法就会变成SquareAndPrintNumber
所以印出来会是 25 36
我们再做一个实验 如果有两个委派 具有相同的签章 他们是否可以互通呢?
我们修改一下程序码 新增一个DoSomething1 跟 DoSomething一模一样 只是名称不同
再修改一下呼叫委派的方式
public delegate void DoSomething1(int number);
public static void ExeDoSomething(DoSomething something)//呼叫委派的方式
{
something.Invoke(5);
something(6);
}
static void Main(string[] args)
{
{
var something = new DoSomething(PrintNumber);
ExeDoSomething(something);
}
{
var something = new DoSomething1(PrintNumber);
ExeDoSomething(something);//error
}
}
会发现 就算签章一样 只要宣告的委派不同 他们之间是不可以互相转换的~相当的严谨
照上面正规委派写法 我可能一个方法就必须要建立一个public delegate void DoSomething1(int number);这种委派签章
又臭又长 使用上又不方便
接下来 就是巨硬的德政了!Action/Func
Action/Func 是泛型的委派(泛型之後会再开一篇来讲 不要在这边乱开副本!)
我如果需要一个无回传值且具有一个int参数的方法 我不需要从头宣告一个
public delegate void DoSomething1(int number);来用
我只要写Action 就可以了
Action action1;//无回传值无参数
Action<int> action2;//无回传值具有一个int参数
Action<int, string> action3;//无回传值具有int string参数
Func<int> func1;//回传int 无参数
Func<string,int> func2;//回传int 具有一个string参数
Func<int,string,DateTime> func3;//回传DateTime具有int string参数
上面这些宣告可以自行体会一下
我们可以很容易地知道 Action就是无回传值的委派 而Func就是有回传值的
其余用法几乎没有差异
附带一提 以前最多支援到8个型别参数 也就是说我最多可以写
Action<int,string,double,float,Datetime,long,List,bool> //应该没人会这样写..吧?
但刚刚看了一下程序码.Net6 已经可以支援到16个参数型别了呢!(洒花?
回归正题
修改一下程序码 将 ExeDoSomething的参数从 DoSomething 更换成 Action
public static void ExeDoSomething(Action<int> something)
{
something.Invoke(5);
something(6);
}
static void Main(string[] args)
{
ExeDoSomething(PrintNumber);
ExeDoSomething(SquareAndPrintNumber);
}
因为 PrintNumber 跟 SquareAndPrintNumber 都是符合 无回传值 有一个int参数的方法签章
所以他们都可以当作参数传入ExeDoSomething
执行结果会是 5, 6, 25, 36
喔~ 可是还是好烦阿 要把方法当参数传入 我必须要建立一个方法才能这样做
想名字应该是程序猿永远的痛吧..
还好还好 巨硬的德政 匿名委派 我们可以这样写
上面那两个方法可以砍掉了(PrintNumber/SquareAndPrintNumber)
static void Main(string[] args)
{
ExeDoSomething((int n) =>
{
Console.WriteLine(n);
});
ExeDoSomething((int n) =>
{
n *= n;
Console.WriteLine(n);
});
}
(int n) 这边可以想成就是方法後面的参数签章 前面没有名字 => 後面是方法主体
这种lambda写法在巨硬很常使用,建议要习惯一下!
喔喔喔喔!! 不用想名字了真好~ 刚刚光想PrintNumber/SquareAndPrintNumber 就花了我两小时呢!
修但几累!
巨硬表示 我觉得这样看起来还是有点蠢
因为ExeDoSomething 里面的参数就已经知道 Action的参数是int 为什麽你外面还要写一次?
ExeDoSomething((n) =>
{
Console.WriteLine(n);
});
巨硬表示 : 我觉得只有一个参数n还要加括号有点蠢
ExeDoSomething(n =>
{
Console.WriteLine(n);
});
巨硬表示 : 程序本体只有一行 应该可以再精简吧?
ExeDoSomething(n => Console.WriteLine(n));
流浪汉表示 : (n)後面没加分号 (这里不用加啊!!!!!
这种写法在巨硬称做lambda表达式
上面这种写法 有几个限制
第一 参数要一个才能省略参数的括号
第二 如果没参数的画 一定要有括号
第三 程序码如果只有一行 可以省略该行的分号以及程序本体的大括号({})(分号 大括号必须同时存在/省略)
参数型别都可以省略
Func<int> func = () => 1;//因为无参数 所以都是回传1;
Func<int,int> func2 = x => x+1;//具有参数 回传 x+1
委派基础就到这边!
下一篇预计会开委派的实作
因为知道委派但是不知道怎麽去活用它还是没用阿!!!
------3/8号补充-----
本来一直记得要讲这个重要的东西
但居然忘记了...Orz(这年代还有人用这个吗?
委派的 闭包问题
先上Code
static void Main(string[] args)
{
Action action = null;
for(int i = 0; i < 10; i++)
{
action += () => Console.WriteLine(i);
}
action();
}
会印出什麽?
0 1 2 3 4 5 6 7 8 9 ??
不对
会是
10 10 10 10 10 10 10 10 10 10
Why?
因为委派的方法是 印出i
但我只是去设定委派内容
真正在执行的是 action(); 这一行
而i跑完回圈 for(int i = 0; i < 10; i++) 跳离回圈时会是10
所以真正执行的时候 是印出 10
那我真正要印出0~9怎麽办?
给他一个暂存变数即可
static void Main(string[] args)
{
Action action = null;
for(int i = 0; i <10;i++)
{
var temp = i;
action += () => Console.WriteLine(temp);
}
action();
}
这样 其实记忆体会产生10个temp 而每个temp分别就是 0-9
最後执行的时候就是执行印出各自的temp
使用委派要特别注意执行的时机跟变数的值喔!
<<: Python产生QRCode图片 - Python练习题一
废话不多说,直接附上code 影片含有程序码详细解说,若有误再烦请告知,谢谢 library(glm...
前情提要 昨天跟各位读者简介了反爬虫技术中,较常出现的验证码之应对方法。 开始之前 今天要跟各位介绍...
标题的字每个都认识,合起来却感觉哪边怪怪的...有些漠生吗? 企业永续经营 = 资讯安全制度运行,2...
国庆连假中,假日只想耍废玩 game,不想进修QQ,但为了避免断赛,还是加减推一些东西,等明後天再来...
最後一天了就来写个後记吧!感谢有看到最後的各位,能忍受我的超新手网页程序分享。铁人赛真的是一大挑战,...