C# で Type.GetType(string)の戻り値がnullになる場合の対処方法

C# で Type.GetType(string)の戻り値がnullになる場合の対処方法

例えば以下のようにType.GetTypeを使用していても何故かnullが返ってきてしまうときがある。
もちろんクラスが存在しないから、などという落ちではない。
そのような状況に陥ったときの解決方法について解説する。

Type objType = obj.GetType();
string typeFullName = objType.FullName;
// 蛇足だが、AssemblyQualifiedNameとは
// アセンブリの名前を含む型のアセンブリ修飾名である。
string assemblyQualifiedName = objType.AssemblyQualifiedName;


// 何故か以下の2つの文はどちらもnullが返ってくる。
Type type1 = Type.GetType(typeFullName);
Type type2 = Type.GetType(assemblyQualifiedName)
前提環境
  • Windows 10
  • Visual Studio Professional 2019
  • .NET Framework 4.2
  • C#

コードのポイント

以下のようなメソッドを作成し、参照済みのAssamblyから一致したものを取得すればよい。
上記がnullで、これだと大丈夫なのは謎だが、うん、よくわからん。

public static Type GetTypeByAssemblyQualifiedName(string assemblyQualifiedName)
{
    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
    {
        foreach (Type type in assembly.GetTypes())
        {
            if (type.AssemblyQualifiedName == assemblyQualifiedName)
            {
                return type;
            }
        }
    }
    return null;
}

ASP.net Webフォームで、コントローラのHTMLを動的に生成、追加し、イベントハンドラを紐付ける方法

概要

ASP.net Webフォームで、コントローラのHTMLを動的に生成、追加し、イベントハンドラを紐付ける(正確には紐付いてはいませんが。。。)方法について説明します。
今回はbuttonコントローラ(buttonタグ)を動的に追加する方法を例として紹介します。

前提環境
  • Windows 10
  • Visual Studio Professional 2019
  • .NET Framework 4.2
  • C#

コードのポイント

  • 「Page.Form.Controls.Add(button);」で動的にコントローラを追加できるのですが、この方法では任意の場所や、動的に作成しているHTML内に設定することができません。
  • コントロールのHTML化(HTMLの生成)は、RenderControlメソッドを使用しますが、StringWriterを使うのがポイントです。
  • 動的に生成し、かつControls.Addを使用しない場合、イベントハンドラと紐付けられません。そのため、どのボタンが押下されたのかをRequest[“__EVENTTARGET”]を使って判定して、イベント用のメソッドを呼び出します。

サンプルコード

protected void Page_Load(object sender, EventArgs e)
{
    // 出力用のWriterの準備。StringWriterを用意するのがポイント。
    StringBuilder builder = new StringBuilder();
    StringWriter writer = new StringWriter(builder);
    HtmlTextWriter htmlWriter = new HtmlTextWriter(writer);

    // ボタンコントロールの作成
    Button btn = new Button();
    btn.Text = "新規ボタン";
    btn.ID = "newButton";  // ボタンを複数設置するときはIDが重複しないように!
    // JavaScript用のScriptである「__doPost」を作成する。
    string script = Page.ClientScript.GetPostBackEventReference(btn, string.Empty);
    btn.OnClientClick = script;
    btn.UseSubmitBehavior = false;  // trueだと「__doPost」がHTMLに反映されない。
    btn.RenderControl(htmlWriter);
  // HtmlWriterに出力

    // innerAreaはデザイナ上で追加した「<div id="innerArea" runat="server">」タグのID
    // Writerの大元であるStringBuilderでHTMLの文字列出力
    innerArea.InnerHtml += builder.ToString();
 

  // ..中略..

    if (isPostBack) {
        // イベントが発生しないので対象ボタンを確認してメソッドを呼び出す。
        // 通常のイベントはPage_Loadが終わってから呼び出されるが、
        // これはPage_Loadの実行中であるため、Page_Loadの最後に記載する
        string eventTarget = (string)Request["__EVENTTARGET"];
        if (! String.IsNullOrEmpty(eventTarget) && eventTarget == "newButton")
        {
            // 別途用意したイベントハンドラ用のメソッドを呼び出す。
            Btn_Click(sender, e);
        }
    }

// イベントハンドラ用に用意しているメソッド
protected void Btn_Click(object sender, EventArgs e)
{
    // ..省略..

ASP.net Webフォームで、どのようなイベントが発生したのか(イベントハンドラ)をコード上で確認・取得する方法

概要

ASP.net Webフォームで、どのようなイベントが発生したのか(イベントハンドラ)をコード上で確認・取得する方法について説明します。

前提環境
  • Windows 10
  • Visual Studio Professional 2019
  • .NET Framework 4.2
  • C#

コードのポイント

Requestの添字「”__EVENTTARGET”」や「”__EVENTARGUMENT”」で確認することができます。
※ウォッチ式で確認する場合は注意が必要です。サンプルコードの下の解説を参照してください。

サンプルコード

string eventTarget = (string)Request["__EVENTTARGET"];
string eventArgument = (string)Request["__EVENTARGUMENT"];


// eventTargetの値は"gridView1"
// eventArgumentの値は"Select$1"

ウォッチ式で確認するときの注意事項

ウォッチ式で確認した場合の表示は以下のとおりです。
例として、GridViewで行を選択したときのものを簡単に示します。
__EVENTTARGETはRequestの直下には存在していないので注意してください。
添字ではRequest[“__EVENTTARGET”]なのに不思議です。。。何故なのかは調べていませんが、エイリアスでも設定されているのでしょう(適当)

Request
 - Form
   - _entriesArray
     - [0]
       - Key:"__EVENTTARGET"
       - Value
         - [0]:"gridView1"
      - [1]
       - Key:"__EVENTARGUMENT"
       - Value
         - [0]:"Select$1"

C#のListを並び替える方法(数字や文字列以外にStruct(構造体)なども!)

概要

C#のListをどうやって並び替えていますか?
数字や文字列以外にStruct(構造体)など、各種ジェネリックでも対応可能な方法について解説します。

前提環境
  • Windows 10
  • Visual Studio Professional 2019
  • .NET Framework 4.2
  • C#

コードのポイント

  • ListはSortメソッドで並び替えが可能です。
  • Sortメソッドを使用すると、そのオブジェクトList自体が並び変えられます。
    (他の変数に代入したい場合はOrderByメソッドなどを使用します。)
  • compareToメソッドがある型を並び替える場合、Sortは引数無しでOKです。
    (正確にはIComparableインターフェースのある型)
  • compareToメソッドが無い場合、ラムダ式で比較内容を記載すると楽ちんです。(サンプルコード参照。)

サンプルコード

int(数値)のListの場合

List<int> intList = new List<int>();
// 追加は省略
intList.Sort();  // Sortを引数無しで呼び出す。

string(文字列)のListの場合

List<string> stringList = new List<string>();
// 追加は省略
stringList.Sort();  // Sortを引数無しで呼び出す。

Struct(構造体)のListの場合

// 下記のような構造体をdisplayNameで並び替える。
struct TestStruct
{
        public string name;
        public string displayName;
        public string description;
}

// 追加は省略
List<TestStruct> stList = new List<TestStruct>();

// Sort引数の中身をラムダ式で表現する。
// 以下の例はdisplayNameで並び替えを行っています。
// 今回のラムダ式の詳しい解説は下記に記載しています。
stList.Sort((a, b) => a.displayName.CompareTo(b.displayName));

ラムダ式の簡単な解説

そもそもSortの()の中には何を書くの?

  • マニュアルを参照すると4種類ぐらいありますが、今回紹介しているのは「Comparison<T>」です。
  • 「Comparison<T>」とは同じ型の 2 つのオブジェクトを比較する「メソッド」を表します。
  • そうです。「メソッド」を引数にするのです。普段、数字や文字列ばかりだと慣れないかもしれませんが、「メソッド」も引数にできるのです。
  • で、「Comparison<T>」ですが、「public delegate int Comparison<in T>(T x, T y);」というメソッドで、引数が2つ、返り値がint型のメソッドです。

なんでラムダ式?矢印の左のカッコと右側って何?

  • ラムダ式は「メソッド」の1つです。なので、Sortの()の「Comparison<T>」として書くことができます。
  • 「=>」の左が引数で、右側が処理内容です。
  • (a, b)なので、引数が2つです。右側の「a.displayName.CompareTo(b.displayName)」が処理内容です。本来なら{}で囲んで、returnも付ける必要がありますが、1行だけの場合はどちらも省略可能です。
  • つまり引数が2つで戻り値がint(compreToの結果を返す)ので、「public delegate int Comparison<in T>(T x, T y);」と一致したメソッドになります。

それはわかったけど戻り値と並び替えの関係性は?

「public delegate int Comparison<in T>(T x, T y);」の戻り値intと、並び替えの関係ですが以下のようになります。

戻り値意味
0より小さい引数aは引数bよりも小さい(昇順の並び替えで上にくる)
0引数aは引数bと同じ大きさ。並び替えない
0より大きい引数aは引数bよりも大きい(昇順の並び替えで下にくる)

ASP.NET WebフォームでCSSのStyle属性をコード上から追加、変更する方法

概要

ASP.netのWebフォームでコントロールのCSSを修正するためにStyleプロパティを変更しようとすると「プロパティ’Style’は’ReadOnly’です。」のエラーになります。
ここではStyleを変更する正しい方法を解説します。

前提環境
  • Windows 10
  • Visual Studio Professional 2019
  • .NET Framework 4.2
  • C#

コードのポイント

Attributesプロパティから変更するのが正しいやり方です。

サンプルコード

「プロパティ’Style’は’ReadOnly’です。」のエラーになる書き方

Button.Style = "display:none";

Styleを変更するための正しい書き方

// 変更する時もAddメソッドでOKです。同じキーなら上書きされます。
Button.Attributes.CssStyle.Add("display", "block");

cakePHP4のバリデーションを動的に変更する方法(既存のルールをcontroller側で一時的に無効化する方法)

概要

cakePHP4のバリデーションを動的に変更する方法(既存のルールをcontroller側で一時的に無効化する方法)について解説します。

例えば、ユーザー情報の登録、パスワード変更において以下のような要件があったとします。このとき、UsersTableのvalidationDefaultに設定済みのルールを動的に変更するにはどうしたら良いでしょうか?

  • ユーザー追加時には現在のパスワードのチェックは不要
  • ユーザー情報編集時に、パスワード欄に入力があれば、現在のパスワード、確認用パスワードをチェックする。
  • ユーザー情報編集時に、パスワード欄に入力が無ければ、現在のパスワード、確認用パスワードをチェック不要。
前提環境
  • Hosting Server: さくらインターネット
  • PHP: 7.4.9
  • cakePHP: 4.1.1

コードのポイント

動的にValidatorの設定を変更する方法はいくつか考えられますが、今回はあらかじめ必要な設定を全て記入し、必要に応じて設定を外していく方法を使用します。

controllerからvalidatorを取得するには「$this->(モデル名->getValidator();」を使います。

設定を外していくには「offsetUnset」を使用します。

サンプルコード

※注意!
本来ならばパスワードの文字数や確認用パスワードとの一致など、ルールがもっと必要ですが、サンプルコードのため除外しています。

/src/Model/Table/UsersTable.php

public function validationDefault(Validator $validator): Validator
{
    $validator
        ->requirePresence('email')
        ->notEmptyString('email', 'メールアドレスは省略出来ません。')
        ->email('email', false, 'メールアドレスの形式ではありません。')

        ->requirePresence('username')
        ->notEmptyString('username', 'ユーザ名は省略出来ません。')

        ->requirePresence('password')
        ->notEmptyString('password', 'パスワードは省略出来ません。')

        ->requirePresence('password_check')
        ->notEmptyString('password_check', '確認用パスワードは省略出来ません。')


    return $validator;
}

/src/Controller/UsersController.php

public function edit($id = null)
{
    $user = $this->Users->get($id);
    if ($this->request->is(['patch', 'post', 'put'])) { 
        // パスワードに入力があるか確認、パスワードがなければ更新対象としない。
        if (empty($this->request->getData('password'))) {
            // バリデーションのルールを解除する。
            $this->offsetPasswordValidator();
            // 更新対象からも外しておく
            $user->setAccess('password', false);
        } else {
            $user->setAccess('password', true);
        }

        $user = $this->Users->patchEntity($user, $this->request->getData());

        if ($this->Users->save($user)) {
            $this->Flash->success(__('ユーザー情報の変更を保存しました。'));
            return $this->redirect(['controller'=>'pages', 'action' => 'index']);
        } else {
            $this->Flash->error(__('ユーザー情報の変更の保存に失敗しました。もう一度お試しください。'));
        }
    }

    // 編集画面表示時点ではパスワードの入力があるかどうかわからないので
    // バリデーションのルールを解除する。
    // ここで解除しないと、パスワードが必須のままになる。
    $this->offsetPasswordValidator();
    $this->set(compact('user'));
}

// バリデーションのルール解除用の関数。
// 複数回呼ばれるので関数にまとめた。
private function offsetPasswordValidator()
{
    $validator = $this->Users->getValidator();
    $validator->offsetUnset('password_now');
    $validator->offsetUnset('password');
    $validator->offsetUnset('password_check');
}

cakePHP4のページネーションで、1ページ当たり100件までしかデータが表示できない場合の対応方法

概要

cakePHP4のページネーションで、1ページ当たり100件までしかデータが表示できない場合の対応方法について解説します。

前提環境
  • Hosting Server: さくらインターネット
  • PHP: 7.4.9
  • cakePHP: 4.1.1

コードのポイント

cakePHP4ではPaginatorComponentによる1ページの表示件数の上限がデフォルト値で100件になっています。このため「maxLimit」を使って上限数を変更する必要があります。
CookBook:ページネーション「1ページあたりの最大行数を制限する」

ページネーションは一覧画面でよく使用されますが、一覧画面の表示件数をユーザーが変更できる機能が設けられていることがあります。このとき、ユーザーが10,000のような大きな数字を設定すると、サーバーに大きな負荷がかかってしまいます。
アプリ開発者が上限数を設定すればよいのですが、必ずしも設定するとは限りません。そのためフレームワーク側でデフォルト値を設けています。

サンプルコード

// 商品検索のクエリ
$itemQuery = $this->Items->find();

// ページネータの処理
$paginate = [
    'limit' => 50,  // ユーザーが指定可能な数字
    'maxLimit' => 150, // アプリ側で制限する上限値
];

// ページネーションの設定
$items = $this->Paginator->paginate($itemQuery, $paginate);

C# 構造体のカスタム属性をつかったHTMLのセレクトボックス(ドロップダウン)の作成方法

概要

C#の構造体の情報を使ってHTMLのセレクトボックス(ドロップダウンともいうselectタグとoptionタグのやつ)を作成する場合、値であるvalueは構造体から取得できますが、実際に画面に表示させる文字列はどうやっていますか?
今回はそれらの情報をカスタム属性を使用して構造体に記載し、構造体のデータだけでセレクトボックスを作成する方法を解説します。

前提環境
  • Windows 10
  • Visual Studio Professional 2019
  • .NET Framework 4.2
  • C#

コードのポイント

  • カスタム属性「System.ComponentModel.DescriptionAttribute」を使うことで、構造体のメンバに対して説明の文字列を追加することができます。
  • 構造体のメンバをforeachで取得するにはEnum.GetValuesを使用します。
  • カスタム属性を取得するにはGetCustomAttributesを使用します。

サンプルコード

前提となる構造体

public enum FruitEnum
{
     [System.ComponentModel.DescriptionAttribute("りんご")]
     Apple = 1,

     [System.ComponentModel.DescriptionAttribute("みかん")]
     Orange = 2,

     [System.ComponentModel.DescriptionAttribute("パイナップル")]
     Pine = 3,
}

HTMLのセレクトボックス(ドロップダウン)の作成

Type type = typeof(FruitEnum);
string inputHtml = "<select name=\"fruitName\">";

// 構造体の各メンバの値をforeachで取得する。
foreach (Object enumVal in Enum.GetValues(type))
{
    // 構造体の値からメンバを取得
    FieldInfo enumField = type.GetField(enumVal.Tostring());

    // 構造体のメンバに設定したカスタム属性を取得する。
    // カスタム属性は同じものを複数設定できることがあるため、配列で取得することになる。
    // そのためFirstOrDefaultで1つに絞り込む。
    var attributes = (DescriptionAttribute[])enumField.GetCustomAttributes(typeof(DescriptionAttribute), false);
    var desciptionstring = attributes.Select(n => n.Description).FirstOrDefault();
    string enumName = desciptionstring.Tostring();

    inputHtml += $"<option value=\"{enumVal.Tostring()}\">{enumName}</option>";
}
inputHtml += $"</select>";

Formから取得したデータ(文字列)を構造体に変換する場合

string value = Page.Request.Form["fruitName"];

FruitEnum fruit;
if (! Enum.TryParse<FruitEnum>(value, out fruit)) {
    fruit = FruitEnum.Apple;
}

C#で文字列から構造体への変換

概要

構造体はそのまま使用するのがコード上で求められるわけですが、DBのデータから取得した値や、WebのHTML Formなどで送信されてきた値を構造体に変換させることが求められることがあります。 そこで、「文字列」のデータを「構造体」のデータに変換させる方法について記載します。

前提環境
  • Windows 10
  • Visual Studio Professional 2019
  • .NET Framework 4.2
  • C#

コードのポイント

変換不可能な場合がありますから、TryParseを使って変換可能か確認しつつ値を変換します。
TryParseの第2引数は out を使うことで参照渡しとなり、変換可能な場合に値が入ります。

サンプルコード

public enum FruitEnum
{
    Apple,
    Orange,
    Pine,
}

public class Test
{
    public void getEnum(String enumCode)
    {
        FruitEnum fruit;
        // ジェネリックで型指定を忘れないように!
        if (! Enum.TryParse<FruitEnum>(enumCode, out fruit)) {
           fruit = FruitEnum.Apple;
        }

// 以下省略

C#で複数の型を持つ多次元配列(型指定あり)を作る

概要

PHPでは以下のような型の異なる多次元配列を作成することが可能です。
このような多次元配列をC#でも作成する方法について解説します。

$list = [
    ["TV", 25]
    ["Table", 80],
];
前提環境
  • Windows 10
  • Visual Studio Professional 2019
  • .NET Framework 4.2
  • C#

コードのポイント

複数の型をもつデータを持つにはいろいろな方法があります。
Class(クラス)やStruct(構造体)がまず思いつきますが、コード上のその場限りで使うにはふさわしくありません。
そこで今回はTuple(タプル)を使うことにします。このTupleのデータに対してListを使って配列化していきます

サンプルコード

public function createTable() {
    // Tupleを使って、文字列や数字、メソッド(関数)などの型を持つデータ構造を作ります。
    // そのTupleの型をListのジェネクリックに指定して多次元配列とします。
    // 戻り値無しのメソッドの型はAction。戻り値がある場合はFuncを使います。
    // Actionの後ろの<int>は引数のジェネリックで、第一引数のintを示します。
    List<Tuple<string, int, Action<int>, Action>> list = new List<Tuple<string, int, Action<int>, Action>>();
    
    // Tupleをnewする場合は、後ろの丸括弧内に値を記載します。
    list.Add(new Tuple<string, int, Action<int>, Action>("TV", 23, ShowTVSize, ShowTVPrice));
    list.Add(new Tuple<string, int, Action<int>, Action>("Table", 90, ShowTableSize, ShowTablePrice));
 
   // foreachでTupleのListデータを取得する場合はこのような記載になります。
    foreach (Tuple<string, int, Action<int>, Action> tuple in list)
    {
        // Tupleの値はItem1, Item2となりますが、可読性が低いので変数を用意します。
        string name = tuple.Item1;
        int size = tuple.Item2;
        Action<IConfig> showSizeMethod = tuple.Item3;
        Action showPriceMethod = tuple.Item4;

/*
 * 途中省略
 */

// TVのサイズを表示するメソッド
public void ShowTVSize(int size)
{
    System.Console.WriteLine("TV Size is {0}", size * 2.54);
}

// 食卓のサイズを取得するメソッド
public void ShowTVSize(int size)
{
    System.Console.WriteLine("Table Size is {0}", size);
}

// TVの金額を取得するメソッド
public void ShowTVPrice()
{
    System.Console.WriteLine("TV Price is 25,000");
}

// 食卓の金額を取得するメソッド
public void ShowTVPrice()
{
    System.Console.WriteLine("Table Price is 10,000");
}