[C#] 27. varキーワードとdynamicキーワード


Study / C#    作成日付 : 2019/07/16 20:41:27   修正日付 : 2021/09/10 21:16:32

こんにちは。明月です。


この投稿はvarキーワードとdynamicキーワードに関する説明です。


以前の投稿でプログラムのStackメモリとHeapメモリの関係に関して説明したことがあります。

link - [C#] 11. インスタンスう生成(new)とメモリ割り当て(StackメモリとHeapメモリ)そしてヌル(null)


変数で宣言されているデータタイプはHeapに生成するインスタンスのポインタのアドレスということで説明したことがあります。

このポインタのアドレスというのは大きいデータがあることではなく、普通の整数になっているデータです。それで、一々にクラス名に合わせてソースを作成することは面倒くさいことですね。

それで、この変数のデータタイプを作成しやすいようにするキーワードがありますが、それがvarキーワードです。

using System;

namespace Example
{
  // 例クラス
  class Node
  {
    // プロパティ
    public int Data
    {
      get; set;
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // Nodeタイプのnode変数にNodeインスタンスのポインタを格納する。
      Node node = new Node();
      // プロパティ変数の値を設定
      node.Data = 10;
      // コンソール出力
      Console.WriteLine(node.Data);
      
      // varタイプのnode1変数にNodeインスタンスのポインタを格納する。
      var node1 = new Node();
      // プロパティ変数の値を設定
      node1.Data = 20;
      // コンソール出力
      Console.WriteLine(node1.Data);
      
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例の結果をみればnodeの変数とnode1の変数の差異はソース上で作成するところでNodeのクラスタイプからvarタイプに変わったことだけで、プログラム上で実装する処理に関して同じです。

つまり、varキーワードとは変数で宣言を簡単にするためのキーワードだけで、性能と処理に関してはなにも影響はありません。


それでC#のソース規約(コーディングに関する規則)に従うとローカル変数宣言はvarキーワードを使うのをお勧めします。


でも、このvarキーワードには何個か制約条件があります。

最初の宣言する時にnullで設定はできません。


そして最初の宣言したタイプは途中でタイプ変更はできません。


そしてvarキーワードは関数のスタック領域だけ、つまりローカルだけ使うことができるしメンバー変数や関数のリターン値、パラメータは使えません。


改めてまとめると、varキーワードは最初の宣言した原始データあるいはクラスタイプで設定ができるし、次は設定を変わりません。

なので、当然に最初宣言する時にタイプを設定するのでnullは使えないし、メンバー変数とリターンタイプ、パラメータタイプでは設定を区分することができないので使えません。


C#にはvarキーワードと似ているdynamicキーワードがあります。

dynamicキーワードはvarキーワードと似ていますが、最初に宣言する時にデータタイプが決定することではなく、基本データタイプのObjectで設定があります。


Objectクラスに関して簡単に説明するとC#ではクラスが最小単位です。

C#ではクラスを宣言する時に別に継承を設定しなくても、基本的にObjectクラスを継承します。つまり、最上位クラスという意味です。

Objectに関して少し複雑なので別の投稿で説明します。


また、dynamicに戻って説明すると、我々がプログラムを作成すればデバッグのエラーによりプログラムを作成できない場合があります。

using System;

namespace Example
{
  // インタフェース
  interface INode
  {
  }
  // INodeインタフェースを継承するクラス
  class Node : INode
  {
    // プロパティ
    public int Data
    {
      get; set;
    }
  }
  class Program
  {
    // 関数でインスタンス取得する。
    static INode GetNode()
    {
      // インスタンスを生成
      return new Node();
    }
    // 実行関数
    static void Main(string[] args)
    {
      // 変数のタイプはINodeで設定して、関数でインスタンスを取得する。
      INode node = GetNode();
      // NodeクラスにDataプロパティがあるので使えそうだ。
      node.Data = 1;
      // NodeクラスのDataプロパティ値を取得する。
      Console.WriteLine(node.Data);
      
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


それで、Dataを参照できないエラーが発生します。

理由はGetNode関数でNodeインスタンスをINodeタイプに変換して返却するし、また、変数タイプもINodeなので、INodeにはDataプロパティがないのでデバッグエラーが発生します。

まぁ。当然なことです。そうならデータタイプをINodeからvarに変わったらDataを使いそうですが、GetNode関数でINodeに変換するので、結局同じエラーが発生します。


そうなら上みたいなコードはDataを使えないですが、dynamicキーワードを使いなら可能です。

using System;

namespace Example
{
  // インタフェース
  interface INode
  {
  }
  // INodeインタフェースを継承するクラス
  class Node : INode
  {
    // プロパティ
    public int Data
    {
      get; set;
    }
  }
  class Program
  {
    // 関数でインスタンスを取得する。
    static INode GetNode()
    {
      // インスタンスを生成
      return new Node();
    }
    // 実行関数
    static void Main(string[] args)
    {
      // 変数のタイプはdynamicで設定して、関数でインスタンスを取得する。
      dynamic node = GetNode();
      // NodeクラスのDataを参照する。
      node.Data = 1;
      // NodeクラスのDataプロパティ値を取得する。
      Console.WriteLine(node.Data);
      
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


dynamicを使うと実行できますね。dynamicキーワードは一応debug環境ではエラーが発生しません。

その後で実行をすると実際のHeapメモリにあるインスタンスの関数と変数を探すので実行ができることです。


このdynamicキーワードが万能みたいですが、実際にはこのdynamicはReflactionの機能と関連があるキーワードなので、普通の変数よりは少し遅くなります。

つまり、dynamicキーワードをたくさん使うと性能が遅くなる可能性があります。


そしてエラーをdebug上で発生しないので、プログラム品実と関係があります。

プログラムを作成する時にはエラーを分からないし、実行する途中でエラーが発生することなので、一つ一つに実行とテストで問題があるかエラーが無いかを検証するべきです。

using System;

namespace Example
{
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // Programのインスタンスを生成
      dynamic p = new Program();
      // ProgramクラスはDataプロパティ値が存在しないが、デバッグエラーが発生しない。
      p.Data = 1;
      
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


debugでエラーが発生せず、実行するとエラーが発生することを確認することができます。


dynamicキーワードはvarキーワードと違い、関数の返却タイプで設定できるし、メンバー変数、パラメータを設定することができます。

using System;

namespace Example
{
  class Program
  {
    // メンバー変数
    private static dynamic data;
    // 関数の返却式はdynamicタイプとパラメータのdynamicタイプ
    public static dynamic Function(dynamic param)
    {
      // 返却の値はString値だが。
      return data.ToString() + " : " + param.ToString();
    }
    // 実行関数
    static void Main(string[] args)
    {
      // メンバー変数設定
      Program.data = "Dynamic Example";
      // 関数実行
      var ret = Function("test");
      // コンソール出力
      Console.WriteLine(ret);
      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例をみればメンバー変数と関数返却値、パラメータでdynamicで使うことができます。


ここまでvarキーワードとdynamicキーワードに関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

#C#
最新投稿