2011-01-22 107 views
22

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

因此,例如,在检票,你定义一个链接是这样的:

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

由于链接是一个抽象类,它强制实行一个onClick方法的实现者。

但是,在C#中,因为没有匿名的内部类,所以没有办法做到这一点。作为替代方案,您可以使用类似的事件:

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

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

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

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

这解决了有许多处理的问题,不过还是不会强制执行者添加的处理程序。

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

+1

我在你给的例子中看不到一个匿名的内部类。如果你希望你的抽象类的实现者总是实现一些方法,你可以在类中创建一个抽象方法或者让它实现一个接口。 – tenor 2011-01-22 20:05:55

+2

@tenor,定义了一个内联的匿名类,它从`Link`继承并覆盖`onClick`方法。与Java不同,C#不支持匿名类从给定的用户类型派生。 – 2011-01-22 20:13:18

+0

@Darin Dimitrov,谢谢你的指出。我正在寻找一个真正的“内部/嵌套”类。所提供的示例看起来更像是一个匿名类,它从现有类派生,至少使用C#术语。 – tenor 2011-01-22 20:17:15

回答

17

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

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

} 

然后您创建一个实例是这样的:

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

I @ 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<object, EventArgs> 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<object, EventArgs> _onClick; 

} 
3

这是我会做:

保留链接作为一个抽象类,使用工厂实例并通过在关闭/匿名方法作为工厂的构建方法的参数。通过这种方式,您可以使用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类中声明尽可能多的动作的可选实现。

下面是相关的链接类(使用密封的生成器内部类)。

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.0的功能。让我们看看带有可选命名参数的链接示例声明:

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

为什么这很酷?让我们来看看新的链接类:

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; 
    } 
} 

里面的静态类生成器,还有一个工厂方法构建需要在1个所需的参数(ID)和3个可选参数,的OnClick,OnFoo和OnBar会。如果他们没有分配,工厂方法给他们一个默认的实现。

因此,在您的Link的构造函数的参数参数中,只需要实现所需的方法,否则它们将使用默认操作,该操作可能不是任何操作。

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

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