csharp C#功能容器和实用程序
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了csharp C#功能容器和实用程序相关的知识,希望对你有一定的参考价值。
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
namespace RZ.Foundation {
public static class CollectionExtension{
public static void Clear<T>(this ConcurrentQueue<T> queue){
Contract.Requires(queue != null);
T item;
while(queue.TryDequeue(out item)) { }
}
public static void ForEach<T>(this IEnumerable<T> seq, Action<T> handler){
Contract.Requires(seq != null);
Contract.Requires(handler != null);
foreach (var item in seq)
handler(item);
}
public static void ForEachIndex<T>(this IEnumerable<T> seq, Action<T,int> handler){
Contract.Requires(seq != null);
Contract.Requires(handler != null);
var index = 0;
foreach (var item in seq)
handler(item, index++);
}
public static T[] RemoveAt<T>(this T[] array, int n){
Contract.Requires(array != null);
Contract.Requires(n >= 0);
Contract.Ensures(Contract.Result<T[]>() != null);
return array.Take(n).Skip(n + 1).ToArray();
}
public static Option<T> Get<TKey, T>(this Dictionary<TKey, T> dict, TKey key){
Contract.Requires(dict != null);
Contract.Requires(!ReferenceEquals(null, key));
Contract.Ensures(Contract.Result<Option<T>>() != null);
T result;
return dict.TryGetValue(key, out result) ? (Option<T>) Option<T>.Some(result) : Option<T>.None();
}
public static Option<T> TryFirst<T>(this IEnumerable<T> seq, Func<T, bool> predicate){
Contract.Requires(seq != null);
Contract.Requires(predicate != null);
Contract.Ensures(Contract.Result<Option<T>>() != null);
foreach (var item in seq.Where(predicate))
return Option<T>.Some(item);
return Option<T>.None();
}
}
}
using System;
using RZ.Foundation
namespace RZ.Extensions
{
public static class ExceptionExtension
{
const string CodeField = "code";
const string DataField = "data";
public static Exception CreateError(string message, string code, string source, object data)
{
var exn = new Exception(message);
exn.Source = source;
exn.Data.Add(CodeField, code);
exn.Data.Add(DataField, data);
return exn;
}
public static Option<string> GetErrorCode(this Exception exn) => exn.GetData(CodeField).TryCast<string>();
public static Option<object> GetData(this Exception exn) => exn.GetData(DataField);
public static Option<object> GetData(this Exception exn, string field) => exn.Data.Contains(field) ? exn.Data[field] : null;
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace RZ.Foundation
{
/// <summary>
/// Option for HTTP Request.
/// </summary>
public class HttpRequestOptions
{
/// <summary>
/// Authentication token.
/// </summary>
public string Token { get; set; }
/// <summary>
/// Accept Language header.
/// </summary>
public string Language { get; set; }
}
/// <summary>
/// Represent HTTP API Error.
/// </summary>
public class ApiError
{
/// <summary>
/// Instantiate API error.
/// </summary>
/// <param name="code"></param>
/// <param name="message"></param>
public ApiError(int code, string message)
{
Code = code;
Message = message;
}
/// <summary>
/// Error code, which could be either HTTP code or custom code.
/// </summary>
public int Code { get; }
/// <summary>
/// Error message.
/// </summary>
public string Message { get; }
}
/// <summary>
/// Custom error code for HTTP request module
/// </summary>
public enum ApiErrorCode
{
/// <summary>
/// HTTP request failure.
/// </summary>
HttpRequest = -1,
/// <summary>
/// Failure from JSON deserialization.
/// </summary>
JsonDecode = -2
}
/// <summary>
/// HTTP request service. This HTTP expects JSON as the payload, both on request and response.
/// </summary>
public interface IHttpRequest
{
/// <summary>
/// GET from a url.
/// </summary>
/// <typeparam name="T">Type of result</typeparam>
/// <param name="url"></param>
/// <param name="options"></param>
/// <returns></returns>
Result<T, ApiError> Get<T>(Uri url, HttpRequestOptions options = null);
/// <summary>
/// POST to a url.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="data"></param>
/// <param name="options"></param>
/// <returns></returns>
Result<T, ApiError> Post<T>(Uri url, object data, HttpRequestOptions options = null);
/// <summary>
/// PUT to a url.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="uri"></param>
/// <param name="data"></param>
/// <param name="options"></param>
/// <returns></returns>
Result<T, ApiError> Put<T>(Uri uri, object data, HttpRequestOptions options = null);
/// <summary>
/// DELETE a url.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="uri"></param>
/// <param name="options"></param>
/// <returns></returns>
Result<T, ApiError> Delete<T>(Uri uri, HttpRequestOptions options = null);
Task<Result<T, ApiError>> GetAsync<T>(Uri uri, HttpRequestOptions options = null);
/// <summary>
/// Asynchronously post to a url.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <param name="data"></param>
/// <param name="options"></param>
/// <returns></returns>
Task<Result<T, ApiError>> PostAsync<T>(Uri url, object data, HttpRequestOptions options = null);
Task<Result<T, ApiError>> PutAsync<T>(Uri uri, object data, HttpRequestOptions options = null);
Task<Result<T, ApiError>> DeleteAsync<T>(Uri uri, HttpRequestOptions options = null);
}
/// <summary>
/// Default implementation of <see cref="IHttpRequest"/>.
/// </summary>
public class HttpRequest : IHttpRequest
{
const string JsonMimeType = "application/json";
readonly ILogger<HttpRequest> logger;
/// <summary>
/// Instantiate a default HTTP request service.
/// </summary>
public HttpRequest(ILoggerFactory loggerFactory)
{
logger = loggerFactory.CreateLogger<HttpRequest>();
}
/// <summary>
/// GET from a url.
/// </summary>
public Result<T, ApiError> Get<T>(Uri url, HttpRequestOptions options)
{
return get<T>(HttpMethod.Get, url, options ?? new HttpRequestOptions());
}
/// <summary>
/// POST to a url.
/// </summary>
public Result<T, ApiError> Post<T>(Uri url, object data, HttpRequestOptions options)
{
return get<T>(HttpMethod.Post, url, options ?? new HttpRequestOptions(), data);
}
/// <summary>
/// PUT to a url.
/// </summary>
public Result<T, ApiError> Put<T>(Uri url, object data, HttpRequestOptions options)
{
return get<T>(HttpMethod.Put, url, options ?? new HttpRequestOptions(), data);
}
/// <summary>
/// DELETE a url.
/// </summary>
public Result<T, ApiError> Delete<T>(Uri url, HttpRequestOptions options)
{
return request(HttpMethod.Delete, url, options ?? new HttpRequestOptions())
.Result.Map(_ => default(T));
}
public Task<Result<T, ApiError>> GetAsync<T>(Uri uri, HttpRequestOptions options = null)
{
return requestJson<T>(HttpMethod.Get, uri, options);
}
public Task<Result<T, ApiError>> PostAsync<T>(Uri uri, object data, HttpRequestOptions options = null)
{
return requestJson<T>(HttpMethod.Post, uri, options, data);
}
public Task<Result<T, ApiError>> PutAsync<T>(Uri uri, object data, HttpRequestOptions options = null)
{
return requestJson<T>(HttpMethod.Put, uri, options, data);
}
public Task<Result<T, ApiError>> DeleteAsync<T>(Uri uri, HttpRequestOptions options = null)
{
return request(HttpMethod.Delete, uri, options ?? new HttpRequestOptions())
.Map(r => r.Map(_ => default(T)));
}
Result<T, ApiError> get<T>(HttpMethod method, Uri uri, HttpRequestOptions options, object data = null)
{
return request(method, uri, options, data).Result.Map(JsonConvert.DeserializeObject<T>);
}
static HttpRequestMessage CreateRequest(HttpMethod method, Uri uri, object data, HttpRequestOptions options)
{
var request = new HttpRequestMessage
{
RequestUri = uri,
Method = method
};
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(JsonMimeType));
if (!string.IsNullOrEmpty(options.Token))
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", options.Token);
if (!string.IsNullOrEmpty(options.Language))
request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(options.Language));
if (data != null)
{
var requestContent = JsonConvert.SerializeObject(data);
request.Content = new StringContent(requestContent);
request.Content.Headers.ContentType = new MediaTypeHeaderValue(JsonMimeType);
}
return request;
}
static async Task<Result<string, ApiError>> RequestAsString(HttpRequestMessage request)
{
try
{
using (var http = new HttpClient())
{
var response = await http.SendAsync(request).NoSync();
var text = await response.Content.ReadAsStringAsync();
return response.IsSuccessStatusCode
? text.AsSuccess<string, ApiError>()
: new ApiError((int)response.StatusCode, text).AsFailure<string, ApiError>();
}
}
catch (HttpRequestException ex)
{
return new ApiError((int)ApiErrorCode.HttpRequest, ex.Message).AsFailure<string, ApiError>();
}
}
static Result<T, ApiError> JsonDecode<T>(string s)
{
try {
return JsonConvert.DeserializeObject<T>(s).AsSuccess<T, ApiError>();
} catch (JsonSerializationException ex) {
return new ApiError((int)ApiErrorCode.JsonDecode, ex.Message).AsFailure<T, ApiError>();
}
}
async Task<Result<string, ApiError>> request(
HttpMethod method,
Uri uri,
HttpRequestOptions options,
object data = null
)
{
logger.LogDebug($"HTTP request (async) method: {method.Method}, uri: {uri}");
var request = CreateRequest(method, uri, data, options);
return await RequestAsString(request);
}
Task<Result<T, ApiError>> requestJson<T>(
HttpMethod method,
Uri uri,
HttpRequestOptions options,
object data = null
)
{
return request(method, uri, options, data).Map(r => r.Chain(JsonDecode<T>));
}
}
}
using System;
using System.Runtime.InteropServices;
namespace RZ.Foundation
{
public static class OptionHelper
{
public static Option<T> ToOption<T>(this T data) => data;
public static Result<T, F> ToResult<T, F>(this Option<T> o, Func<F> none) => o.IsSome ? o.Get().AsSuccess<T,F>() : none();
public static ApiResult<T> ToApiResult<T>(this Option<T> o, Func<Exception> none) => o.IsSome ? o.Get().AsApiSuccess() : none();
}
public struct Option<T>
{
static readonly Option<T> NoneSingleton = new Option<T>();
Option(T v)
{
isSome = true;
value = v;
}
readonly bool isSome;
readonly T value;
public static implicit operator Option<T> (T value) => From(value);
public Option<TB> Chain<TB>(Func<T, Option<TB>> mapper) => isSome? mapper(value) : Option<TB>.None();
public Option<T> IfNoneTry(Func<Option<T>> other) => isSome? this : other();
public bool IsSome => isSome;
public bool IsNone => !isSome;
public void Apply(Action<T> handler)
{
if (isSome) handler(value);
}
public void Apply(Action noneHandler, Action<T> someHandler)
{
if (isSome) someHandler(value); else noneHandler();
}
public T Get() => isSome? value : throw new InvalidOperationException();
public TResult Get<TResult>(Func<T, TResult> someHandler, Func<TResult> noneHandler) => isSome? someHandler(value) : noneHandler();
public T GetOrElse(Func<T> noneHandler) => isSome? value : noneHandler();
public T GetOrDefault() => isSome? value : default(T);
public Option<TB> Map<TB>(Func<T, TB> mapper) => isSome? mapper(value) : Option<TB>.None();
public Option<U> TryCast<U>()
{
if (!isSome) return Option<U>.None();
if (Equals(value, null)) return Option<U>.Some(default(U));
var converted = Convert.ChangeType(value, typeof(U));
return Equals(converted, null)
? Option<U>.None()
: Option<U>.Some((U)converted);
}
#region Equality
public override bool Equals(object obj)
{
var other = obj as Option<T>?;
return other != null && Equals(value, other.Value.value);
}
public override int GetHashCode() => isSome? value.GetHashCode() : 0;
#endregion
public T GetOrElse(T defaultValue) => isSome? value : defaultValue;
public static Option<T> From(Func<T> initializer)
{
try
{
var result = initializer();
return Equals(result, null) ? None() : Some(result);
}
catch (Exception)
{
return None();
}
}
public static Option<T> From(T value) => Equals(value, null) ? None() : Some(value);
public static Option<T> None() => NoneSingleton;
public static Option<T> Some(T value) => new Option<T>(value);
}
[StructLayout(LayoutKind.Auto)]
public struct OptionSerializable<T>
{
public bool HasValue;
public T Value;
public OptionSerializable(Option<T> opt)
{
HasValue = opt.IsSome;
Value = opt.Get(x => x, () => default(T));
}
public Option<T> ToOption() => HasValue ? Value : Option<T>.None();
}
}
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace RZ.Foundation
{
public static class TaskExtensions
{
/// <summary>
/// Prevent locking from Synchronization Context
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public static ConfiguredTaskAwaitable NoSync(this Task t) => t.ConfigureAwait(continueOnCapturedContext: false);
public static ConfiguredTaskAwaitable<T> NoSync<T>(this Task<T> t) => t.ConfigureAwait(continueOnCapturedContext: false);
public static Task<TB> Map<TA, TB>(this Task<TA> task, Func<TA, TB> mapper) =>
task.ContinueWith( t => t.IsFaulted ? throw new AggregateException(t.Exception?.InnerExceptions)
: t.IsCanceled ? throw new TaskCanceledException(t)
: mapper(t.Result)
, TaskContinuationOptions.ExecuteSynchronously);
public static Task<Result<T, Exception>> MapEither<T>(this Task<T> task) => MapEither(task, CancellationToken.None);
public static Task<Result<T, Exception>> MapEither<T>(this Task<T> task, CancellationToken token) =>
task.ContinueWith( t => token.IsCancellationRequested || t.IsCanceled || t.IsFaulted
? GetException(t).AsFailure<T, Exception>()
: t.Result.AsSuccess<T, Exception>()
, token
, TaskContinuationOptions.ExecuteSynchronously
, TaskScheduler.Current
);
public static Task<ApiResult<T>> ToApiResult<T>(this Task<T> task) => ToApiResult(task, CancellationToken.None);
public static Task<ApiResult<T>> ToApiResult<T>(this Task<T> task, CancellationToken token) =>
task.ContinueWith(t => token.IsCancellationRequested || t.IsCanceled || t.IsFaulted
? GetException(t).AsApiFailure<T>()
: t.Result.AsApiSuccess()
, token
, TaskContinuationOptions.ExecuteSynchronously
, TaskScheduler.Current
);
static Exception GetException<T>(Task<T> t) => t.Exception ?? (Exception) new TaskCanceledException(t);
public static Task<TB> Chain<TA, TB>(this Task<TA> task, Func<TA, Task<TB>> chain)
{
var result = new TaskCompletionSource<TB>();
task.ContinueWith(t => {
if (t.IsCanceled)
result.SetCanceled();
else if (t.IsFaulted)
// ReSharper disable once AssignNullToNotNullAttribute
result.SetException(t.Exception);
else
{
Debug.Assert(t.IsCompleted);
chain(t.Result)
.Then(success: r => result.SetResult(r),
faulted: ex => result.SetException(ex),
canceled: () => result.SetCanceled());
}
});
return result.Task;
}
public static Task Then<T>(this Task<T> task, Action<T> success = null, Action<Exception> faulted = null,
Action canceled = null)
{
return task.ContinueWith(t => {
if (t.IsCompleted)
success?.Invoke(t.Result);
else if (t.IsFaulted)
faulted?.Invoke(t.Exception);
else
{
Contract.Assert(t.IsCanceled);
canceled?.Invoke();
}
});
}
}
}
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using RZ.Foundation;
using RZ.Extensions;
namespace RZ.Net
{
public struct HttpAuthentication
{
public string Scheme;
public Option<string> Parameter;
public static HttpAuthentication Bearer(string parameter) => new HttpAuthentication { Scheme = "Bearer", Parameter = parameter };
}
public struct HttpRequestOption
{
public Option<HttpAuthentication> Authentication;
}
public interface ITextHttp
{
Task<ApiResult<string>> Request(HttpMethod method, Uri uri, Option<string> data, Option<HttpRequestOption> config);
Task<ApiResult<string>> Get(Uri uri, Option<HttpRequestOption> config);
Task<ApiResult<string>> Post(Uri uri, Option<string> data, Option<HttpRequestOption> config);
Task<ApiResult<string>> Put(Uri uri, Option<string> data, Option<HttpRequestOption> config);
Task<ApiResult<string>> Delete(Uri uri, Option<HttpRequestOption> config);
}
/// <summary>
/// Text HTTP is a HTTP requester that specially works with string as input and output.
/// </summary>
public class TextHttp : ITextHttp
{
static readonly MediaTypeWithQualityHeaderValue JsonMimeType = new MediaTypeWithQualityHeaderValue("application/json");
public async Task<ApiResult<string>> Request( HttpMethod method
, Uri uri
, Option<string> data
, Option<HttpRequestOption> config) {
var req = new HttpRequestMessage(method, uri);
data.Apply(text =>
{
req.Content = new StringContent(text);
req.Content.Headers.ContentType = JsonMimeType;
});
config.Apply(ApplyConfig(req));
using(var http = new HttpClient())
{
var res = await http.SendAsync(req);
var text = await res.Content.ReadAsStringAsync();
return res.IsSuccessStatusCode
? text.AsApiSuccess()
: ExceptionExtension.CreateError(res.ReasonPhrase, $"http-{(int)res.StatusCode}", uri.ToString(), text);
}
}
public Task<ApiResult<string>> Get(Uri uri, Option<HttpRequestOption> config) => Request(HttpMethod.Get, uri, null, config);
public Task<ApiResult<string>> Post(Uri uri, Option<string> data, Option<HttpRequestOption> config) => Request(HttpMethod.Post, uri, data, config);
public Task<ApiResult<string>> Put(Uri uri, Option<string> data, Option<HttpRequestOption> config) => Request(HttpMethod.Post, uri, data, config);
public Task<ApiResult<string>> Delete(Uri uri, Option<HttpRequestOption> config) => Request(HttpMethod.Post, uri, null, config);
static Action<HttpRequestOption> ApplyConfig(HttpRequestMessage req) => config =>
config.Authentication.Apply(auth =>
req.Headers.Authorization = auth.Parameter.Get( p => new AuthenticationHeaderValue(auth.Scheme, p)
, () => new AuthenticationHeaderValue(auth.Scheme)));
}
}
namespace RZ.Foundation {
public static class Prelude {
public static Func<T> Constant<T>(T x) => () => x;
public static T Identity<T>(T x) => x;
public static void Noop() { }
}
}
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace RZ.Foundation
{
/// <summary>
/// Helper functions for <see cref="Result{TSuccess, TFail}"/>
/// </summary>
public static class ResultHelper
{
/// <summary>
/// Helper function for creating a success result from any value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TFail"></typeparam>
/// <param name="data"></param>
/// <returns></returns>
public static Result<T, TFail> AsSuccess<T, TFail>(this T data) => data;
/// <summary>
/// Helper function for creating a failure result from any value.
/// </summary>
/// <typeparam name="TSuccess"></typeparam>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <returns></returns>
public static Result<TSuccess, T> AsFailure<TSuccess, T>(this T data) => data;
public static ApiResult<T> AsApiSuccess<T>(this T data) => data;
public static ApiResult<T> AsApiFailure<T>(this Exception data) => data;
public static Task<ApiResult<U>> ChainApiTask<T, U>(this ApiResult<T> result, Func<T, Task<ApiResult<U>>> f) => result.Get(f, ex => Task.FromResult((ApiResult<U>) ex));
public static Task<Result<T, Exception>> AsFailTask<T>(this Exception ex) => Task.FromResult(ex.AsFailure<T, Exception>());
public static Task<Result<T, Exception>> AsFailTask<T>(this string message) => Task.FromResult(new Exception(message).AsFailure<T, Exception>());
}
/// <summary>
/// Represent two-state result.
/// </summary>
/// <typeparam name="TSuccess">Type that represents success data.</typeparam>
/// <typeparam name="TFail">Type that represents failed data.</typeparam>
public struct Result<TSuccess, TFail>
{
readonly bool isFailed;
readonly TFail error;
readonly TSuccess data;
public Result(TSuccess success)
{
isFailed = false;
error = default(TFail);
data = success;
}
public Result(TFail fail)
{
isFailed = true;
data = default(TSuccess);
error = fail;
}
/// <summary>
/// Check if this result represents success state.
/// </summary>
public bool IsSuccess => !isFailed;
/// <summary>
/// Check if this result represents failure state.
/// </summary>
public bool IsFail => isFailed;
/// <summary>
/// Get instance of success type.
/// </summary>
/// <returns>Instance of success type.</returns>
public TSuccess GetSuccess() => isFailed ? throw new InvalidOperationException() : data;
/// <summary>
/// Get instance of failed type.
/// </summary>
/// <returns>Instance of failed type.</returns>
public TFail GetFail() => isFailed? error : throw new InvalidOperationException();
/// <summary>
/// Transform Result into a value.
/// </summary>
/// <typeparam name="T">Target type</typeparam>
/// <param name="success">Transformer function for success case.</param>
/// <param name="fail">Transformer function for failure case.</param>
/// <returns></returns>
public T Get<T>(Func<TSuccess, T> success, Func<TFail, T> fail) => isFailed? fail(error) : success(data);
/// <summary>
/// Transform success result to other success type. Do nothing if current result is a failure.
/// </summary>
/// <typeparam name="U">Target success type</typeparam>
/// <param name="mapper">Function to transform success type.</param>
/// <returns>New Result type</returns>
public Result<U, TFail> Map<U>(Func<TSuccess, U> mapper) => isFailed? (Result<U,TFail>) error : mapper(data);
/// <summary>
/// Transform success result to other success or failed type. Do nothing if current result is a failure.
/// </summary>
/// <typeparam name="U">Target success type</typeparam>
/// <param name="mapper">Function to transform success type to either U type or _TFail_ type. </param>
/// <returns></returns>
public Result<U, TFail> Chain<U>(Func<TSuccess, Result<U, TFail>> mapper) => isFailed? (Result<U,TFail>) error : mapper(data);
/// <summary>
/// Convert Result.
/// </summary>
/// <typeparam name="U"></typeparam>
/// <typeparam name="V"></typeparam>
/// <param name="successMapper"></param>
/// <param name="failMapper"></param>
/// <returns></returns>
public Result<U, V> MapAll<U, V>(Func<TSuccess, U> successMapper, Func<TFail, V> failMapper) => isFailed? (Result<U,V>) failMapper(error) : successMapper(data);
/// <summary>
/// Try calling <paramref name="f"/> if current result is failure.
/// </summary>
/// <param name="f"></param>
/// <returns></returns>
public Result<TSuccess, TFail> OrElse(Func<TFail, Result<TSuccess, TFail>> f) => isFailed? f(error) : this;
/// <summary>
/// Call <paramref name="f"/> if current result is success.
/// </summary>
/// <param name="f"></param>
/// <returns>Always return current result.</returns>
public Result<TSuccess, TFail> Apply(Action<TSuccess> f)
{
if (!isFailed) f(data);
return this;
}
public static implicit operator Result<TSuccess, TFail>(TSuccess success) => new Result<TSuccess, TFail>(success);
public static implicit operator Result<TSuccess, TFail>(TFail failed) => new Result<TSuccess, TFail>(failed);
}
/// <summary>
/// Represent two-state result. Failure state is represented by <seealso cref="Exception"/>
/// </summary>
/// <typeparam name="T">Type that represents success data.</typeparam>
public struct ApiResult<T>
{
readonly Exception error;
readonly T data;
public ApiResult(T success)
{
error = null;
data = success;
}
public ApiResult(Exception fail)
{
data = default(T);
error = fail;
}
public static implicit operator ApiResult<T>(T success) => Equals(success,null)? new InvalidOperationException() : new ApiResult<T>(success);
public static implicit operator ApiResult<T>(Exception failed) => failed == null? new ArgumentNullException("failed") : new ApiResult<T>(failed);
public bool IsSuccess => error == null;
public bool IsFail => error != null;
public T GetSuccess() => IsFail? throw new InvalidOperationException() : data;
public Exception GetFail() => IsFail? error : throw new InvalidOperationException();
public U Get<U>(Func<T, U> success, Func<Exception, U> fail) => IsFail? fail(error) : success(data);
public ApiResult<U> Map<U>(Func<T, U> mapper) => IsFail? new ApiResult<U>(error) : mapper(data);
public ApiResult<U> Chain<U>(Func<T, ApiResult<U>> mapper) => IsFail? new ApiResult<U>(error) : mapper(data);
public ApiResult<T> Apply(Action<T> f)
{
if (IsSuccess) f(data);
return this;
}
public ApiResult<T> IfFail(Action<Exception> f)
{
if (IsFail) f(error);
return this;
}
}
}
using System;
namespace RZ.Foundation {
public sealed class SingleCache<T>
{
readonly TimeSpan _lifetime;
readonly Func<T> _loader;
readonly object _locker = new object();
T data;
DateTime expired = DateTime.MinValue;
public SingleCache(TimeSpan lifetime, Func<T> loader)
{
_lifetime = lifetime;
_loader = loader;
}
public T Value
{
get
{
if (expired > DateTime.Now) return data;
lock (_locker)
if (expired < DateTime.Now)
{
data = _loader();
expired = DateTime.Now + _lifetime;
}
return data;
}
}
}
}
以上是关于csharp C#功能容器和实用程序的主要内容,如果未能解决你的问题,请参考以下文章
csharp 用于以人类可读方式表示任意字节长度的实用程序。