2021年6月26日 星期六

C# yield return 疊代器模式

有一個題目請印出1~100

使用for來解
 
for (int i = 1; i <= 100; i++)
{
    Console.WriteLine("Number: {0}", i);
}

這樣寫沒什麼毛病,簡單明瞭
若改成印出 1~100 之中,2 或 3 的倍數

 
for (int i = 1; i <= 100; i++)
{
    bool match = false;

    if (i % 2 == 0) match = true;
    if (i % 3 == 0) match = true;

    if (match == true)
    {
        Console.WriteLine("Number: {0}", i);
    }
}

 好像for裡面沒這麼乾淨了,若之後邏輯更複雜,可想而知後面修改的人看到這個迴圈
可能會去填離職單,所以這時候可以使用Iterator Pattern(疊代器模式)來把邏輯包起來寫法如下

 
public class Example : IEnumerator
    {
        private int _start = 1;
        private int _end = 100;
        private int _current;
        public Example(int start, int end)
        {
            this._start = start;
            this._end = end;
            this.Reset();
        }

        public object Current
        {
            get { return this._current; }
        }

        public bool MoveNext()
        {
            this._current++;
            bool match = false;            
            if (this._current % 2 == 0) match = true;
            if (this._current % 3 == 0) match = true;
            return match;
        }

        public void Reset()
        {
            this._current = 0;
        }
    }

使用方法如下
 
Example e = new Example(1, 100);
while (e.MoveNext())
{
    Console.WriteLine("Number: {0}", e.Current);
}

這樣寫的好處是你完全不需要知道實作的邏輯,你只要相信類別給你的數字顯示即可
程式碼也變得很好閱讀,也非常的乾淨,但是每次都要叫你實作Iterator Pattern
都要寫一大坨類別實作,真的也是滿累的
所以在這邊要介紹一下c#的yield return
若改成yield return會變成怎樣呢,請看以下實作

 
public static IEnumerable<int> yieldExample(int start, int end)
        {
            for (int i = 1; i <= 100; i++)
            {
                bool match = false;

                if (i % 2 == 0) match = true;
                if (i % 3 == 0) match = true;

                if (match == true)
                {
                    yield return i;
                }
            }
        }

如何使用呢
 
public void Example()
        {
            foreach (var current in yieldExample(1, 100))
            {
                Console.WriteLine("Current Number: {0}", current);
            }
        }

挖靠c#居然有這麼好用的東西

ASP.net Core 3 DI 自動註冊

在Asp.net core會開始使用DI
每次都會忘記註冊,發生錯誤才會發現,我看同事好像也很有耐心的一個一個的註冊
耐心雖然是種美德,但我個人比較懶惰,於是弄了一個DI自動註冊
先建立一個擴展

public static class NativeInjectorConfig
    {
        public static void RegisterServices(this IServiceCollection services)
        {
            //繼承ServiceBase的自動註冊
            services.RegisterInheritedTypes(typeof(ServiceBase).Assembly, typeof(ServiceBase));
        }

        public static void RegisterInheritedTypes(this IServiceCollection container, Assembly assembly, Type baseType)
        {
            var allTypes = assembly.GetTypes();
            var baseInterfaces = baseType.GetInterfaces();
            foreach (var type in allTypes)
            {
                if (type.BaseType != null && type.BaseType.GenericEq(baseType))
                {
                    var typeInterface = type.GetInterfaces().FirstOrDefault(x => !baseInterfaces.Any(bi => bi.GenericEq(x)));
                    if (typeInterface == null)
                    {
                        continue;
                    }
                    container.AddScoped(typeInterface, type);
                }
            }
        }

        public static bool GenericEq(this Type type, Type toCompare)
        {
            return type.Namespace == toCompare.Namespace && type.Name == toCompare.Name;
        }
    }


然後再到startup加入

public void ConfigureServices(IServiceCollection services)
        {    
            services.RegisterServices();            
        }

所以以上只要繼承ServerBase類別就會被偵測到自動註冊

ASP.net Core 3 Webapi 全域錯誤處理

進入了.net core時代,開始有了Middleware
所以我們可以從startup加入一個ErrrorHandler的Middleware
於是建了一個處理錯誤的class

 
  public class ErrorHandlerMiddleware
  {

        private ILogger _logger = NLog.LogManager.GetCurrentClassLogger();
        private readonly RequestDelegate _next;

        public ErrorHandlerMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception error)
            {
                var response = context.Response;
                response.StatusCode = (int)HttpStatusCode.OK;
                response.ContentType = "application/json";

                var result = new ResponseBase<string>();
                result.Message = error.GetErrorMessage();
                result.StatusCode = EnumStatusCode.Fail;

                await response.WriteAsync(JsonSerializer.Serialize(result));
            }
        }
    }

之後再到startup.cs加入這一個class就可以全域處理錯誤

  
  public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {         
           app.UseHttpsRedirection();
            app.UseRouting();
            app.UseCors("CorsPolicy");
            app.UseAuthorization();

            app.UseMiddleware<ErrorHandlerMiddleware>();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
所以Controller發生了錯誤直接會在這邊統一處理,就不用每個Controller寫try catch 是否方便了許多