I'm subscribing to the following event and I wonder what the difference between the following two examples is and do I need to unsubscribe. The library I'm using is Binance.Net by JKorf.
public event Action<Exception> Exception
{
add => subscription.Exception = value;
remove => subscription.Exception -= value;
}
Example #1
// Subscribe
subscription.Data.Exception = PortfolioService_Exception;
// Unsubscribe
subscription.Data.Exception -= PortfolioService_Exception;
private void PortfolioService_Exception(Exception ex)
{
_logger.LogInformation($"Error: {ex.StackTrace}");
}
Example #2
If I do it this way, do I need to unsubscribe with -=
?
subscription.Data.Exception = (ex) =>
{
_logger.LogInformation($"Error: {ex.StackTrace}");
};
Full code in addition
Snippet
private async Task<bool> InitAsync()
{
var restClient = new BinanceClient(new BinanceClientOptions
{
ApiCredentials = new ApiCredentials(ApiKey, SecretKey),
AutoTimestamp = true,
AutoTimestampRecalculationInterval = TimeSpan.FromMinutes(30),
TradeRulesBehaviour = TradeRulesBehaviour.AutoComply,
#if DEBUG
LogLevel = LogLevel.Debug,
LogWriters = new List<ILogger> { _logger } // TODO: FIX
#endif
});
var socketClient = new BinanceSocketClient(new BinanceSocketClientOptions
{
ApiCredentials = new ApiCredentials(ApiKey, SecretKey),
AutoReconnect = true,
ReconnectInterval = TimeSpan.FromSeconds(15),
#if DEBUG
LogLevel = LogLevel.Debug,
LogWriters = new List<ILogger> { _logger } // TODO: FIX
#endif
});
var listenKeyResult = await restClient.Spot.UserStream.StartUserStreamAsync();
if (!listenKeyResult.Success)
{
return false;
}
var subscription = await socketClient.Spot.SubscribeToUserDataUpdatesAsync(listenKeyResult.Data,
null,
null,
data =>
{
// TODO: Fix the warning
_logger.LogInformation($"ASD {data.Data.Balances}");
},
data =>
{
// TODO: Fix the warning
_logger.LogInformation($"BALANCE DELTA {data.Data.BalanceDelta}");
});
if (!subscription.Success)
{
return false;
}
// Auto unsubscribe?
subscription.Data.Exception = (ex) =>
{
_logger.LogInformation($"Error: {ex.StackTrace}");
};
// Subscribe
subscription.Data.Exception = PortfolioService_Exception;
// Unsubscribe
subscription.Data.Exception -= PortfolioService_Exception;
// TODO: Put a CancellationToken logic and `StopUserStreamAsync`. and subscription.Error -=
var keepAliveTask = Task.Run(async () =>
{
while (true)
{
// Listen key is kept alive for 60 minutes.
await restClient.Spot.UserStream.KeepAliveUserStreamAsync(listenKeyResult.Data);
await Task.Delay(TimeSpan.FromMinutes(50));
}
});
return true;
}
private void PortfolioService_Exception(Exception ex)
{
_logger.LogInformation($"Error: {ex.StackTrace}");
}
The library
using CryptoExchange.Net.Objects;
using System;
using System.Threading.Tasks;
namespace CryptoExchange.Net.Sockets
{
/// <summary>
/// Subscription to a data stream
/// </summary>
public class UpdateSubscription
{
private readonly SocketConnection connection;
private readonly SocketSubscription subscription;
/// <summary>
/// Event when the connection is lost. The socket will automatically reconnect when possible.
/// </summary>
public event Action ConnectionLost
{
add => connection.ConnectionLost = value;
remove => connection.ConnectionLost -= value;
}
/// <summary>
/// Event when the connection is closed. This event happens when reconnecting/resubscribing has failed too often based on the <see cref="SocketClientOptions.MaxReconnectTries"/> and <see cref="SocketClientOptions.MaxResubscribeTries"/> options,
/// or <see cref="SocketClientOptions.AutoReconnect"/> is false
/// </summary>
public event Action ConnectionClosed
{
add => connection.ConnectionClosed = value;
remove => connection.ConnectionClosed -= value;
}
/// <summary>
/// Event when the connection is restored. Timespan parameter indicates the time the socket has been offline for before reconnecting.
/// Note that when the executing code is suspended and resumed at a later period (for example laptop going to sleep) the disconnect time will be incorrect as the diconnect
/// will only be detected after resuming. This will lead to an incorrect disconnected timespan.
/// </summary>
public event Action<TimeSpan> ConnectionRestored
{
add => connection.ConnectionRestored = value;
remove => connection.ConnectionRestored -= value;
}
/// <summary>
/// Event when the connection to the server is paused based on a server indication. No operations can be performed while paused
/// </summary>
public event Action ActivityPaused
{
add => connection.ActivityPaused = value;
remove => connection.ActivityPaused -= value;
}
/// <summary>
/// Event when the connection to the server is unpaused after being paused
/// </summary>
public event Action ActivityUnpaused
{
add => connection.ActivityUnpaused = value;
remove => connection.ActivityUnpaused -= value;
}
/// <summary>
/// Event when an exception happens during the handling of the data
/// </summary>
public event Action<Exception> Exception
{
add => subscription.Exception = value;
remove => subscription.Exception -= value;
}
/// <summary>
/// The id of the socket
/// </summary>
public int SocketId => connection.Socket.Id;
/// <summary>
/// The id of the subscription
/// </summary>
public int Id => subscription.Id;
/// <summary>
/// ctor
/// </summary>
/// <param name="connection">The socket connection the subscription is on</param>
/// <param name="subscription">The subscription</param>
public UpdateSubscription(SocketConnection connection, SocketSubscription subscription)
{
this.connection = connection;
this.subscription = subscription;
}
/// <summary>
/// Close the subscription
/// </summary>
/// <returns></returns>
public Task CloseAsync()
{
return connection.CloseAsync(subscription);
}
/// <summary>
/// Close the socket to cause a reconnect
/// </summary>
/// <returns></returns>
internal Task ReconnectAsync()
{
return connection.Socket.CloseAsync();
}
/// <summary>
/// Unsubscribe a subscription
/// </summary>
/// <returns></returns>
internal async Task UnsubscribeAsync()
{
await connection.UnsubscribeAsync(subscription).ConfigureAwait(false);
}
/// <summary>
/// Resubscribe this subscription
/// </summary>
/// <returns></returns>
internal async Task<CallResult<bool>> ResubscribeAsync()
{
return await connection.ResubscribeAsync(subscription).ConfigureAwait(false);
}
}
}
CodePudding user response:
Example #1 and Example #2 do exactly the same thing. The only difference is that in Example #1 your handler has a name while in Example #2 it does not have a name (i.e. is anonymous). Because it has no name, it is difficult to unregister the handler with -=
, as you seem to have figured out.
If you want to be able to use a lambda but you also want to be able to unsubscribe, you can assign the lambda to a variable first.
Action<Exception> tmp = ex => _logger.LogInformation($"Error: {ex.StackTrace}");
subscription.Data.Exception = tmp;
subscription.Data.Exception -= tmp; //Unsubscribe