便利な小ネタ集（C#.NET編）

# はじめに
ソフト作るときに地味にストレスなのが
 - この程度の機能の実装に時間をかけたくない
 - メモっていたけどどこに置いたか忘れた（探すのめんど）

これらを解消したいので何度も使いそうなプログラム部品を記録しておきます。
C#.NET編です。

# 小さな部品たち
それでは早速便利な部品たちを載せていきます。

## ファイルアクセス関係
```diff_c_sharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using System.Text.Json;
using System.Text.Unicode;

public static class clsFileIO
{
    //ファイル選択
    public static (FileInfo?, Exception?) SelectFile(string filter = "")
    {
        try
        {
            using(var ofd = new OpenFileDialog())
            {
                ofd.Filter = filter;
                return ofd.ShowDialog() switch
                {
                    DialogResult.OK => (new FileInfo(ofd.FileName), null),
                    _ => (null, new OperationCanceledException()) // OperationCanceledException のインスタンスを作るがスローせず戻り値にする
                }
            }
        }
        catch(Exception ex)
        {
            return (null, ex);
        }
    }
    
    //ファイル選択ダイアログでフォルダを選択させる
    public static (FileInfo?, Exception?) SelectFolder()
    {
        try
        {
            using (var ofd = new OpenFileDialog() { FileName = "SelectFolder", Filter = "Folder|.", CheckFileExists = false })
            {
                return ofd.ShowDialog() switch
                {
                    DialogResult.OK => (new FileInfo(Path.GetDirectoryName(ofd.FileName)), null),
                    _ => (null, new OperationCanceledException()) // OperationCanceledException のインスタンスを作るがスローせず戻り値にする
                }
            }
        }
        catch(Exception ex)
        {
            return (null, ex);
        }
    }

    //xmlファイルに保存
    public static Exception? WriteXml<T>(string filePath, T obj)
    {
        try
        {
            using (var writer = XmlWriter.Create(filePath))
            {
                var serializer = new XmlSerializer(obj.GetType());
                serializer.Serialize(writer, obj);
            }
            return null;
        }
        catch (Exception ex)
        {
            return ex;
        }
    }

    //xmlファイル読み込み
    public static Exception? ReadXml<T>(string filePath, out T obj)
    {
        obj = default(T);
        try
        {
            using (var reader = XmlReader.Create(filePath))
            {
                var serializer = new XmlSerializer(typeof(T));
                obj = (T)(serializer.Deserialize(reader));
            }
            return null;
        }
        catch (Exception ex)
        {
            return ex;
        }
    }
        
    /// <summary>
    /// クラスをJsonファイルに書き込む
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="file_path"></param>
    /// <param name="write_data"></param>
    /// <param name="encoding">指定しない場合は、System.Text.Encoding.UTF8</param>
    /// <param name="options"></param>
    /// <returns></returns>
    public static Exception? WriteJson<T>(
        string file_path, 
        T write_data, 
        System.Text.Encoding encoding = null,
        JsonSerializerOptions options = null)
    {
        try
        {
            if(encoding == null)
            {
                encoding = System.Text.Encoding.UTF8;
            }

            using(var fs = new FileStream(file_path, FileMode.CreateNew))
            {
                using(var writer =new StreamWriter(fs, encoding))
                {
                    var str = JsonSerializer.Serialize<T>(write_data, options);
                    writer.Write(str);
                }
            }

            return null;
        }
        catch (Exception ex)
        {
            return ex;
        }
    }

    /// <summary>
    /// Jsonファイルからクラスを読み込む
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="file_path"></param>
    /// <param name="encoding">指定しない場合は、System.Text.Encoding.UTF8</param>
    /// <returns></returns>
    public static (T?, Exception?) ReadJson<T>(string file_path, System.Text.Encoding encoding=null)
    {
        try
        {
            if(encoding == null)
            {
                encoding = System.Text.Encoding.UTF8;
            }

            using (FileStream fs = new FileStream(file_path, FileMode.Open))
            {
                using (StreamReader reader = new StreamReader(fs, encoding))
                {
                    return (JsonSerializer.Deserialize<T>(reader.ReadToEnd()), null);
                }
            }
        }
        catch (Exception ex)
        {
            return (default(T), ex);
        }
    }
}
```

## あるクラスのプロパティを一気にダンプする
```c#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;

public static class Dump
{
    public static bool DumpProperties<T>(
        T obj, 
        [CallerMemberName] string callerMethodName = "",
        [CallerFilePath] string callerFilePath = "")
    {
        try
        {
            print($"==Dump {obj.GetType().ToString()}'s properties.== {callerMethodName} in {callerFilePath}");
            foreach (var prop in obj.GetType().GetProperties())
            {
                print($"{prop.Name}: {prop.GetValue(obj)} {callerMethodName} in {callerFilePath}");
            }
            return true;
        }
        catch(Exception ex)
        {
            print($"Dump.DumpProperties exception! {ex.ToString()}");
            try
            {

            }
            catch(Exception e)
            {
                
            }
            return false;
        }
    }
}
```

## 画面上にログを表示する。（Windows Form）
まずは表示用の```ListView```を作る
（たしか大事なプロパティは```this.lsvLog.View```くらいで、```column```を追加しておくのが肝だったはず、、）
```C#
//
// lsvLog
//
this.lsvLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.lsvLog.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.columnHeader1});
this.lsvLog.GridLines = true;
this.lsvLog.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None;
this.lsvLog.HideSelection = false;
this.lsvLog.Location = new System.Drawing.Point(25, 723);
this.lsvLog.Margin = new System.Windows.Forms.Padding(25, 3, 25, 3);
this.lsvLog.Name = "lsvLog";
this.lsvLog.ShowItemToolTips = true;
this.lsvLog.Size = new System.Drawing.Size(734, 186);
this.lsvLog.TabIndex = 4;
this.lsvLog.UseCompatibleStateImageBehavior = false;
this.lsvLog.View = System.Windows.Forms.View.Details;
```
　

ログを```ListView```に追加していくための関数。
メッセージと表示色を引数に渡す。
```C#
private int MAX_LOG_ROWS = 1000;

private void DispLog(string message, Color msgColor)
{
    if (InvokeRequired)
    {
        BeginInvoke(new Action<string, Color>(DispLog), message, msgColor);
        return;
    }
    
    var item = new ListViewItem(message);
    item.ForeColor = msgColor;
    lsvLog.Items.Add(item);
    if (lsvLog.Items.Count > MAX_LOG_ROWS)
    {
        lsvLog.Items.RemoveAt(0);
    }
    lsvLog.Items[lsvLog.Items.Count - 1].EnsureVisible();
}
```

## ワイルドカードで文字操作
他の言語であれば割とワイルドカードが気軽に使えるらしいが、C#では一筋縄ではいかない
（あーやだやだ）

正規表現を使うのでusingしておく
```C#
using System.Text.RegularExpressions;
```
### 文字検索
一般的なワイルドカード記号```*```を```.*?```に置き換えて、
```Regex.IsMatch()```を実行すると結果が返ってくる
- 検索したい文字列が含まれている場合は```true```
- 検索したい文字列が含まれていない場合は```false```

```C#
private const string SEARCH_STRING = "あああ.*?いいい";

private void btnDebug_Click(object sender, EventArgs e)
{
    var receiveLinesArray = new string[]
    {
        "aaaaaaa",
        "bbbb",
        "あああｊぎろｈぎおえｒｈｇｈｒいいい",
        "ああｆｈすえｈぐいくぇｒふいいい",
    };

    if (!receiveLinesArray.Any(line => Regex.IsMatch(line, SEARCH_STRING)))  
    {
        MessageBox.Show("ありません。");
    }
    else
    {
        MessageBox.Show("あります。");
    }
}
```
## PCのEthernetポートのIPアドレスを取得する
```C#
NetworkInterface[] nis = NetworkInterface.GetAllNetworkInterfaces();
foreach(var ni in nis)
{
    if (ni.NetworkInterfaceType != NetworkInterfaceType.Ethernet) continue; //Ethernetで絞り込み
    if (ni.OperationalStatus != OperationalStatus.Up) continue; //接続できるかどうかで絞り込み
    if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue; //ループバックは除く
    if (ni.NetworkInterfaceType == NetworkInterfaceType.Tunnel) continue; //トンネルは除く
    UnicastIPAddressInformationCollection unicastIPs = ni.GetIPProperties().UnicastAddresses; //インターフェースのIPを取得
    foreach (var ip in unicastIPs)
    {
        // PCのIPv4を取得
        if (ip.Address.AddressFamily != AddressFamily.InterNetwork) continue;
        Logging.Info(Convert.ToString(ip.Address));
    }
}
```
# 過去コード
<details><summary>コードはこちら</summary><div>

:::note
皆様のアドバイスのおかげで進化を遂げたコード達の
進化前の姿を供養する場所とします。
:::
## ファイルアクセス
```diff_c_sharp
public static class clsFileIO
{

-    private static StringBuilder _ErrorBuff = new StringBuilder();
-    public static string Error
-    {
-        get
-        {
-            return _ErrorBuff.ToString();
-        }
-        private set
-        {
-            _ErrorBuff.Clear();
-            _ErrorBuff.Append(value);
-        }
-    }
+    private static string Error {get; set;};

    //ファイル選択
    public static bool SelectFile(out string fileName, string filter = "")
    {
        fileName = "";
        try
        {
            using(var ofd = new OpenFileDialog())
            {
                ofd.Filter = filter;
                if(ofd.ShowDialog()==DialogResult.OK)
                {
                    fileName = ofd.FileName;
                }
                else
                {
                    Error = "Canceled..";
                    return false;
                }
            }
    
            return true;
        }
        catch(Exception ex)
        {
            Error = $"clsFileIO.SelectFile exception! {ex.ToString()}";
            return false;
        }
    }
    
    //ファイル選択ダイアログでフォルダを選択させる
    public static bool SelectFolder(out string folderPath)
    {
        folderPath = "";
        try
        {
            using (var ofd = new OpenFileDialog() { FileName = "SelectFolder", Filter = "Folder|.", CheckFileExists = false })
            {
                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    folderPath = Path.GetDirectoryName(ofd.FileName);
                }
                else
                {
                    Error = "Canceled..";
                    return false;
                }
            }
    
            return true;
        }
        catch(Exception ex)
        {
            Error = $"clsFileIO.SelectFolder exception! {ex.ToString()}";
            return false;
        }
    }

    //xmlファイルに保存
    public static bool WriteXml<T>(string filePath, T obj)
    {
        try
        {
            using (var writer = XmlWriter.Create(filePath))
            {
                var serializer = new XmlSerializer(obj.GetType());
                serializer.Serialize(writer, obj);
            }
            return true;
        }
        catch(Exception ex)
        {
            Error = $"clsFileIO.WriteXml exception! {ex.ToString()}";
            return false;
        }
    }
    
    //xmlファイル読み込み
    public static bool ReadXml<T>(string filePath, out T obj)
    {
        obj = default(T);
        try
        {
            using (var reader = XmlReader.Create(filePath))
            {
                var serializer = new XmlSerializer(typeof(T));
                obj = (T)(serializer.Deserialize(reader));
            }
            return true;
        }
        catch (Exception ex)
        {
            Error = $"clsFileIO.ReadXml exception! {ex.ToString()}";
            return false;
        }
    }
}
```
</div></details>


# 最後に
まだまだ少ないけど、どんどん追加していきます。

どこかで誰かの役に立ってくれればいいな。
（主に将来の自分）

zennでも活動してますので、応援よろしくお願いします。

https://zenn.dev/hiyokotti