【问题标题】:Anonymous inner classes in C#C#中的匿名内部类
【发布时间】:2011-06-13 19:12:06
【问题描述】:

我正在编写 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(...);

这解决了有很多handler的问题,但仍然不强制实现者添加handler。

所以,我的问题是,你如何在惯用的 C# 中模拟这样的东西?

【问题讨论】:

  • 我在您给出的示例中没有看到匿名内部类。如果你希望你的抽象类的实现者总是实现一些方法,你可以在类中创建一个抽象方法或者让它实现一个接口。
  • @tenor,定义了一个内联匿名类,它继承自 Link 并覆盖 onClick 方法。与 Java 不同,C# 不支持从给定用户类型派生的匿名类。
  • @Darin Dimitrov,感谢您指出这一点。我正在寻找一个真正的“内部/嵌套”类。提供的示例看起来更像是派生自现有类的匿名类,至少在 C# 术语中是这样。
  • 老话题,但是它仍然在这里......这是一个内部类,因为在 Java 中,每个类要么是顶级类,要么是嵌套类(在另一个类中定义),并且要么是静态类,要么是非静态类。那是一个非静态的嵌套类,它的术语是内部类。

标签: c# java closures wicket anonymous-inner-class


【解决方案1】:

这就是我会做的:

将链接保留为抽象类,使用工厂实例化它并传入您的闭包/匿名方法作为工厂构建方法的参数。这样,您可以将 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 中,有一个工厂方法 Build,它接受 1 个必需参数(ID)和 3 个可选参数,OnClick、OnFoo 和 OnBar。如果它们没有被赋值,工厂方法会给它们一个默认的实现。

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

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

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

【讨论】:

  • 这是 3 年前的答案,但我只是想在您的最终答案中提到您可以稍微缩短返回线:return new Link(ID, OnClick ?? DefaultAction , OnFoo ?? DefaultAction, OnBar ?? DefaultAction);
【解决方案2】:

我在@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 类的构造函数的一部分。这样用户就必须添加它。

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-06
      • 1970-01-01
      • 1970-01-01
      • 2010-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-29
      相关资源
      最近更新 更多