简体   繁体   English

C#中的匿名内部类

[英]Anonymous inner classes in C#

I'm in the process of writing a C# Wicket implementation in order to deepen my understanding of C# and Wicket. 我正在编写C#Wicket实现,以加深我对C#和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. 我们遇到的一个问题是Wicket大量使用匿名内部类,而C#没有匿名内部类。

So, for example, in Wicket, you define a Link like this: 因此,例如,在Wicket中,您可以像这样定义一个链接:

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

Since Link is an abstract class, it forces the implementor to implement an onClick method. 由于Link是一个抽象类,它强制实现者实现onClick方法。

However, in C#, since there are no anonymous inner classes, there is no way to do this. 但是,在C#中,由于没有匿名内部类,因此无法执行此操作。 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. 首先,可以有多个Click处理程序,这可能不太酷。 It also does not force the implementor to add a Click handler. 它也不会强制实现者添加Click处理程序。

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#? 所以,我的问题是,你如何在惯用的C#中模仿这样的东西?

You can make the delegate be part of the constructor of the Link class. 您可以使委托成为Link类的构造函数的一部分。 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);

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. 保留链接作为抽象类,使用Factory实例化它并传递闭包/匿名方法作为Factory的构建方法的参数。 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. 这样,您可以将原始设计与Link作为抽象类保持一致,强制通过工厂实现,并且仍然隐藏工厂内任何具体的Link痕迹。

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. 美妙之处在于它使用了一个init块,允许您根据需要在Link类中声明多个可选的动作实现。

Here's the relevant Link class (With sealed Builder inner class). 这是相关的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;
    }
}

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. 这与您正在寻找的内容非常接近,但我认为我们可以使用可选的命名参数(C#4.0功能)更进一步。 Let's look at the example declaration of Link with optional named arguments: 让我们看看带有可选命名参数的Link的示例声明:

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: 我们来看看新的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;
    }
}

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. 在静态类Builder中,有一个工厂方法Build,它接受1个必需参数(ID)和3个可选参数,OnClick,OnFoo和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. 因此,在构造函数的Link参数参数中,您只需要实现所需的方法,否则它们将使用默认操作,这可能是什么都没有。

The drawback, however, is in the final example, the Link class is not abstract. 然而,缺点是在最后一个例子中,Link类不是抽象的。 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). 但它不能在Link类的范围之外实例化,因为它的构造函数是私有的(强制使用Builder类来实例化Link)。

You could also move the optional parameters into Link's constructor directly, avoiding the need for a factory altogether. 您还可以直接将可选参数移动到Link的构造函数中,从而完全不需要工厂。

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

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM