C#中的匿名内部类

我正在编写C#Wicket实现,以加深我对C#和Wicket的理解。 我们遇到的一个问题是Wicket大量使用匿名内部类,而C#没有匿名内部类。

因此,例如,在Wicket中,您可以像这样定义一个链接:

Link link = new Link("id") { @Override void onClick() { setResponsePage(...); } }; 

由于Link是一个抽象类,它强制实现者实现onClick方法。

但是,在C#中,由于没有匿名内部类,因此无法执行此操作。 作为替代方案,您可以使用以下事件:

 var link = new Link("id"); link.Click += (sender, eventArgs) => setResponsePage(...); 

当然,这有一些缺点。 首先,可以有多个Click处理程序,这可能不太酷。 它也不会强制实现者添加Click处理程序。

另一个选择可能是只有一个这样的闭包属性:

 var link = new Link("id"); link.Click = () => setResponsePage(...); 

这解决了具有许多处理程序的问题,但仍然没有强制实现者添加处理程序。

所以,我的问题是,你如何在惯用的C#中模仿这样的东西?

您可以使委托成为Link类的构造函数的一部分。 这样用户就必须添加它。

 public class Link { public Link(string id, Action handleStuff) { ... } } 

然后以这种方式创建一个实例:

 var link = new Link("id", () => do stuff); 

这就是我要做的:

将Link保留为抽象类,使用Factory实例化它并将闭包/匿名方法作为Factory的构建方法的参数传递。 这样,您可以将原始设计与Link作为抽象类保持一致,强制通过工厂实现,并且仍然隐藏工厂内任何具体的Link痕迹。

这是一些示例代码:

 class Program { static void Main(string[] args) { Link link = LinkFactory.GetLink("id", () => // This would be your onClick method. { // SetResponsePage(...); Console.WriteLine("Clicked"); Console.ReadLine(); }); link.FireOnClick(); } public static class LinkFactory { private class DerivedLink : Link { internal DerivedLink(String id, Action action) { this.ID = id; this.OnClick = action; } } public static Link GetLink(String id, Action onClick) { return new DerivedLink(id, onClick); } } public abstract class Link { public void FireOnClick() { OnClick(); } public String ID { get; set; } public Action OnClick { get; set; } } } 

编辑:实际上,这可能更接近你想要的:

 Link link = new Link.Builder { OnClick = () => { // SetResponsePage(...); }, OnFoo = () => { // Foo! } }.Build("id"); 

美妙之处在于它使用了一个init块,允许您根据需要在Link类中声明多个可选的动作实现。

这是相关的Link类(使用密封的Builder内部类)。

 public class Link { public sealed class Builder { public Action OnClick; public Action OnFoo; public Link Build(String ID) { Link link = new Link(ID); link.OnClick = this.OnClick; link.OnFoo = this.OnFoo; return link; } } public Action OnClick; public Action OnFoo; public String ID { get; set; } private Link(String ID) { this.ID = ID; } } 

这与您正在寻找的内容非常接近,但我认为我们可以通过可选的命名参数(C#4.0function)更进一步。 让我们看看带有可选命名参数的Link的示例声明:

 Link link = Link.Builder.Build("id", OnClick: () => { // SetResponsePage(...); Console.WriteLine("Click!"); }, OnFoo: () => { Console.WriteLine("Foo!"); Console.ReadLine(); } ); 

为什么这很酷? 我们来看看新的Link类:

 public class Link { public static class Builder { private static Action DefaultAction = () => Console.WriteLine("Action not set."); public static Link Build(String ID, Action OnClick = null, Action OnFoo = null, Action OnBar = null) { return new Link(ID, OnClick == null ? DefaultAction : OnClick, OnFoo == null ? DefaultAction : OnFoo, OnBar == null ? DefaultAction : OnBar); } } public Action OnClick; public Action OnFoo; public Action OnBar; public String ID { get; set; } private Link(String ID, Action Click, Action Foo, Action Bar) { this.ID = ID; this.OnClick = Click; this.OnFoo = Foo; this.OnBar = Bar; } } 

在静态类Builder中,有一个工厂方法Build,它接受1个必需参数(ID)和3个可选参数,OnClick,OnFoo和OnBar。 如果未分配它们,则工厂方法为它们提供默认实现。

因此,在构造函数的Link参数参数中,您只需要实现所需的方法,否则它们将使用默认操作,这可能是什么都没有。

然而,缺点是在最后一个例子中,Link类不是抽象的。 但它不能在Link类的范围之外实例化,因为它的构造函数是私有的(强制使用Builder类来实例化Link)。

您还可以直接将可选参数移动到Link的构造函数中,从而完全不需要工厂。

我在@ meatthew的好答案之前开始这个 – 除了我将从一个抽象的基类开始之外我几乎完全相同 – 所以如果你不想走匿名实现的路线你可以自由地做那也是。

 public abstract class LinkBase { public abstract string Name { get; } protected abstract void OnClick(object sender, EventArgs eventArgs); //... } public class Link : LinkBase { public Link(string name, Action onClick) { _name = Name; _onClick = onClick; } public override string Name { get { return _name; } } protected override void OnClick(object sender, EventArgs eventArgs) { if (_onClick != null) { _onClick(sender, eventArgs); } } private readonly string _name; private readonly Action _onClick; } 
Interesting Posts