1

I have some interface:

interface IServerListener
{
    void onServerStarted();
    void onSessionStarted();
    void onSessionCompleted(List<string> data);
}

And there is some method, which gets an instance of that interface for executing methods:

public void start(IServerListener listener)
{
    IPEndPoint hostPoint = new IPEndPoint(IPAddress.Parse(getLocalHost()), PORT);
    serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    serverSocket.Bind(hostPoint);
    serverSocket.Listen(5);

    listener.onServerStarted(); //Calling

    while(true)...
}

When I execute this method (start) from the main form class, I want to pass into parameters exactly the anonymous class that implements this interface to have access to use form elements:

private void Form1_Load(object sender, EventArgs e)
{
    server = new Server();
    server.start(new IServerListener()
    {
        void onServerStarted()
        {
            rtxtOutput.AppendText("Started... ");
        }

        void onSessionCompleted(List<string> data)
        {
            rtxtOutput.AppendText("Session completed: " + String.Join(", ", data));
        }

        void onSessionStarted()
        {
            rtxtOutput.AppendText("New session started... ");
        }
    });
}

But I can't do it the way I did it in Java. I get the following message:

Cannot create an instance of the abstract class or interface 'IServerListener'

So I tried to create separate class that implement this interface and already there to do what I need. But I can't to get access to use form elements from there:

private class AnonymousIServerListener : IServerListener
{
    public void onServerStarted()
    {
        rtxtOutput.AppendText("Started... ");
        //The name 'rtxtOutput' does not exist in the current context
    }

    public void onSessionCompleted(List<string> data)
    {
        rtxtOutput.AppendText("Session completed: " + String.Join(", ", data));
        //The name 'rtxtOutput' does not exist in the current context
    }

    public void onSessionStarted()
    {
        rtxtOutput.AppendText("New session started... ");
        //The name 'rtxtOutput' does not exist in the current context
    }
}

Please tell me what to do in this case without crutches. Is it possible to use an anonymous class in C# in general? If not, what to do in this case?

Thanks in advance. Regards...

Dev0ps
  • 71
  • 3
  • 9
  • Yes, you can create anonymous types. No, you can't use them the way you are trying to. Anonymous types are only valid within the code block you are using them in. If you want to pass them as parameters then they can't be anonymous. – Steve Todd Jun 17 '19 at 11:03
  • @SᴇM You don't. What you _can_ do is what OP is stating: create an anonymous implementation of that interface. But anyway. .Net works differently. – Fildor Jun 17 '19 at 11:04
  • Why are you trying to do it this way? – sr28 Jun 17 '19 at 11:04
  • @Fildor, no you can't do that. https://stackoverflow.com/questions/191013/can-a-c-sharp-anonymous-class-implement-an-interface – Steve Todd Jun 17 '19 at 11:06
  • @SᴇM Exactly. Your comment implied OP has been trying to instantiate an interface. He wasn't. (Or I misunderstood your comment completely.) See also: https://stackoverflow.com/a/26582180/982149 – Fildor Jun 17 '19 at 11:07
  • @SteveTodd Not in C#, right. – Fildor Jun 17 '19 at 11:07
  • In C#, I tend to implement some derivate of the Visitor pattern for these scenarios. – Fildor Jun 17 '19 at 11:13
  • It's in the code, @SᴇM. In Java, that code in the 3rd snippet would be an anonymous inner class, implementing `IServerListener`. So it may _look_ like instantiating the interface, but it actually isn't. Doesn't work in C#, of course. – Fildor Jun 17 '19 at 11:16
  • 1
    @Fildor So if you write `new IServerListener() {rest here}` it will create an anonymous class which implements `IServerListener`? Didn't know, sorry for taking your time, have a nice day) – SᴇM Jun 17 '19 at 11:22
  • 1
    @SᴇM You're welcome. That being legal and looking like something it actually isn't drove me crazy once, because a college made extensive use of that and I was newly switching from .net 2.0 to Java. – Fildor Jun 17 '19 at 11:26

2 Answers2

3

Why not use Actions for this...

private void Form1_Load(object sender, EventArgs e)
{
    server = new Server();
    server.start(new ServerListener()
        { OnServerStarted = () => rtxtOutput.AppendText("Started... ")
        , OnSessionCompleted = data =>
            {
                rtxtOutput.AppendText("Session completed: " + String.Join(", ", data));
            }
        , OnSessionStarted = () => rtxtOutput.AppendText("New session started... ")
        )
    );
}

And the ServerListener:

public class ServerListener : IServerListener
{
    void IServerListener.onServerStarted() => OnServerStarted?.Invoke();
    void IServerListener.onSessionStarted() => OnSessionStarted?.Invoke();
    void IServerListener.onSessionCompleted(List<string> data) => OnSessionCompleted?.Invoke(data);

    public Action OnServerStarted { get; set; }

    public Action<List<string>> OnSessionCompleted { get; set; }

    public Action OnSessionStarted { get; set; }
}

Here, ServerListener just serves as some middleware which connects the dots. It doesn't do anything itself.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
2

This has nothing to do with anonymous types. You want to decouple your AnonymousIServerListener from your form. And rightfully so, because otherwise you couldn't instantiate an AnonymousIServerListener without a Form1 instance containing a TextBox rtxtOutput.

Instead you'd want, for example, an event being raised from your listener. Give your interface an event:

public interface IServerListener
{
    event EventHandler<LogEventArgs> Log;

    // ...
}

Define the EventArgs:

public class LogEventArgs : EventArgs
{
    public string LogText { get; }

    public LogEventArgs(string logText)
    {
        LogText = logText;
    }
}

Then add it to your listener:

public class AnonymousIServerListener : IServerListener
{
    public event EventHandler<LogEventArgs> Log = delegate { };

    public void OnServerStarted()
    {
        Log.Invoke(new LogEventArgs("Started... "));
    }

    public void OnSessionCompleted(List<string> data)
    {
        Log.Invoke(new LogEventArgs("Session completed: " + String.Join(", ", data)));
    }

    public void OnSessionStarted()
    {
        Log.Invoke(new LogEventArgs("New session started... "));
    }
}

Finally, subscribe to your event in the class that instantiates the AnonymousIServerListener:

private void Form1_Load(object sender, EventArgs e)
{
    server = new Server();

    IServerListener listener = new AnonymousIServerListener();

    listener.Log += (e) => rtxtOutput.AppendText(e.LogText);

    server.start(listener);
}

But now when Form1_Load returns, you have a dangling reference, tied by the Log event subscription. You'll never be able to unsubscribe from that, leaving your listener alive for the remainder of your application lifetime.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • My first thought was to have Form1 implement IServerListener (explicitly) and passing `this` ... am I missing something really bad about that? – Fildor Jun 17 '19 at 11:28
  • 1
    A form is a UI element, a listener deals with network stuff. You don't want your form to do too much. Otherwise, if all the listener does is print stuff, then yeah, that would be ideal for the form to implement. – CodeCaster Jun 17 '19 at 11:32
  • Of course, that's not a "perfect for all situations" thing. I assumed it is _really_ just to update a status text. But apart from your valid objection, it may also run into cross-thread issues ... – Fildor Jun 17 '19 at 11:34