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 用于以人类可读方式表示任意字节长度的实用程序。

csharp 一组用于App_code的自定义实用程序函数

c_cpp Windows实用程序功能

csharp 功能齐全的计算器用C#编写,紧凑和格式化

不使用 gcloud 实用程序从 docker 容器访问 GCP 云存储桶

csharp C# - 执行带范围的功能