Skip to content

Commit 7ede01c

Browse files
authored
C# 8 phase 1 (#472)
* Start C# 8 refactor... * changed nullable to per file instead of projet wide - we can go back to project wide once we convert everything
1 parent f761f27 commit 7ede01c

14 files changed

+253
-257
lines changed

ExchangeSharp.sln

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 15
4-
VisualStudioVersion = 15.0.27130.2027
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.29318.209
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExchangeSharp", "ExchangeSharp\ExchangeSharp.csproj", "{B4ADDAEF-95BF-4FDA-8B2F-8B899EDA3F45}"
77
EndProject

ExchangeSharp/API/Common/APIRequestMaker.cs

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/*
22
MIT LICENSE
33
44
Copyright 2017 Digital Ruby, LLC - http://www.digitalruby.com
@@ -9,7 +9,7 @@ The above copyright notice and this permission notice shall be included in all c
99
1010
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1111
*/
12-
12+
#nullable enable
1313
using System;
1414
using System.Collections.Generic;
1515
using System.IO;
@@ -33,7 +33,7 @@ private class InternalHttpWebRequest : IHttpWebRequest
3333

3434
public InternalHttpWebRequest(Uri fullUri)
3535
{
36-
request = HttpWebRequest.Create(fullUri) as HttpWebRequest;
36+
request = (HttpWebRequest.Create(fullUri) as HttpWebRequest ?? throw new NullReferenceException("Failed to create HttpWebRequest"));
3737
request.KeepAlive = false;
3838
}
3939

@@ -145,22 +145,18 @@ public APIRequestMaker(IAPIRequestHandler api)
145145
/// The encoding of payload is API dependant but is typically json.</param>
146146
/// <param name="method">Request method or null for default. Example: 'GET' or 'POST'.</param>
147147
/// <returns>Raw response</returns>
148-
public async Task<string> MakeRequestAsync(string url, string baseUrl = null, Dictionary<string, object> payload = null, string method = null)
148+
public async Task<string> MakeRequestAsync(string url, string? baseUrl = null, Dictionary<string, object>? payload = null, string? method = null)
149149
{
150150
await new SynchronizationContextRemover();
151-
152151
await api.RateLimit.WaitToProceedAsync();
153-
if (string.IsNullOrWhiteSpace(url))
154-
{
155-
return null;
156-
}
157-
else if (url[0] != '/')
152+
153+
if (url[0] != '/')
158154
{
159155
url = "/" + url;
160156
}
161157

162158
string fullUrl = (baseUrl ?? api.BaseUrl) + url;
163-
method = method ?? api.RequestMethod;
159+
method ??= api.RequestMethod;
164160
Uri uri = api.ProcessRequestUrl(new UriBuilder(fullUrl), payload, method);
165161
InternalHttpWebRequest request = new InternalHttpWebRequest(uri)
166162
{
@@ -171,8 +167,8 @@ public async Task<string> MakeRequestAsync(string url, string baseUrl = null, Di
171167
request.AddHeader("user-agent", BaseAPI.RequestUserAgent);
172168
request.Timeout = request.ReadWriteTimeout = (int)api.RequestTimeout.TotalMilliseconds;
173169
await api.ProcessRequestAsync(request, payload);
174-
HttpWebResponse response = null;
175-
string responseString = null;
170+
HttpWebResponse? response = null;
171+
string responseString;
176172

177173
try
178174
{
@@ -194,21 +190,19 @@ public async Task<string> MakeRequestAsync(string url, string baseUrl = null, Di
194190
}
195191
}
196192
using (Stream responseStream = response.GetResponseStream())
193+
using (StreamReader responseStreamReader = new StreamReader(responseStream))
194+
responseString = responseStreamReader.ReadToEnd();
195+
if (response.StatusCode != HttpStatusCode.OK)
197196
{
198-
responseString = new StreamReader(responseStream).ReadToEnd();
199-
if (response.StatusCode != HttpStatusCode.OK)
197+
// 404 maybe return empty responseString
198+
if (string.IsNullOrWhiteSpace(responseString))
200199
{
201-
// 404 maybe return empty responseString
202-
if (string.IsNullOrWhiteSpace(responseString))
203-
{
204-
throw new APIException(string.Format("{0} - {1}",
205-
response.StatusCode.ConvertInvariant<int>(), response.StatusCode));
206-
}
207-
throw new APIException(responseString);
200+
throw new APIException(string.Format("{0} - {1}", response.StatusCode.ConvertInvariant<int>(), response.StatusCode));
208201
}
209-
api.ProcessResponse(new InternalHttpWebResponse(response));
210-
RequestStateChanged?.Invoke(this, RequestMakerState.Finished, responseString);
202+
throw new APIException(responseString);
211203
}
204+
api.ProcessResponse(new InternalHttpWebResponse(response));
205+
RequestStateChanged?.Invoke(this, RequestMakerState.Finished, responseString);
212206
}
213207
catch (Exception ex)
214208
{
@@ -225,6 +219,6 @@ public async Task<string> MakeRequestAsync(string url, string baseUrl = null, Di
225219
/// <summary>
226220
/// An action to execute when a request has been made (this request and state and object (response or exception))
227221
/// </summary>
228-
public Action<IAPIRequestMaker, RequestMakerState, object> RequestStateChanged { get; set; }
222+
public Action<IAPIRequestMaker, RequestMakerState, object>? RequestStateChanged { get; set; }
229223
}
230224
}

ExchangeSharp/API/Common/BaseAPI.cs

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/*
22
MIT LICENSE
33
44
Copyright 2017 Digital Ruby, LLC - http://www.digitalruby.com
@@ -9,7 +9,7 @@ The above copyright notice and this permission notice shall be included in all c
99
1010
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1111
*/
12-
12+
#nullable enable
1313
using System;
1414
using System.Collections.Generic;
1515
using System.Diagnostics;
@@ -148,28 +148,28 @@ public IAPIRequestMaker RequestMaker
148148
/// <summary>
149149
/// Base URL for the API for web sockets
150150
/// </summary>
151-
public virtual string BaseUrlWebSocket { get; set; }
151+
public virtual string BaseUrlWebSocket { get; set; } = string.Empty;
152152

153153
/// <summary>
154154
/// Gets the name of the API
155155
/// </summary>
156-
public virtual string Name { get; private set; }
156+
public virtual string Name { get; private set; } = string.Empty;
157157

158158
/// <summary>
159159
/// Public API key - only needs to be set if you are using private authenticated end points. Please use CryptoUtility.SaveUnprotectedStringsToFile to store your API keys, never store them in plain text!
160160
/// </summary>
161-
public System.Security.SecureString PublicApiKey { get; set; }
161+
public System.Security.SecureString? PublicApiKey { get; set; }
162162

163163
/// <summary>
164164
/// Private API key - only needs to be set if you are using private authenticated end points. Please use CryptoUtility.SaveUnprotectedStringsToFile to store your API keys, never store them in plain text!
165165
/// </summary>
166-
public System.Security.SecureString PrivateApiKey { get; set; }
166+
public System.Security.SecureString? PrivateApiKey { get; set; }
167167

168168
/// <summary>
169169
/// Pass phrase API key - only needs to be set if you are using private authenticated end points. Please use CryptoUtility.SaveUnprotectedStringsToFile to store your API keys, never store them in plain text!
170170
/// Most services do not require this, but Coinbase is an example of one that does
171171
/// </summary>
172-
public System.Security.SecureString Passphrase { get; set; }
172+
public System.Security.SecureString? Passphrase { get; set; }
173173

174174
/// <summary>
175175
/// Rate limiter - set this to a new limit if you are seeing your ip get blocked by the API
@@ -209,12 +209,12 @@ public IAPIRequestMaker RequestMaker
209209
/// <summary>
210210
/// The nonce end point for pulling down a server timestamp - override OnGetNonceOffset if you need custom handling
211211
/// </summary>
212-
public string NonceEndPoint { get; protected set; }
212+
public string? NonceEndPoint { get; protected set; }
213213

214214
/// <summary>
215215
/// The field in the json returned by the nonce end point to parse out - override OnGetNonceOffset if you need custom handling
216216
/// </summary>
217-
public string NonceEndPointField { get; protected set; }
217+
public string? NonceEndPointField { get; protected set; }
218218

219219
/// <summary>
220220
/// The type of value in the nonce end point field - override OnGetNonceOffset if you need custom handling.
@@ -297,7 +297,7 @@ public BaseAPI()
297297
}
298298
else
299299
{
300-
Name = (nameAttributes[0] as ApiNameAttribute).Name;
300+
Name = (nameAttributes[0] as ApiNameAttribute)?.Name ?? string.Empty;
301301
}
302302
}
303303

@@ -376,7 +376,7 @@ public async Task<object> GenerateNonceAsync()
376376
{
377377
// why an API would use a persistent incrementing counter for nonce is beyond me, ticks is so much better with a sliding window...
378378
// making it required to increment by 1 is also a pain - especially when restarting a process or rebooting.
379-
string tempFile = Path.Combine(Path.GetTempPath(), PublicApiKey.ToUnsecureString() + ".nonce");
379+
string tempFile = Path.Combine(Path.GetTempPath(), (PublicApiKey?.ToUnsecureString() ?? "unknown_pub_key") + ".nonce");
380380
if (!File.Exists(tempFile))
381381
{
382382
File.WriteAllText(tempFile, "0");
@@ -458,7 +458,7 @@ public void LoadAPIKeys(string encryptedFile)
458458
/// <param name="publicApiKey">Public Api Key</param>
459459
/// <param name="privateApiKey">Private Api Key</param>
460460
/// <param name="passPhrase">Pass phrase, null for none</param>
461-
public void LoadAPIKeysUnsecure(string publicApiKey, string privateApiKey, string passPhrase = null)
461+
public void LoadAPIKeysUnsecure(string publicApiKey, string privateApiKey, string? passPhrase = null)
462462
{
463463
PublicApiKey = publicApiKey.ToSecureString();
464464
PrivateApiKey = privateApiKey.ToSecureString();
@@ -474,7 +474,7 @@ public void LoadAPIKeysUnsecure(string publicApiKey, string privateApiKey, strin
474474
/// The encoding of payload is API dependant but is typically json.
475475
/// <param name="method">Request method or null for default</param>
476476
/// <returns>Raw response</returns>
477-
public Task<string> MakeRequestAsync(string url, string baseUrl = null, Dictionary<string, object> payload = null, string method = null) => requestMaker.MakeRequestAsync(url, baseUrl: baseUrl, payload: payload, method: method);
477+
public Task<string> MakeRequestAsync(string url, string? baseUrl = null, Dictionary<string, object>? payload = null, string? method = null) => requestMaker.MakeRequestAsync(url, baseUrl: baseUrl, payload: payload, method: method);
478478

479479
/// <summary>
480480
/// Make a JSON request to an API end point
@@ -485,7 +485,7 @@ public void LoadAPIKeysUnsecure(string publicApiKey, string privateApiKey, strin
485485
/// <param name="payload">Payload, can be null. For private API end points, the payload must contain a 'nonce' key set to GenerateNonce value.</param>
486486
/// <param name="requestMethod">Request method or null for default</param>
487487
/// <returns>Result decoded from JSON response</returns>
488-
public async Task<T> MakeJsonRequestAsync<T>(string url, string baseUrl = null, Dictionary<string, object> payload = null, string requestMethod = null)
488+
public async Task<T> MakeJsonRequestAsync<T>(string url, string? baseUrl = null, Dictionary<string, object>? payload = null, string? requestMethod = null)
489489
{
490490
await new SynchronizationContextRemover();
491491

@@ -509,8 +509,8 @@ public Task<IWebSocket> ConnectWebSocketAsync
509509
(
510510
string url,
511511
Func<IWebSocket, byte[], Task> messageCallback,
512-
WebSocketConnectionDelegate connectCallback = null,
513-
WebSocketConnectionDelegate disconnectCallback = null
512+
WebSocketConnectionDelegate? connectCallback = null,
513+
WebSocketConnectionDelegate? disconnectCallback = null
514514
)
515515
{
516516
if (messageCallback == null)
@@ -541,7 +541,7 @@ public Task<IWebSocket> ConnectWebSocketAsync
541541
/// </summary>
542542
/// <param name="payload">Payload to potentially send</param>
543543
/// <returns>True if an authenticated request can be made with the payload, false otherwise</returns>
544-
protected virtual bool CanMakeAuthenticatedRequest(IReadOnlyDictionary<string, object> payload)
544+
protected virtual bool CanMakeAuthenticatedRequest(IReadOnlyDictionary<string, object>? payload)
545545
{
546546
return (PrivateApiKey != null && PublicApiKey != null && payload != null && payload.ContainsKey("nonce"));
547547
}
@@ -552,7 +552,7 @@ protected virtual bool CanMakeAuthenticatedRequest(IReadOnlyDictionary<string, o
552552
/// </summary>
553553
/// <param name="request">Request</param>
554554
/// <param name="payload">Payload</param>
555-
protected virtual Task ProcessRequestAsync(IHttpWebRequest request, Dictionary<string, object> payload)
555+
protected virtual Task ProcessRequestAsync(IHttpWebRequest request, Dictionary<string, object>? payload)
556556
{
557557
return Task.CompletedTask;
558558
}
@@ -573,7 +573,7 @@ protected virtual void ProcessResponse(IHttpWebResponse response)
573573
/// <param name="payload">Payload</param>
574574
/// <param name="method">Method</param>
575575
/// <returns>Updated url</returns>
576-
protected virtual Uri ProcessRequestUrl(UriBuilder url, Dictionary<string, object> payload, string method)
576+
protected virtual Uri ProcessRequestUrl(UriBuilder url, Dictionary<string, object>? payload, string? method)
577577
{
578578
return url.Uri;
579579
}
@@ -656,22 +656,14 @@ protected virtual async Task OnGetNonceOffset()
656656

657657
try
658658
{
659-
JToken token = await MakeJsonRequestAsync<JToken>(NonceEndPoint);
659+
JToken token = await MakeJsonRequestAsync<JToken>(NonceEndPoint!);
660660
JToken value = token[NonceEndPointField];
661-
DateTime serverDate;
662-
switch (NonceEndPointStyle)
661+
DateTime serverDate = NonceEndPointStyle switch
663662
{
664-
case NonceStyle.Iso8601:
665-
serverDate = value.ToDateTimeInvariant();
666-
break;
667-
668-
case NonceStyle.UnixMilliseconds:
669-
serverDate = value.ConvertInvariant<long>().UnixTimeStampToDateTimeMilliseconds();
670-
break;
671-
672-
default:
673-
throw new ArgumentException("Invalid nonce end point style '" + NonceEndPointStyle + "' for exchange '" + Name + "'");
674-
}
663+
NonceStyle.Iso8601 => value.ToDateTimeInvariant(),
664+
NonceStyle.UnixMilliseconds => value.ConvertInvariant<long>().UnixTimeStampToDateTimeMilliseconds(),
665+
_ => throw new ArgumentException("Invalid nonce end point style '" + NonceEndPointStyle + "' for exchange '" + Name + "'"),
666+
};
675667
NonceOffset = (CryptoUtility.UtcNow - serverDate);
676668
}
677669
catch
@@ -681,7 +673,7 @@ protected virtual async Task OnGetNonceOffset()
681673
}
682674
}
683675

684-
async Task IAPIRequestHandler.ProcessRequestAsync(IHttpWebRequest request, Dictionary<string, object> payload)
676+
async Task IAPIRequestHandler.ProcessRequestAsync(IHttpWebRequest request, Dictionary<string, object>? payload)
685677
{
686678
await ProcessRequestAsync(request, payload);
687679
}
@@ -691,7 +683,7 @@ void IAPIRequestHandler.ProcessResponse(IHttpWebResponse response)
691683
ProcessResponse(response);
692684
}
693685

694-
Uri IAPIRequestHandler.ProcessRequestUrl(UriBuilder url, Dictionary<string, object> payload, string method)
686+
Uri IAPIRequestHandler.ProcessRequestUrl(UriBuilder url, Dictionary<string, object>? payload, string? method)
695687
{
696688
return ProcessRequestUrl(url, payload, method);
697689
}

ExchangeSharp/API/Common/IAPIRequestMaker.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/*
22
MIT LICENSE
33
44
Copyright 2017 Digital Ruby, LLC - http://www.digitalruby.com
@@ -9,7 +9,7 @@ The above copyright notice and this permission notice shall be included in all c
99
1010
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1111
*/
12-
12+
#nullable enable
1313
using System;
1414
using System.Collections.Generic;
1515
using System.Net;
@@ -52,12 +52,13 @@ public interface IAPIRequestMaker
5252
/// The encoding of payload is API dependant but is typically json.</param>
5353
/// <param name="method">Request method or null for default</param>
5454
/// <returns>Raw response</returns>
55-
Task<string> MakeRequestAsync(string url, string baseUrl = null, Dictionary<string, object> payload = null, string method = null);
55+
/// <exception cref="System.Exception">Request fails</exception>
56+
Task<string> MakeRequestAsync(string url, string? baseUrl = null, Dictionary<string, object>? payload = null, string? method = null);
5657

5758
/// <summary>
5859
/// An action to execute when a request has been made (this request and state and object (response or exception))
5960
/// </summary>
60-
Action<IAPIRequestMaker, RequestMakerState, object> RequestStateChanged { get; set; }
61+
Action<IAPIRequestMaker, RequestMakerState, object>? RequestStateChanged { get; set; }
6162
}
6263

6364
/// <summary>
@@ -130,7 +131,7 @@ public interface IAPIRequestHandler
130131
/// </summary>
131132
/// <param name="request">Request</param>
132133
/// <param name="payload">Payload</param>
133-
Task ProcessRequestAsync(IHttpWebRequest request, Dictionary<string, object> payload);
134+
Task ProcessRequestAsync(IHttpWebRequest request, Dictionary<string, object>? payload);
134135

135136
/// <summary>
136137
/// Additional handling for response
@@ -145,7 +146,7 @@ public interface IAPIRequestHandler
145146
/// <param name="payload">Payload</param>
146147
/// <param name="method">Method</param>
147148
/// <returns>Updated url</returns>
148-
Uri ProcessRequestUrl(UriBuilder url, Dictionary<string, object> payload, string method);
149+
Uri ProcessRequestUrl(UriBuilder url, Dictionary<string, object>? payload, string method);
149150

150151
/// <summary>
151152
/// Base url for the request
@@ -177,4 +178,4 @@ public interface IAPIRequestHandler
177178
/// </summary>
178179
RateGate RateLimit { get; }
179180
}
180-
}
181+
}

0 commit comments

Comments
 (0)