FC2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム »C#
カテゴリー「C#」の記事一覧

【C#】クラスのフィールド名を文字列の配列で取得する  


 ただのメモ。クラスや構造体をデータのやり取りに使う際、フィールド名を一覧で取得したいときがある。JSONスキーマ的なものとかね。そんな時に役立つ簡単なサンプルコード。

●フィールド名を取得するクラス例
using System.Linq;
using System.Reflection;

public class Sample //※名前は任意(typeof で指定する)
{
//取得するフィールド
public const string c_value = "const value";
public static readonly string s_value = "static value";
public string pub_value = "public value";
private string pri_value = "private value";

//フィールド名を string[] で返す
public string[] GetFieldNames()
{
var bindingFlags = BindingFlags.Public //┐どちらか一方は指定する
| BindingFlags.NonPublic //┘
| BindingFlags.Instance //┐どちらか一方は指定する
| BindingFlags.Static //┘
| BindingFlags.DeclaredOnly; //このクラスのみ(継承などは含めない)

var t = typeof(Sample); //取得するクラス名
var fields = t.GetFields(bindingFlags);
return fields.Select(e => e.Name).ToArray();
}
}

●メインコード例
using System;

var sample = new Sample();
var names = sample.GetFieldNames();
Console.WriteLine($"Length = {names.Length}");
foreach (var item in names)
{
Console.WriteLine(item);
}

Length = 4
c_value
s_value
pub_value
pri_value

 BindingFlags の指定で、public や private, インスタンスや static などを選択することができる(少なくともこれらの属性は指定した方が良い)。また継承してるクラスがある場合、BindingFlags.DeclaredOnly でスーパークラスのフィールドを除外することもできる。

 ここではフィールド名が欲しかったので Type.GetFields() を使っているが、メソッドやプロパティ名を取得したいなら、Type.GetMembers() を同様に使えば良い。

 リフレクション(Reflection)関連は使いこなせれば色々と便利だが、ランタイム時の負荷はそれなりにかかるそうで、実行速度を優先するなら、先に文字列化したものをデータ化しておくなど、工夫した方が良いだろう。使い所は適切に(笑)。

BindingFlags 列挙型
Type.GetFields()
Type.GetMembers()
リフレクション





(関連記事)
【C#】【Unity】enum 型と string, int 型の相互変換など


スポンサーサイト



category: C#

thread: プログラミング

janre: コンピュータ

tag: C#リファレンス 
tb: 0   cm: --

【C#】GUID の生成と書式  


 GUID の生成はググればすぐに出てくるのだが、生成されたオブジェクトは Guid 型なので、普段使うには文字型に変換して使うことが多い。

Guid.NewGuid メソッド

 幸いなことに文字列変換の Guid.ToString() には、いくつかのオーバーロードが定義されているので、利用方法は簡単だ。ただ「あれ?何を指定するんだっけ…?」とよく調べるので、メモ代わりに具体例を書いておくことにした(解説はマニュアル参照)。

Guid.ToString(String)

●テストコード
using System;

Console.WriteLine("(): " + Guid.NewGuid().ToString());
Console.WriteLine("N : " + Guid.NewGuid().ToString("N"));
Console.WriteLine("D : " + Guid.NewGuid().ToString("D"));
Console.WriteLine("B : " + Guid.NewGuid().ToString("B"));
Console.WriteLine("P : " + Guid.NewGuid().ToString("P"));
Console.WriteLine("X : " + Guid.NewGuid().ToString("X"));

(): 9379a57a-eb51-431e-bf78-e2bdb96bf249
N : b11b80a4ebd54e19916d3de68cf6e548
D : f5f54a7d-b46b-4d99-a70c-dff3216f1766
B : {cc3e87ff-83a5-4eac-b465-e4b967173db7}
P : (8cc1037b-db1b-4bb0-969a-6af901719548)
X : {0xe49cb895,0xe333,0x4704,{0x81,0x25,0xa2,0x70,0x79,0x4e,0xf6,0xae}}

 GUID はハッシュ値と違い、毎回変わるので、上の例はあくまでもその実行時の結果なので注意。

 "()":引数無しと "D" は同じようだ。普段使うのは引数無し(or "D") か "N" (ハイフン無し)を使うことが多いだろう。

 他は括弧囲み "()" だったり、中括弧 "{}" だったり、16進 "0x" だったりとなるだけだ。

 GUID はセッションID や識別ID などによく使われる。内容的には以下の記事にあるように『GUID値とは、世界で重複することがない、ユニーク(=唯一)な128bit(16byte)のランダムな数値である。GUID値は必ずしも「絶対に同じID値が生成されないこと」を保証するものではないが、その数値の範囲が2の128乗、つまり「およそ3.4×(10の38乗)」(=340億の100兆倍の100兆倍)もあるので、現実的に同じIDが生成される可能性はきわめて低い。』というものだ。例えば、ブラウザやアプリなどで初めに生成→保存し、利用者を識別したりするのにも使われる(広告とか)。簡易的なユニークID として利用するにはもってこいだろう。

.NET TIPS GUID値を生成するには?





(関連記事)
【C#】MD5, SHA1, SHA256, SHA384, SHA512 等のハッシュ値を生成する
【Unity】【C#】文字列の暗号化・復号化を行う


category: C#

thread: プログラミング

janre: コンピュータ

tag: C#リファレンス 
tb: 0   cm: --

【C#】MD5, SHA1, SHA256, SHA384, SHA512 等のハッシュ値を生成する  


 簡易的な暗号化として、ハッシュを簡単に生成できる拡張メソッドを定義しておこう。

 元々 .NET には色々はハッシュアルゴリズムが System.Security.Cryptography に定義されているのだが、毎回アルゴリズムを選択して書くのは面倒だったりするので、それを省くための拡張メソッドだ。

System.Security.Cryptography 名前空間
HashAlgorithm.ComputeHash メソッド

 今回紹介する MD5, SHA1, SHA256, SHA384, SHA512 はただ単に生成される文字列の長さを選択したかっただけなので、必要なら色々追加するのも良いかも知れない。実際には、以下の参考記事にもあるが、暗号化として使うには「256ビット以上のハッシュ関数を選択することが望ましい」ともあるので、使い所は選んだ方が良いだろう。

MD5やSHA1などでハッシュ値を計算する

●ハッシュアルゴリズムを使ってハッシュ値を生成する(拡張メソッド)
using System;
using System.Text;
using System.Security.Cryptography;

public static class HashExtensions //※名前は任意
{
//ハッシュアルゴリズムを使ってハッシュ値を生成する
public static string GenerateHash(this string text, HashAlgorithm hashAlgorithm)
{
if (string.IsNullOrEmpty(text) || hashAlgorithm == null)
return null;

var bytes = Encoding.UTF8.GetBytes(text);
var hash = hashAlgorithm.ComputeHash(bytes);
hashAlgorithm.Clear();
return BitConverter.ToString(hash).ToLower().Replace("-", ""); //小文字にして、'-' をカット(元は XX-XX-…)
}

//MD5 でハッシュ値を生成(32文字)
public static string GenerateHashMD5(this string text)
{
return GenerateHash(text, MD5.Create());
}

//SHA1 でハッシュ値を生成(40文字)
public static string GenerateHashSHA1(this string text)
{
return GenerateHash(text, SHA1.Create());
}

//SHA256 でハッシュ値を生成(64文字)
public static string GenerateHashSHA256(this string text)
{
return GenerateHash(text, SHA256.Create());
}

//SHA384 でハッシュ値を生成(96文字)
public static string GenerateHashSHA384(this string text)
{
return GenerateHash(text, SHA384.Create());
}

//SHA512 でハッシュ値を生成(128文字)
public static string GenerateHashSHA512(this string text)
{
return GenerateHash(text, SHA512.Create());
}
}

●テストコード
string text = "hogehoge";
Console.WriteLine("MD5 : " + text.GenerateHashMD5());
Console.WriteLine("SHA1 : " + text.GenerateHashSHA1());
Console.WriteLine("SHA256 : " + text.GenerateHashSHA256());
Console.WriteLine("SHA384 : " + text.GenerateHashSHA384());
Console.WriteLine("SHA512 : " + text.GenerateHashSHA512());

MD5 : 329435e5e66be809a656af105f42401e  (32文字)
SHA1 : 3b2c6c10d0e78072d14e02cc4c587814d0f10f3a  (40文字)
SHA256 : 4c716d4cf211c7b7d2f3233c941771ad0507ea5bacf93b492766aa41ae9f720d  (64文字)
SHA384 : 09514da2edaf9e4d083a14136dab995897207f8fb24f362fedc422889f313064cd4cad65e08384770917f809928d3bb2  (96文字)
SHA512 : 2b7e36b16f8a849ef312f9ef5ff9b3f4281a8681d0657150899f1113a0eecfdbb4491da763159055b55e122e85281415b11897d268e124f9ef2b40457a63a465  (128文字)

 あくまでもサクッと使いたい時用に。MD5 はパリティ用などによく使われるね。ハッシュは一方向関数なので、パスワードの暗号化にも使われる(元に戻せないので、次回入力したものをハッシュ化して比較する。復号化したいものには使えないが、逆算は難しいメリットがある → 復号化したいときはこっち)。実際の暗号化には DESAES (Rijndael) などがよく使われるけどね。

【Unity】【C#】文字列の暗号化・復号化を行う

 暗号化としては SHA512 の方がもちろん強固だろうけど、文字列はとても長くなるので、利用時にはその辺りがネックになるかな(例えば、データベースで長さ制限のある文字列型を使ってる場合は注意)。

 とは言え、「Google が量子コンピュータを完成させた」みたいな話題もあったから、いつか暗号化アルゴリズムそのものを考え直す時代が来るかもね…(笑)。

Googleが1万年かかる計算問題を3分20秒で解き終える量子コンピューターを完成させる





(関連記事)
【C#】GUID の生成と書式
【Unity】【C#】文字列の暗号化・復号化を行う


category: C#

thread: プログラミング

janre: コンピュータ

tag: C#ライブラリ  暗号化 
tb: 0   cm: --

【C#】二分探索の実装(範囲インデクス指定実装)  


 ただの実装用メモ。元々標準ライブラリで BinarySearch は実装されているので(配列も同様)、利用するだけなら、自分で実装する必要はない。ちょっと改造して、検索を高速化したいときにアルゴリズムだけ利用することもあるので(Java の標準ライブラリはすぐ実装見れるが、C# の標準ライブラリってメタデータだけだったりするので)、確認用に書いておくだけ(笑)。

●インデクスで範囲指定の二分探索の実装
using System;
using System.Collections.Generic;

/// <summary>
/// インデクスで範囲指定の二分探索
/// </summary>
/// <typeparam name="T">比較する値の型</typeparam>
/// <param name="arr">対象の配列(※ソート済みであること)</param>
/// <param name="start">開始インデクス [inclusive]</param>
/// <param name="end">終了インデクス [exclusive]</param>
/// <param name="value">検索する値</param>
/// <param name="comparer">比較関数(インターフェイス)</param>
/// <returns>0 以上のとき見つかったインデクス / 0 より小さいとき ~index (ビット反転)で挿入位置になる</returns>
public static int BinarySearch<T>(this List<T> list, int start, int end, T value, IComparer<T> comparer)
{
int low = start;
int high = end - 1;
int mid;
while (low <= high)
{
mid = ((high - low) >> 1) + low;
if (comparer.Compare(list[mid], value) < 0)
{
low = mid + 1;
}
else if (comparer.Compare(list[mid], value) > 0)
{
high = mid - 1;
}
else
{
return mid;
}
}
return -(low + 1);
}

//引数省略のオーバーロード
public static int BinarySearch<T>(this List<T> list, int start, int end, T value)
{
return BinarySearch(list, start, end, value, Comparer<T>.Default);
}

 実際には以前に書いた、LowerBound, UpperBound(配列で実装してるが、List でも同じ)と BinarySearch を使い分けると便利だったりする。大きな違いは検索結果のインデクスの値で、例えば BinarySearch では同値が並んでいるとどのインデクスになるかは不定だが、LowerBound を使うと同値の最初のインデクス、UpperBound を使うと同値の最後の次のインデクスとなる(「検索する値の個数を求める」の例がわかり易いと思う)。

 私の場合、一部のプロパティを二分探索で検索するために(例えば常にインクリメントする ID 番号など)、上記の実装を少し改造して使うことも多い。大量にデータが増えたとき、二分探索はその効果を発揮する(よく FindIndexFirstOrDefault で実装されている場合も多いが、逐次検索なので大量のデータには向かない)。LowerBound, UpperBound と共に使えば、かなりの高速化ができるので、色々やってみると良いだろう。





(関連記事)
【C#】 LowerBound, UpperBound (二分探索)
【Ruby】二分探索, lower_bound, upper_bound
【Java】lower_bound(), upper_bound() 関数を作る
【Java】二分探索を汎用的に使えるようにする


category: C#

thread: プログラミング

janre: コンピュータ

tag: C#ライブラリ  二分探索 
tb: 0   cm: --

【C#】【Unity】enum 型と string, int 型の相互変換など  


 enum 型をファイルに保存したり、UIなどのインデクスに対応させたりと、相互変換するとき確認したいことが多くあるので、備忘録的にまとめておいた。今回は通常の C# として書いているが、Unity などでも使用することは多いので、ちょっとしたトラブルシューティングなどもついでに。どちらかというとリファレンス的な使い方になると思うので、解説は少なめに凡例を列挙しておこう。


(※) mono-4.2.1 (C#6, CLI4.5)[paiza.io] / Unity 5.6.3p1 - 2018.1.5f1 / Windows10(x64) で確認



■enum → string 変換
using System;

enum BloodType //※enum 型は任意
{
A, B, O, AB
}

//enum → string 変換
BloodType bloodType = BloodType.A;
string str = bloodType.ToString();

Console.WriteLine(str);

A

 または以下のようにも書ける。
string str = Enum.GetName(typeof(BloodType), bloodType);

Console.WriteLine(str);

A




■string → enum 変換
using System;

enum BloodType //※enum 型は任意
{
A, B, O, AB
}

//string → enum 変換
string str = "B"; //"b" でも可
BloodType bloodType = (BloodType)Enum.Parse(typeof(BloodType), str, true); //true = IgnoreCase

Console.WriteLine(bloodType);

B

 ちょっと試しに IgnoreCase のオプションを外して小文字にしてみると、System.ArgumentException が出る。
string str = "b";
//↓このコードはエラーが出ます。
BloodType bloodType = (BloodType)Enum.Parse(typeof(BloodType), str); //System.ArgumentException

Console.WriteLine(bloodType);

System.ArgumentException




■enum → int 変換
using System;

enum BloodType //※enum 型は任意
{
A, B, O, AB
}

//enum → int 変換
BloodType bloodType = BloodType.AB;
int i = (int)bloodType;

Console.WriteLine(i);

3




■int → enum 変換
using System;

enum BloodType //※enum 型は任意
{
A, B, O, AB
}

//int → enum 変換
int i = 3;
BloodType bloodType = (BloodType)Enum.ToObject(typeof(BloodType), i);

Console.WriteLine(bloodType);

AB

 試しに範囲外の値を入れてみると、そのまま整数値が出てくる。
//int → enum 変換
int i = 4; //範囲外の値
BloodType bloodType = (BloodType)Enum.ToObject(typeof(BloodType), i);

Console.WriteLine(bloodType);

4




■enum の名前の配列を取得
using System;

enum BloodType //※enum 型は任意
{
A, B, O, AB
}

string[] names = Enum.GetNames(typeof(BloodType));

Console.WriteLine(string.Join(", ", names));

A, B, O, AB




■enum の値(enum)の配列オブジェクト(Array)を取得
using System;

enum BloodType //※enum 型は任意
{
A, B, O, AB
}

Array values = Enum.GetValues(typeof(BloodType));

foreach (var item in values) {
Console.WriteLine(item + " = " + (int)item);
}

A = 0
B = 1
O = 2
AB = 3




■enum を long 型で使う
using System;

enum LongRange : long //※enum 型は任意
{
Min = long.MinValue,
Num = 123456789L,
Max = long.MaxValue,
}

long min = (long)LongRange.Min; //キャストが必要
long num = (long)LongRange.Num; //キャストが必要
long max = (long)LongRange.Max; //キャストが必要

Console.WriteLine(min);
Console.WriteLine(num);
Console.WriteLine(max);

-9223372036854775808
123456789
9223372036854775807




■enum の値割当と Unity インスペクタのずれを直す(Unity トラブルシューティング)

 例えば以下のような列挙型があったとき、途中から "Banana" を削除したくなったとしよう。しかし、コメントアウトで消したとするとそれぞれに割り当てられれた値がずれ、インスペクタでの表示(名前)も変わってしまう。

public enum Hoge  //※enum 型は任意
{
Apple,
//Banana,
Cat,
Dog,
}
public Hoge hoge;

 これは、コメントアウト前は {Apple = 0, Banana = 1, Cat = 2, Dog = 3} であったのに対し、コメントアウト後は {Apple = 0, Cat = 1, Dog = 2} になるために出る症状なので、以下のように値を直接割り当てれば、元の並びと同じになる。

public enum Hoge  //※enum 型は任意
{
Apple,
//Banana,
Cat = 2,
Dog,
}
public Hoge hoge;

 また列挙型はデフォルトでは0からの整数連番だが、値は自由に割り当てられるので、飛び番号や負の値なども使える。色々やってみると良いだろう。



■enum の PlayerPrefs 保存と読み込み(Unity)

 Unity でユーザーデータとして enum 型の保存と読み込みをやってみよう。といっても内容的には先に述べた「enum←→stringの相互変換」をやっているだけである。ここでは Load と Save の機能を持ったメソッドを2つ作ったとする。シーンに UI-Button を2つ置いて、それぞれの OnClick() で呼び出すなどすれば、簡単に確認できるだろう。

using System;
using UnityEngine;

public enum Hoge { //※enum 型は任意
Apple,
Banana,
Cat,
Dog,
}
public Hoge hoge = Hoge.Apple;

string prefsName = "hoge";

public void LoadHoge() //UI-Button の OnClick に登録する等
{
string str = PlayerPrefs.GetString(prefsName, hoge.ToString());
hoge = (Hoge)Enum.Parse(typeof(Hoge), str);

Debug.Log("Loaded : " + hoge);
}

public void SaveHoge() //UI-Button の OnClick に登録する等
{
PlayerPrefs.SetString(prefsName, hoge.ToString());
PlayerPrefs.Save();

Debug.Log("Saved : " + hoge);
}

(インスペクタで「Banana」を選んで保存・読み込みをした場合)
Saved : Banana
Loaded : Banana

 ちなみに int 型で保存と読み込みをする場合には以下のように書ける。

・・・(略)・・・
public void LoadHoge()
{
int i = PlayerPrefs.GetInt(prefsName, (int)hoge);
hoge = (Hoge)Enum.ToObject(typeof(Hoge), i);

Debug.Log("Loaded : " + hoge + " (" + i + ")");
}

public void SaveHoge()
{
PlayerPrefs.SetInt(prefsName, (int)hoge);
PlayerPrefs.Save();

Debug.Log("Saved : " + hoge + " (" + (int)hoge + ")");
}
・・・(略)・・・

(インスペクタで「Banana」を選んで保存・読み込みをした場合)
Saved : Banana (1)
Loaded : Banana (1)

 stringint のどちらでも良いが、後から値の割当を変えたくなったときにも対応できるように、string 型で保存しておいた方が無難ではあるだろう(※public で宣言してる場合、後から変更するとインスペクタでの名前表示のずれが発生するので注意)。





(関連記事)
【Java】enum を int で取得する
【Java】文字列を enum 型に変換する
【Unity】色形式:Unity の Color と Android の ARGB(int32) の相互変換をする


category: C#

thread: プログラミング

janre: コンピュータ

tag: C#リファレンス  Unityトラブルシューティング 
tb: 0   cm: --


プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR