简体   繁体   English

单例对象的实例成员或类的静态成员

[英]Instance members of singleton object or static members of class

I am writing a listener class which creates a single HttpListener on random unused port and listens on it. 我正在编写一个侦听器类,该类在随机未使用的端口上创建一个HttpListener并在其上侦听。 I tried to make it singleton. 我试图使其单身。 However I am finding it difficult to keep that singleton object null when listening to random HTTP port fails 10 times. 但是,我发现在侦听随机HTTP端口失败10次时,很难将该单例对象保持为null

(I have shown port number and HttpListener instance as members of singleton instance. However there are many other members that I have not shown for simplicity.) This is my code: (我已将端口号和HttpListener实例显示为单例实例的成员。但是,为简单起见,还有许多其他成员未显示。)这是我的代码:

class Listener
{
    private static Listener listener = null;  //singleton instance

    //member variables
    private HttpListener httpListener = null;
    private int port = -1;

    static Listener()
    {
        listener = new Listener();
    } 

    private Listener()
    {
        try
        {
            port = //randomly generate
            httpListener = new HttpListener();
            //start listening
        }   
        catch(Exception ex)
        {
            //cant listen on randomly chosen port
            listener = null;
        httpListener = null;
            port = -1;
            return;            
        }   
    }
}

However inside catch() , listener = null sets listener to null just for a while. 但是,在catch()内部, listener = null会将listener设置为null一段时间。 When default constructor returns it returns a new instance of Listener , thus listener inside static constructor always have instance assigned to it. 当默认构造函数返回时,它将返回Listener的新实例,因此静态构造函数内部的listener始终会为其分配实例。 Thus instead of listener = null inside default constructor it should be this = null which is invalid. 因此,代替默认构造函数中的listener = null它应该是this = null ,这是无效的。

Thus I moved the whole code inside static constructor. 因此,我将整个代码移到了静态构造函数中。 But that forcesd me to 但这迫使我

  • Either make instance members ( port and httpListener ) static or 使实例成员( porthttpListenerstatic
  • or use listener.port , listener.httpListener everywhere 或在各处使用listener.portlistener.httpListener

    class Listener { private static Listener listener = null; 类Listener {private static Listener listener = null; //singleton instance //单个实例

     //member variables private HttpListener httpListener = null; private int port = -1; static Listener() { listener = new Listener(); try { listener.port = //randomly generate listener.httpListener = new HttpListener(); //start listening } catch(Exception ex) { //cant listen on randomly chosen port listener = null; return; } } private Listener() { } 

    } }

I dont understand 我不明白

Q1 whether to make port & httpListener static ? Q1是否使porthttpListener static (This I feel somewhat against OOP principles) or (我觉得这有点违反OOP原则)或

Q2 whether to keep them as instance members and use listener. Q2是否保留它们作为实例成员并使用listener. everywhere? 到处? (This is problematic since there are many such members and methods in my actual code and I have to attach listener. everywhere) (这是有问题的,因为在我的实际代码中有很多这样的成员和方法,并且我必须在每个地方附加listener.

Or I am thinking it all wrong and should have followed different approach? 还是我认为这都是错误的,应该采取不同的方法?

The immediate problem is that on failure the instance constructor is setting the static member listener to null. 直接的问题是,一旦失败,实例构造函数会将静态成员侦听器设置为null。 However, control then returns to the static constructor which sets the static member listener to the object that was created in the instance constructor. 但是,控制权然后返回到静态构造函数,后者将静态成员侦听器设置为在实例构造函数中创建的对象。 Hence, the behaviour you're seeing. 因此,您所看到的行为。

I'd argue that your (instance) constructor is trying to do too much. 我认为您的(实例)构造函数试图做太多事情。 I would move the 'start listening' logic into a separate method and call that from anywhere other than the instance constructor. 我会将“开始侦听”逻辑移到单独的方法中,然后从实例构造函数以外的任何地方调用它。 That will make your error handling a little easier eg 这将使您的错误处理更加容易,例如

class Listener
{
    public static Listener listener = null;  //singleton instance

    //member variables
    private HttpListener httpListener = null;
    private int port = -1;

    static Listener GetListener()
    {
        if (listener != null)
        {
            return listener;
        }

        try
        {
            listener = new Listener();
            listener.StartListening();
            return listener;
        }
        catch (Exception)
        {
            //cant listen on randomly chosen port
            listener.Cleanup();
            listener = null;
            throw;
        }
    }

    private Listener()
    {
        port = RandomlyGenerate();
        httpListener = new HttpListener();
    }

    private void StartListening()
    {
        //start listening
    }

    private void Cleanup()
    {
        httpListener.Close();
        httpListener = null;
        port = -1;
    }
}

As BartoszKP mentions in the comments, you would probably be better of with a factory pattern here. 正如BartoszKP在评论中提到的那样,在此处使用工厂模式可能会更好。

If you are interested in having a common place for events you could place that event in the factory class, or implement a static event in the Listener class. 如果您对事件有一个通用的位置感兴趣, 可以将该事件放置在factory类中,或者在Listener类中实现一个静态事件。

public class ListenerFactory {

    public IListener CreateListener(URI uri, int port) {
        Listener l = new Listener();
        l.MessageReceived += OnMessageReceived;
        // do whatever with l. loop until connection, or use l.Start() for instance
        return l;
    }

    public static event EventHandler<MessageEventArgs> ListenerMessageReceived;

    private static void OnMessageReceived(object sender, MessageEventArgs e) {
        // trigger ListenerMessageReceived
    }
}

public interface IListener {
    event EventHandler<MessageEventArgs> MessageReceived;
    void Send(byte[] data);
}

public class Listener : IListener {
    // implement interface
}

Then you just call new ListenerFactory().Create(host, port); 然后,您只需调用new ListenerFactory().Create(host, port); when you need a new Listener, and if you want to listen for all messages, you subscribe to ListenerFactory.MessageReceived for incoming messages. 当您需要新的侦听器时,并且如果您想侦听所有消息,则可以订阅ListenerFactory.MessageReceived接收传入消息。

Using this pattern, you can create several connections at once instead of relying on one class to handle all of them. 使用此模式,您可以一次创建多个连接,而不必依赖一个类来处理所有连接。


You should move the initialization code of httpListener to its own method in order to avoid recreating listener. 您应该将httpListener的初始化代码移至其自己的方法,以避免重新创建侦听器。 By doing that, and adding a property for getting the listener instance, classes can use Listener.Instance.Start() to reconnect if the static constructor fails to connect. 通过这样做,并添加用于获取侦听器实例的属性,如果静态构造函数无法连接,则类可以使用Listener.Instance.Start()重新连接。

public class Listener
{
    private static Listener listener = null;  //singleton instance

    //member variables
    private HttpListener httpListener = null;
    private int port = -1;

    static Listener()
    {
        listener = new Listener();
        // start listener
        try {
             listener.Start();
         }
         catch { }
    }

    // Use this method in other classes to start listener if it fails
    // in static constructor
    public static Listener Instance { get { return listener; } }

    private Listener()
    {
    }

     public bool IsConnected {
         get { return httpListener != null; }
     }

     public void Start() 
     {
        if (IsConnected) { return; }
        try
        {
            port = //randomly generate
            httpListener = new HttpListener();
            //start listening
        }   
        catch(Exception ex)
        {
            //cant listen on randomly chosen port
        httpListener = null;
            port = -1;
            return;            
        }   
    }
}

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

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