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#中模仿这样的东西?


答案 1

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

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

}

然后,您通过以下方式创建一个实例:

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

答案 2

这就是我要做的:

将 Link 保留为抽象类,使用工厂对其进行实例化,并将闭包/匿名方法作为工厂生成方法的参数传入。通过这种方式,您可以将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 = 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;
    }
}

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

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

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

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