C#用链式方法表达循环嵌套

2019-12-30 12:31:30王振洲

p.s.为什么第4版是ISeat3而不是ISeat4呢,因为我本不把第1版当作1个版本,因为太原始了,出现第2版后,我就把第1版给删除了。为了写这篇文章才重新去写第1版。于是原本我当作第3版的ISeat3自然地排到了第4版。

具体的"算法"就很简单了:


class Seat3 : ISeat3
{
static Seat data = new Seat();
string Name { get; set; }
public Seat3(string name)
{
this.Name = name;
}
/// <summary>
/// 解耦的版本
/// </summary>
public static void Run()
{
var sql = ComputeLink<Seat3>
.New(new Seat3("A"), m => m.Try())
.Do(new Seat3("B"), m => m.Try())
.Do(new Seat3("C"), m => m.Try())
.Do(new Seat3("D"), m => m.Try())
.Do(new Seat3("A"), m => m.Try2())
.Do(new Seat3("B"), m => m.Try2())
.Do(new Seat3("C"), m => m.Try2())
.Do(new Seat3("D"), m => m.Try2())
.Do(new Seat3(""), m => m.Print());
sql.Action();
}
public Action<ISeat3> Method { get; set; }
public ISeat3 Child { get; set; }
public void Try()
{
for (int i = 0; i < 4; i++)
{
if (data.IsSelected(0, i))
continue;
data.Selected(0, i, this.Name);
if (this.Child != null)
{
this.Child.Method(this.Child);
}
data.UnSelected(0, i);
}
}
public void Try2()
{
for (int i = 0; i < 5; i++)
{
if (i == 1)
continue;
if (data.IsSelected(1, i))
continue;
if (data.IsSelected(0, i, this.Name))
continue;
data.Selected(1, i, this.Name);
if (this.Child != null)
{
this.Child.Method(this.Child);
}
data.UnSelected(1, i);
}
}
public void Print()
{
data.Count++;
Console.WriteLine("{0,5} {1}", data.Count, data.Current);
}
}

Seat3写起来简单,(Run方法内部)看起来舒服。通过链式写法达到嵌套循环的效果。对,这就是我要的!

它很像linq,所以我直接给变量命名为sql。

•对于Try和Try2来讲,要调用的方法最好从参数传来,但是这样就会增加Run方法中New和Do的参数复杂性,破坏了美感,所以经过权衡,Child和Method通过属性传入。这个我也不确定这样做好不好,请各位大侠指点。

•还有一个细节,就是ComputeLink构造方法中的(行号12的)代码 this.Obj.Method = x => method((T)x); 。我原来是这样写的 this.Obj.Method = method; 编译不通过,原因是不能把 Action<ISeat3> 转化为 Action<T> ,虽然T一定实现了ISeat3,强制转化也不行,想起以前看过的一篇文章里面提到希望C#以后的版本能拥有的一特性叫“协变”,很可能指的就是这个。既然这个 Action<ISeat3> 不能转化为 Action<T> 但是ISeat3是可以强制转化为T的,所以我包了一层薄薄的壳,成了 this.Obj.Method = x => method((T)x); ,如果有更好的办法请告诉我。