Anonymous inner classes in C#

前端 未结 3 2061
后悔当初
后悔当初 2020-12-10 10:55

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 h

3条回答
  •  时光说笑
    2020-12-10 11:43

    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.

提交回复
热议问题