20210604のC#に関する記事は5件です。

【C#】NPOIを使って「.xls」と「.xlsx」に対して罫線を引く

概要 「.xls」と「.xlsx」に対して罫線を引く 以下に記載コードは.xlsと.xlsx共通コード 使用バージョン Npoi 2.5.3 .Netframwork4.7.2 Windowsフォームアプリケーションで実装 セルに対して罫線を引くソース ファイルの読み込みを含めて以下に記載 以下結果 実際に引いたコード sample.cs using NPOI.SS.UserModel; using System.IO; using System.Diagnostics; using System.Windows.Forms; /// filepathに「.xls」or 「.xlsx」のファイルパスを渡してください public void WriteCell(string filepath) { book = WorkbookFactory.Create(filepath); var sheet = book.GetSheetAt(0); IRow row = sheet.CreateRow(5); ICell cell = row.CreateCell(5); cell.SetCellType(CellType.String); cell.SetCellValue("testssss"); ICellStyle cellStyle = sheet.Workbook.CreateCellStyle(); //セルの上に引く cellStyle.BorderTop = NPOI.SS.UserModel.BorderStyle.Thin; cellStyle.TopBorderColor = IndexedColors.Aqua.Index; //セルの下に引く cellStyle.BorderBottom = NPOI.SS.UserModel.BorderStyle.Thin; cellStyle.BottomBorderColor = IndexedColors.Green.Index; //セルの左に引く cellStyle.BorderLeft = NPOI.SS.UserModel.BorderStyle.Thin; cellStyle.LeftBorderColor = IndexedColors.Black.Index; //セルの右に引く cellStyle.BorderRight = NPOI.SS.UserModel.BorderStyle.Thin; cellStyle.RightBorderColor = IndexedColors.LightBlue.Index; //セルの斜めに引く cellStyle.BorderDiagonal = NPOI.SS.UserModel.BorderDiagonal.Forward; cellStyle.BorderDiagonalLineStyle = NPOI.SS.UserModel.BorderStyle.Thin; cellStyle.BorderDiagonalColor = IndexedColors.Black.Index; //セルのスタイルを保存 cell.CellStyle = cellStyle; //ブックを保存 using (var fs = new FileStream(filepath, FileMode.Create)) { book.Write(fs); } var result = MessageBox.Show("処理しました。ファイルを開きますか?","確認",MessageBoxButtons.YesNo,MessageBoxIcon.Information); if (result == DialogResult.Yes) { Process.Start(filepath); } } 注意点 上記のコードで斜めを引くときに「.xlsx」だと斜めだけでなく、どこかの罫線を引いてあげないと罫線が引かれない 参考文書 ApachePoiのドキュメントを参照 https://poi.apache.org/components/spreadsheet/quick-guide.html#Borders
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DryIoc にInterceptor 機能を追加する

Interceptor(Aspect) DI によって注入されるオブジェクトの動作に「横断的関心」を組み込む。 主要なものにログ取得、トランザクション処理がある。 Aspect使わない場合 public int Foo(){ logger.LogTrace($"{nameof(Foo)} start"); DoSomething(); using(var transaction = context.Database.BeginTransaction()){ var data = context.Foos.Find(1); logger.LogDebug($"data id = {data.Id} name = {data.Name}") data.Name ="updated"; context.SaveChanges(); transaction.Commit(); logger.LogDebug($"{nameof(Foo)} commit") return 1; } } となって、特にLog出力は処理の本質には関係ない(=Log出力を削除しても処理結果は同じ)のに処理の可読性を下げてしまう。 Aspect使うと [TransactionMethod] public int Foo(){ DoSomething(); var data = context.Foos.Find(1); logger.LogDebug($"data id = {data.Id} name = {data.Name}") data.Name ="updated"; context.SaveChanges(); transaction.Commit(); return 1; } } で済み、デバッグ用出力以外の「本質に関係ない」コードを除去できる。 他にも、メソッドの前後に必ず実行する処理を挟み込んだり継承によらず処理を書き換えたりできるようになったりする。 DryIoc によるInterceptor Castle.DynamicProxy.IInterceptor の実装を作る。 // Interceptor 自体のコード public class TraceInterceptor : IInterceptor { ILogger logger = LogManager.GetCurrentClassLogger(); // 例えばNLogに出力 public void Intercept(IInvocation invocation) { logger.Trace($"{invocation.Method.Name} start"); invocation.Proceed(); logger.Trace($"{invocation.Method.Name} end"); } } Interceptor を適用できるようにする public static class DryIocInterception { public static void Intercept<TService, TInterceptor>(this IRegistrator registrator, object serviceKey = null) where TInterceptor : class, IInterceptor => registrator.Intercept<TInterceptor>(typeof(TService), serviceKey); public static void Intercept<TInterceptor>(this IRegistrator registrator, Type serviceType, object serviceKey = null) where TInterceptor : class, IInterceptor => registrator.Intercept(serviceType, Parameters.Of.Type(typeof(IInterceptor[]), typeof(TInterceptor[])), serviceKey); public static void Intercept(this IRegistrator registrator, Type serviceType, Type[] interceptors, object serviceKey = null) { registrator.Intercept( serviceType, Parameters.Of.Type( typeof(IInterceptor[]), request => { var list = new List<IInterceptor>(); foreach(var t in interceptors) { list.Add((IInterceptor)request.Container.Resolve(t)); } return list.ToArray(); }), serviceKey); } public static void Intercept(this IRegistrator registrator, Type serviceType, ParameterSelector interceptorsParameterSelector, object serviceKey = null) { Type proxyType; if (serviceType.IsInterface()) proxyType = ProxyBuilder.CreateInterfaceProxyTypeWithTargetInterface( serviceType, ArrayTools.Empty<Type>(), ProxyGenerationOptions.Default); else if (serviceType.IsClass()) proxyType = ProxyBuilder.CreateClassProxyTypeWithTarget( serviceType, ArrayTools.Empty<Type>(), ProxyGenerationOptions.Default); else throw new ArgumentException( $"Intercepted service type {serviceType} is not a supported, cause it is nor a class nor an interface"); registrator.Register(serviceType, proxyType, made: Made.Of( pt => pt.PublicConstructors().FindFirst(ctor => ctor.GetParameters().Length != 0), interceptorsParameterSelector), setup: Setup.DecoratorOf(useDecorateeReuse: true, decorateeServiceKey: serviceKey)); } private static DefaultProxyBuilder ProxyBuilder => _proxyBuilder ?? (_proxyBuilder = new DefaultProxyBuilder()); private static DefaultProxyBuilder _proxyBuilder; public static void Intercept<TInterceptor>(this IRegistrator registrator, Func<Type, bool> predicate) where TInterceptor : class, IInterceptor { foreach (var registration in registrator.GetServiceRegistrations().Where(r => predicate(r.ServiceType))) { registrator.Intercept<TInterceptor>(registration.ServiceType); } } } DIコンテナを構成する public static IServiceProvider CreateServiceProvider() { var services = new ServiceCollection(); services.AddLogging(loggingBuilder => { loggingBuilder.ClearProviders(); loggingBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); loggingBuilder.AddNLog(); }); var container = new DryIoc.Container(rules => rules.With()).WithDependencyInjectionAdapter(services); container.Register<TraceInterceptor>(reuse: Reuse.Transient); container.Register<TraceInterceptorAsync>(reuse: Reuse.Transient); container.Register<IService, Service>(reuse: Reuse.Scoped); container.Intercept(typeof(IService), new Type[] { typeof(TraceInterceptor), typeof(TransactionInterceptor) }); return container.BuildServiceProvider(); } 使う var provider = CreateServiceProvider(); using(var scope = provider.CreateScope()) { var service = scope.ServiceProvider.GetRequiredService<Services.IService>(); Console.WriteLine(service.DoSomething()); } その他 AutofacにもUnity Container にもInterceptor注入の仕組みがあるのだけど DryIoc はいろいろと自前でやる必要があったりする。 ここのサンプルでは1サービスに複数のInterceptor を登録できるようにしている。 オリジナルは https://github.com/dadhi/DryIoc/blob/master/test/DryIoc.IssuesTests/Interception/DryIocInterception.cs 追加したのは public static void Intercept(this IRegistrator registrator, Type serviceType, Type[] interceptors, object serviceKey = null) { registrator.Intercept( serviceType, Parameters.Of.Type( typeof(IInterceptor[]), request => { var list = new List<IInterceptor>(); foreach(var t in interceptors) { list.Add((IInterceptor)request.Container.Resolve(t)); } return list.ToArray(); }), serviceKey); } Parameters.Of.Type が注入する Interceptor を取り出せればよい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C#】リフレクションによる速度劣化の改善アプローチ

【C#】リフレクションによる速度劣化の改善アプローチ はじめに 実運用で使用しているアプリがリフレクションを頻繁に使用しているので、処理速度を改善出来る余地があるかどうかサンプルプログラムを作成して確認しました。 改善ポイントはループ内でリフレクションを行っていた点をループ外で行うようにしました。 環境 Window10 64bit .NET Framework 4.7.2 Windows アプリケーション サンプルプログラムの概要 計測①ボタンは1000万回繰り返すループ内でリフレクションによるクラスの型取得とインスタンス化を行い、インスタンス化したクラスのメソッドを呼び出します。 ループを抜けた後は処理時間を計測②ボタンの隣のラベルに出力します。 計測①ボタンのコードは下記の通り。 Form1.cs /// <summary> /// 計測①:ループ内でクラスの型取得とインスタンス化を行い、メソッドを実行する /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { var sw = new Stopwatch(); // 計測開始 sw.Start(); for (int i = 0; i < 10000000; i++) { // クラスの型取得 Type cType = Type.GetType("UI_Module.TestFunc"); if (cType != null) { // クラスのメソッド取得 MethodInfo m = cType.GetMethod("Func1"); if (m != null) { // インスタンス化 object obj = Activator.CreateInstance(cType); // メソッド実行 m.Invoke(obj, null); } } } // 計測終了 sw.Stop(); // 結果表示 var ts = sw.Elapsed; label_Msg.Text = string.Format( "計測①" + Environment.NewLine + "{0}秒{1}ミリ秒", ts.Seconds, ts.Milliseconds); } 計測②ボタンはループの外でクラスの型取得とインスタンス化を行い、ループ内ではインスタンス化したオブジェクトを使いまわしてメソッドを実行します。 計測②ボタンのコードは下記の通り。 Form1.cs /// <summary> /// 計測②:ループの外でクラスの型取得とインスタンス化を行い、ループ内ではメソッド実行のみ行う /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click(object sender, EventArgs e) { var sw = new Stopwatch(); // 計測開始 sw.Start(); // クラスの型取得 Type cType = Type.GetType("UI_Module.TestFunc"); MethodInfo m = null; object obj = null; if (cType != null) { // クラスのメソッド取得 m = cType.GetMethod("Func1"); if (m != null) { // インスタンス化 obj = Activator.CreateInstance(cType); } } for (int i = 0; i < 10000000; i++) { if (m != null && obj != null) { // メソッド実行 m.Invoke(obj, null); } } // 計測終了 sw.Stop(); // 結果表示 var ts = sw.Elapsed; label_Msg.Text = string.Format( "計測②" + Environment.NewLine + "{0}秒{1}ミリ秒", ts.Seconds, ts.Milliseconds); } } 結果 処理速度を比較した結果、計測②が速くなっていました。 計測① 56秒240ミリ 計測② 20秒694ミリ 改善率 63% サンプルプログラムは1クラス1メソッドの構成のため、劇的に速くなることはありませんでしたが、実運用で使用しているアプリの場合はクラスやメソッドの数が膨大であるため、さらに効果が上がるのではないかと感じました。 終わりに これまでにも処理速度改善のアプローチとしてループ内で行っている重い処理をループ外に移動することで処理速度の改善を図ってきた経験はありましたが、リフレクションにおいても実際にサンプルプログラムを作成してみて効果があることが実感できました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Prism とか コンテナを Microsoft.Extensions.DependencyInjection 化できないけど IServiceCollection 使いたいときに使うライブラリ DryIoc.Microsoft.DependencyInjection.Extension を作りました。

必要があったので DryIoc に IServiceCollection ベースで登録する為の DryIoc.Microsoft.DependencyInjection.Extension を作成しました。 DryIoc.Microsoft.DependencyInjection.Extension 1.0.0.2 できること このライブラリでできることは DryIoc で DryIoc.Microsoft.DependencyInjection 拡張を使えない環境でもできるかぎり IServiceCollection を登録できるようにするということです。 具体的に見ていきましょう。 Prism だと次の様に登録に使います。 using DryIoc.Microsoft.DependencyInjection.Extension // ... Registry.GetContainer().RegisterServices(v => { v.AddTransient<IA>(v => new A1()); }); // ... 使い方のアイディア的には Prism.Container.Extensions の Prism.Microsoft.DependencyInjection.Extensions と同じです。 ただし、こちらは Xamarin じゃなくても動きますし、そもそも Prism 自体をターゲットにしているわけでもないです。(というか Xamarin じゃないと動かないとあったのでこれを作ったので。 制限事項 現在わかっているのは DryIoc のコンテナを作成する際に WithConcreteTypeDynamicRegistrations しているとコンストラクタインジェクション時に登録していないオブジェクトを挿入してしまう為、想定されないコンストラクタを選択されてしまうということですね。例えば ILoggerFactory とか。 なので、登録する前あたりに 想定されたコンストラクタで呼び出す様に登録してやってください。 以上。 不具合等ありましたら。issues まで。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# HTML構造解析 AngleSharpの使い方(1)

はじめに 目的のURLからソースコードを取得できたあと、これを簡単に取り出す方法はないものかと 調べたところAngleSharpが現代的な方法と分かり説明していきます。 今回はAngleSharpのメソッドの説明よりはAngleSharpを使うとこんな処理ができる!ということを 説明する回になりますので詳しい使い方を知りたい方は『C# HTML構造解析 AngleSharpの使い方(2)』(執筆中)をご参照ください。 では実際に抜き出したHTMLを見てみましょう。 実際のHTML文字列 sample.html <!------------------------------------------------------ 途中抜粋---------------------------------------> <div class="history_container_base"> <div class="history_container"> <div class="head"> <span>プレイ履歴</span> <a href="/playdata_view/searchHistory?hbs=1" class="search"> <span class="search_icon"></span>検索 </a> </div> <div class="main"> <div class="lock_count"> <span class="lock_icon"></span> ロック 0/2 </div> <!--articles--> <div class="articles"> <!-- プレイ履歴を表示するループ --> <a class="article normal" href="/playdata_view/~~~"> <div class="date"> 2021-05-30 00:11:16 </div> <div class="mode"> プライベート戦三麻 </div> <div class="replay"> <span class="replay_icon"></span>リプレイ </div> <div class="body"> 1位<span class="">『dartslive』</span> (+49.9) 2位<span class="">『麻雀野郎』</span> (-9.6) 3位<span class="user">『めっつ』</span> (-40.3) </div> </a> <a class="article normal" href="/playdata_~~> <div class="date"> 2021-05-29 23:55:24 </div> <div class="mode"> プライベート戦三麻 </div> <div class="replay"> <span class="replay_icon"></span>リプレイ </div> <div class="body"> 1位<span class="user">『めっつ』</span> (+45.3) 2位<span class="">『ぱりぴ』</span> (+4.6) 3位<span class="">『dartslive』</span> (-49.9) </div> </a> <!---</div>がいくつも足りてないですが省略しています。-----> <!------------------------------------------------------ 以下略---------------------------------------> こちらはMJサイトのユーザープレイ履歴画面です。 余談ですが、MJではユーザーのプレイ履歴は確認できるのですが得点の集計は有料なのです。 以前はブラウザの画面から1つずつコピペかページを見ながら手入力していました(笑) 続いてC#の処理内容です。 ここからは具体的な処理の内容を説明します。 ・HTML文字列(string)を分解するためHtmlDocumentコレクションに分解。 ・HtmlDocumentコレクションからGetElementsByXXXメソッドを使って必要な要素(Node)を抜出す。  ※今回はさらにGetElementsByXXXメソッドを使ってさらに絞り込みを行っています。 ・ListにNodeのTextContentをループで抜き出し。  ※今はラムダ式を使ってもっとスマートな書き方あるかもしれません。 ・RegexやTrimを使って目的の箇所のみ取り出す。  ※今回はさらに名前と得点をセットとして一番左にuserが入り、   あとは名前順になるよう並び替えもおこなっています。 Regex(正規表現)は内容的にすべてググってコピペで事足りるので説明割愛。 最後にreturnで返される文字列が次のようになります。 2021-05-30 00:11:16,めっつ,-40.3,dartslive,49.9,麻雀野郎,-9.6 2021-05-29 23:55:24,めっつ,45.3,ぱりぴ,4.6,dartslive,-49.9 C#コンソールアプリの実装例(メソッド部全体) analizemethod.cs using AngleSharp.Html.Dom; using AngleSharp.Html.Parser; using System; using System.Linq; using System.Collections.Generic; using System.Text.RegularExpressions; private static string AnalizeHtml(int DataCount,string HTMLtext) {        //インスタンス作成 HtmlParser parser = new HtmlParser();        //HTMLの文字列を分解します。 IHtmlDocument doc = parser.ParseDocument(HTMLtext);        //HTML内の<div class="articles">抜き出したいところ</div>        //divは入れ子になっており同名のタグが大量にありますがインデントを目安にすると //抜き取りたいデータが上記タグ内にすべて収まっていることが分かります。 var Nodes = doc.GetElementsByClassName("articles"); //ここから分岐してdateNodes,bodyNodesにはそれぞれ違う絞り込みをかけます。        //今回のHTMLにarticleクラスは1つしか存在しないためNodesの先頭Nodes[0]を参照します。 //(取り出した要素が1つでもコレクションになるため必ず[index]が必要です) var dateNodes = Nodes[0].GetElementsByClassName("date"); var bodyNodes = Nodes[0].GetElementsByClassName("body");        //これはのちのuser(ログインしているユーザー名)を探しています。 string username = Nodes[0].GetElementsByClassName("user")[0].TextContent.Trim('『').Trim('』'); //dateNodesの中身抜出し(TextContentはstring型の戻り値なのでTrimが使える) var datelist = new List<string>(); foreach (var d in dateNodes) datelist.Add(d.TextContent.Trim());        //bodyNodesの中身抜出し&正規表現で『』や()の間の文字列を取り出す。 int Counter = 0; var bodylist = new List<string>(); foreach (var b in bodyNodes) { var Points = new Regex(@"\((.+?)\)").Matches(b.TextContent); var Names = new Regex(@"『(.+?)』").Matches(b.TextContent); var mylist = new List<string>(); //ソートディクショナリを宣言して名前順(Key順)にソート var dic = new SortedDictionary<string, string>(); for (int p = 0; p < Names.Count; p++) { var Name = Names[p].Value.Trim('『').Trim('』'); var Point = Points[p].Value.Trim('(').Trim(')'); Point = Point.Trim('+'); dic.Add(Name, Point); } //先頭にユーザーネームとデータを挿入。 mylist.Add(username + "," + dic[username]); //ユーザー以外は名前順で並び替えて挿入。 foreach (var t in dic) { if (t.Key != username) mylist.Add(t.Key + "," + t.Value); } //並び替え後のリスト作成 bodylist.Add(datelist[Counter] + "," + string.Join(",", mylist)); Counter++; //Counterが指定のデータ数に到達したらループを抜ける if (DataCount < Counter) break; } return string.Join("," + "\n", bodylist); } 引数のint DataCountはプレイ履歴のうち、上のような成績(一行)を何行とりだすか を決める数でメソッドを呼び出すときに任意で決めます。例えばint DataCount = 50 と しておけば上のような成績が50行取り出せます。 細かい説明は次回以降で見ていきたいと思います。 つづく 開発環境 言語:C# 使用ソフト:VisualStudio2019v16.6.0 プロジェクト:コンソールアプリ(.NET Framework) .NET Framework Version 4.8.04084 NuGetパッケージ @AngleSharp v0.15.0 参照URL SEGA NET麻雀MJ ログイン https://pl.sega-mj.com/players/login SEGA NET麻雀MJ プレイ履歴 https://pl.sega-mj.com/playdata_view/showHistory
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む