C# 中的匿名内部类

发布于 2024-10-13 09:38:34 字数 750 浏览 8 评论 0原文

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

因此,例如,在 Wicket 中,您可以这样定义 Link:

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# 中模拟类似的东西?

I'm in the process of writing a C# Wicket implementation in order to deepen my understanding of C# and Wicket. One of the issues we're running into is that Wicket makes heavy use of anonymous inner classes, and C# has no anonymous inner classes.

So, for example, in Wicket, you define a Link like this:

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

Since Link is an abstract class, it forces the implementor to implement an onClick method.

However, in C#, since there are no anonymous inner classes, there is no way to do this. As an alternative, you could use events like this:

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

Of course, there are a couple of drawbacks with this. First of all, there can be multiple Click handlers, which might not be cool. It also does not force the implementor to add a Click handler.

Another option might be to just have a closure property like this:

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

This solves the problem of having many handlers, but still doesn't force the implementor to add the handler.

So, my question is, how do you emulate something like this in idiomatic C#?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

dawn曙光 2024-10-20 09:38:34

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

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

}

然后你可以这样创建一个实例:

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

You can make the delegate be part of the constructor of the Link class. This way the user will have to add it.

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

}

Then you create an instance this way:

var link = new Link("id", () => do stuff);
温柔戏命师 2024-10-20 09:38:34

这就是我要做的:

将 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 类(带有密封的 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 中,有一个工厂方法 Build,它接受 1 个必需参数(ID)和 3 个可选参数:OnClick、OnFoo 和 OnBar。如果未分配它们,工厂方法将为它们提供默认实现。

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

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

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

This is what I would do:

Retain Link as an abstract class, use a Factory to instantiate it and pass in your closure / anonymous method as a parameter for the Factory's build method. This way, you can keep your original design with Link as an abstract class, forcing implementation through the factory, and still hiding any concrete trace of Link inside the factory.

Here is some example code:

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

EDIT: Actually, This may be a little closer to what you want:

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

The beauty is it uses an init block, allowing you to declare as many optional implementations of actions within the Link class as you want.

Here's the relevant Link class (With sealed Builder inner class).

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

This is close to what you're looking for, but I think we can take it a step further with optional named arguments, a C# 4.0 feature. Let's look at the example declaration of Link with optional named arguments:

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

Why is this cool? Let's look at the new Link class:

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

Inside the static class Builder, there is a factory method Build that takes in 1 required parameter (The ID) and 3 optional parameters, OnClick, OnFoo and OnBar. If they are not assigned, the factory method gives them a default implementation.

So in your constructor's parameter arguments for Link, you are only required to implement the methods that you need, otherwise they will use the default action, which could be nothing.

The drawback, however, is in the final example, the Link class is not abstract. But it cannot be instantiated outside of the scope of the Link class, because its constructor is private (Forcing the usage of the Builder class to instantiate Link).

You could also move the optional parameters into Link's constructor directly, avoiding the need for a factory altogether.

人疚 2024-10-20 09:38:34

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

}

I started this before @meatthew's good answer - I would do almost exactly the same except - except that I would start with an abstract base class - so that if you did not want to go the route of an anonymous implementation you would be free to do that too.

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;

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文