23

C#とWicketの理解を深めるために、C#Wicketの実装を作成中です。私たちが直面している問題の1つは、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ハンドラーの追加を強制することもありません。

別のオプションは、次のようなクロージャープロパティを持つことです。

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

これにより、多数のハンドラーが存在するという問題は解決されますが、それでも実装者にハンドラーの追加を強制することはありません。

だから、私の質問は、慣用的なC#でこのようなものをどのようにエミュレートするのですか?

4

3 に答える 3

17

デリゲートをLinkクラスのコンストラクターの一部にすることができます。このようにして、ユーザーはそれを追加する必要があります。

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

}

次に、次の方法でインスタンスを作成します。

var link = new Link("id", () => do stuff);
于 2011-01-22T20:03:37.147 に答える
4

これは私がすることです:

Linkを抽象クラスとして保持し、Factoryを使用してインスタンス化し、クロージャー/匿名メソッドをFactoryのbuildメソッドのパラメーターとして渡します。このようにして、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.0機能を使用してさらに一歩進めることができると思います。オプションの名前付き引数を使用した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の内部には、1つの必須パラメーター(ID)と3つのオプションパラメーターOnClick、OnFoo、およびOnBarを受け取るファクトリメソッドBuildがあります。それらが割り当てられていない場合、ファクトリメソッドはそれらにデフォルトの実装を提供します。

したがって、Linkのコンストラクターのパラメーター引数では、必要なメソッドを実装するだけで済みます。そうしないと、デフォルトのアクションが使用されますが、これは何もない可能性があります。

ただし、最後の例では、Linkクラスが抽象的ではないという欠点があります。ただし、コンストラクターがプライベートであるため(Linkをインスタンス化するためにBuilderクラスを使用するように強制する)、Linkクラスのスコープ外でインスタンス化することはできません。

オプションのパラメーターをLinkのコンストラクターに直接移動して、ファクトリの必要性を完全に回避することもできます。

于 2012-12-25T18:33:53.020 に答える
1

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

}
于 2011-01-22T20:23:13.647 に答える