本文探讨如何使用扩展方法封装 if/else、swith/case及while,通过使用这些扩展,写出的代码将使用很少的大括号{ }。扩展的效果如何,还请大家来评判!
声明:本文属于(改)变(形)态篇,只是提出一种想法,所提供的代码也只是示例,可以测试通过,但不完善。
首先我们来对看if/else和swith/case,两者在代码中都用来表达分支结构。这里我们统一封装成一个If扩展:
public
static
T If
<
T
>
(
this
T t, Predicate
<
T
>
predicate, Action
<
T
>
action)
where
T:
class
{
if
(t
==
null
)
throw
new
ArgumentNullException();
if
(predicate(t)) action(t);
return
t;
}
看下面的调用代码,生成一个People的实例,让他吃饱喝足休息好再工作:
public
static
void
Test1()
{
//
常规代码
People people1
=
new
People { Name
=
"
ldp615
"
, IsHungry
=
true
, IsThirsty
=
true
, IsTired
=
true
};
if
(people1.IsHungry) people1.Eat();
if
(people1.IsThirsty) people1.Drink();
if
(people1.IsTired) people1.Rest();
people1.Work();
//
使用扩展方法
People people2
=
new
People { Name
=
"
ldp615
"
, IsHungry
=
true
, IsThirsty
=
true
, IsTired
=
true
}
.If(p
=>
p.IsHungry, p
=>
p.Eat())
.If(p
=>
p.IsThirsty, p
=>
p.Drink())
.If(p
=>
p.IsTired, p
=>
p.Rest());
people2.Work();
}
扩展方法中的If可以使用点“.”链起来,称之“链式编程”,请参见我我随笔《
c#链式编程
》。
常规代码和使用扩展方法写的代码都在上面,大家比较一下吧。
使用If扩展的代码中用了lambda表达式,如果前面的“p=>p.”能去掉的话,看起来就比较舒服了!编译器通过类型及上下文推演,应该可以做得到吧!
给出People类如下:
People类
public
class
People
{
public
string
Name {
get
;
set
; }
public
bool
IsHungry {
get
;
set
; }
public
bool
IsThirsty {
get
;
set
; }
public
bool
IsTired {
get
;
set
; }
public
int
WorkCount {
get
;
private
set
; }
public
void
Eat()
{
Console.WriteLine(
"Eat
"
);
IsHungry
=
false
;
}
public
void
Drink()
{
Console.WriteLine(
"
Drink
"
);
IsThirsty
=
false
;
}
public
void
Rest()
{
Console.WriteLine(
"
Rest
"
);
IsTired
=
false
;
}
public
void
Work()
{
Console.WriteLine(
"
Work
"
);
IsHungry
=
IsThirsty
=
IsTired
=
true
;
WorkCount
++
;
}
}
对引用类型我们可以使用Action<T>,也以使用链式编程的方式将多个If串起来。
但对值类型来说,就要用Func<T, T>了,每次返回一个新的值 :
public
static
T If
<
T
>
(
this
T t, Predicate
<
T
>
predicate, Func
<
T, T
>
func)
where
T :
struct
{
return
predicate(t)
?
func(t) : t;
}
调用代码也要修改:
public
static
void
Test2()
{
//
扩展方式
int
int0
=
-
121
;
int
int1
=
int0.If(i
=>
i
<
0
, i
=>
-
i)
.If(i
=>
i
>
100
, i
=>
i
-
100
)
.If(i
=>
i
%
2
==
1
, i
=>
i
-
1
);
//
常规方式
int
int3
=
-
121
;
if
(int3
<
0
) int3
=
-
int3;
if
(int3
>
100
) int3
-=
100
;
if
(int3
%
2
==
1
) int3
--
;
}
引用类型及值类型的扩展我们已经完成,用string来测试一下吧,如下:
public
static
void
Test3()
{
//
从邮箱变换成主页
string
email
=
"
ldp615@163.com
"
;
string
page
=
email.If(s
=>
s.Contains(
"
@
"
), s
=>
s.Substring(
0
, s.IndexOf(
"
@
"
)))
.If(s
=>!
s.StartsWith(
"
www.
"
), s
=>
s
=
"
www.
"
+
s)
.If(s
=>!
s.EndsWith(
"
.com
"
), s
=>
s
+=
"
.com
"
);
}
但编译不通过,会提示错误:
这个错误比较怪,我们写了两个扩展,一个是给值类型的,一个给引用类型,可string类型在这里都不行。这个原因我说不清楚了,还留给园子里高手们吧。
不过专门为string写个扩展,这个问题可以化解,如下:
public
static
string
If(
this
string
s, Predicate
<
string
>
predicate, Func
<
string
,
string
>
func)
{
return
predicate(s)
?
func(s) : s;
}
看来扩展方法也是有优先级的:对同一个类进行多次扩展,扩展方法相名,参数也等效(数量、顺序相同),非泛版扩展要比泛型版扩展优先级高。
下面再来看一段swith代码,很啰嗦的!这里是为了引出扩展硬写出来的:
public
static
void
Test4()
{
string
englishName
=
"
apple
"
;
string
chineseName
=
string
.Empty;
switch
(englishName)
{
case
"
apple
"
:
chineseName
=
"
苹果
"
;
return
;
case
"
orange
"
:
chineseName
=
"
桔子
"
;
return
;
case
"
banana
"
:
chineseName
=
"
香蕉
"
;
return
;
case
"
pear
"
:
chineseName
=
"
梨
"
;
break
;
default
:
chineseName
=
"
未知
"
;
break
;
}
Console.WriteLine(chineseName);
}
我们把这种方式用扩展方法来完成:
public
static
TOutput Switch
<
TOutput, TInput
>
(
this
TInput input, IEnumerable
<
TInput
>
inputSource, IEnumerable
<
TOutput
>
outputSource, TOutput defaultOutput)
{
IEnumerator
<
TInput
>
inputIterator
=
inputSource.GetEnumerator();
IEnumerator
<
TOutput
>
outputIterator
=
outputSource.GetEnumerator();
TOutput result
=
defaultOutput;
while
(inputIterator.MoveNext())
{
if
(outputIterator.MoveNext())
{
if
(input.Equals(inputIterator.Current))
{
result
=
outputIterator.Current;
break
;
}
}
else
break
;
}
return
result;
}
下面的Test5是调用:
public
static
void
Test5()
{
string
englishName
=
"
apple
"
;
string
chineseName
=
englishName.Switch(
new
string
[] {
"
apple
"
,
"
orange
"
,
"
banana
"
,
"
pear
"
},
new
string
[] {
"
苹果
"
,
"
桔子
"
,
"
香蕉
"
,
"
梨
"
},
"
未知
"
);
Console.WriteLine(chineseName);
}
简单清晰明了!
最后是一个对while的扩展封装:
public
static
void
While
<
T
>
(
this
T t, Predicate
<
T
>
predicate, Action
<
T
>
action)
where
T:
class
{
while
(predicate(t)) action(t);
}
调用代码:
public
static
void
Test6()
{
People people
=
new
People { Name
=
"
Wretch
"
};
people.While(
p
=>
p.WorkCount
<
7
,
p
=>
p.Work()
);
people.Rest();
}
这里又“召唤”了一个人,不让吃喝不让休息,连续工作7次...
这while扩展中只能执行一个Action<T>,不太好,我们用params改进一下:
public
static
void
While
<
T
>
(
this
T t, Predicate
<
T
>
predicate,
params
Action
<
T
>
[] actions)
where
T :
class
{
while
(predicate(t))
{
foreach
(var action
in
actions)
action(t);
}
}
再来调用,可以在循环中执行多个操作了,这次舒服工作完吃饭喝水休息,再来工作...
public
static
void
Test7()
{
People people
=
new
People { Name
=
"
Wretch
"
};
people.While(
p
=>
p.WorkCount
<
7
,
p
=>
p.Work(),
p
=>
p.Eat(),
p
=>
p.Drink(),
p
=>
p.Rest()
);
people.Rest();
}
当然前面的If也可以这样的,这里只写出一个:
If改进
public
static
T If
<
T
>
(
this
T t, Predicate
<
T
>
predicate,
params
Action
<
T
>
[] actions)
where
T :
class
{
if
(t
==
null
)
throw
new
ArgumentNullException();
if
(predicate(t))
{
foreach
(var action
in
actions)
action(t);
}
return
t;
}
不使用 params,你就要显示声明一个Action<T>的集合了!关于params, 在我的随笔《
改进 Scottgu 的 "In" 扩展
》有说明。
-------------------
思想火花,照亮世界