2011-01-22 104 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的構造函數中,從而避免完全需要工廠。