web-dev-qa-db-ja.com

C#4.0の「動的」タイプは何に使用されますか?

C#4.0では、「ダイナミック」と呼ばれる新しいタイプが導入されました。それはすべて良さそうですが、プログラマーは何のためにそれを使用しますか?

1日を節約できる状況はありますか?

202
Fahad

DynamicキーワードはC#4.0で新しく追加されたもので、コンパイラに変数の型が変更される可能性があること、または実行時まで不明であることを伝えるために使用されます。オブジェクトをキャストすることなくオブジェクトと対話できると考えてください。

dynamic cust = GetCustomer();
cust.FirstName = "foo"; // works as expected
cust.Process(); // works as expected
cust.MissingMethod(); // No method found!

型Customerとしてcustをキャストまたは宣言する必要がないことに注意してください。動的と宣言したため、ランタイムが引き継ぎ、FirstNameプロパティを検索して設定します。もちろん、動的変数を使用しているときは、コンパイラの型チェックをあきらめています。これは、呼び出しcust.MissingMethod()がコンパイルされ、実行時まで失敗しないことを意味します。 MissingMethodがCustomerクラスで定義されていないため、この操作の結果はRuntimeBinderExceptionになります。

上記の例は、メソッドとプロパティを呼び出すときの動的な動作を示しています。別の強力な(そして潜在的に危険な)機能は、さまざまなタイプのデータに変数を再利用できることです。 Python、Ruby、Perlのプログラマーは、これを利用するための無数の方法を考えることができると確信していますが、私はC#を長い間使用してきました。

dynamic foo = 123;
foo = "bar";

わかりましたので、上記のようなコードを頻繁に書くことはほとんどないでしょう。ただし、変数の再利用が役立つ場合や、レガシーコードの汚れた部分をクリーンアップする場合があります。私がよく遭遇する簡単なケースの1つは、常に10進数と2進数の間でキャストすることです。

decimal foo = GetDecimalValue();
foo = foo / 2.5; // Does not compile
foo = Math.Sqrt(foo); // Does not compile
string bar = foo.ToString("c");

2.5はdoubleとして入力され、3行目はMath.Sqrtがdoubleを想定しているためコンパイルされないため、2行目はコンパイルされません。明らかに、あなたがしなければならないことは、変数の型をキャストおよび/または変更することだけですが、ダイナミックを使用することが理にかなっている場合があります。

dynamic foo = GetDecimalValue(); // still returns a decimal
foo = foo / 2.5; // The runtime takes care of this for us
foo = Math.Sqrt(foo); // Again, the DLR works its magic
string bar = foo.ToString("c");

続きを読む機能: http://www.codeproject.com/KB/cs/CSharp4Features.aspx

162
Pranay Rana

dynamicキーワードは、C#4.0の他の多くの新機能とともに追加され、異なるAPIを持つ他のランタイムに存在する、または他のランタイムから来たコードとの対話を簡単にしました。

例を挙げましょう。

Word.ApplicationオブジェクトのようなCOMオブジェクトがあり、ドキュメントを開きたい場合、そのためのメソッドには15個以上のパラメーターがあり、そのほとんどはオプションです。

このメソッドを呼び出すには、次のようなものが必要です(簡略化していますが、これは実際のコードではありません)。

object missing = System.Reflection.Missing.Value;
object fileName = "C:\\test.docx";
object readOnly = true;
wordApplication.Documents.Open(ref fileName, ref missing, ref readOnly,
    ref missing, ref missing, ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing, ref missing, ref missing,
    ref missing, ref missing);

これらすべての引数に注意してください?バージョン4.0より前のC#にはオプションの引数の概念がなかったため、これらを渡す必要があります。 C#4.0では、次のものを導入することにより、COM APIの操作が容易になりました。

  1. オプションの引数
  2. COM APIのrefをオプションにする
  3. 名前付き引数

上記の呼び出しの新しい構文は次のとおりです。

wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);

どれだけ簡単に見えるか、どれだけ読みやすくなるかを見てください。

それをバラバラにしましょう:

                                    named argument, can skip the rest
                                                   |
                                                   v
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
                                 ^                         ^
                                 |                         |
                               notice no ref keyword, can pass
                               actual parameter values instead

魔法は、C#コンパイラが必要なコードをインジェクトし、ランタイムで新しいクラスを操作して、以前とまったく同じことを行うことですが、構文は隠されているため、 what、およびhowではあまりありません。 Anders Hejlsbergは、さまざまな「呪文」を呼び出す必要があると言うのが好きです。「呪文」は、全体の魔法の一種であり、通常は手を振って正しい順序でいくつかの魔法の言葉を言う必要があります特定の種類のスペルを取得するために。 COMオブジェクトと通信する古いAPIの方法は、その多くでした。コンパイラを調整してコードをコンパイルするには、多くのフープをジャンプする必要がありました。

インターフェイスまたはクラスを持たないCOMオブジェクトと通信しようとすると、バージョン4.0より前のC#で事態はさらに悪化します。必要なのはIDispatch参照だけです。

それが何かわからない場合、IDispatchは基本的にCOMオブジェクトのリフレクションです。 IDispatchインターフェースを使用すると、オブジェクトに「保存」と呼ばれるメソッドのID番号を尋ね、引数値を含む特定のタイプの配列を作成し、最後にInvokeインターフェースでIDispatchメソッドを呼び出してメソッドを呼び出すことができます、あなたが管理したすべての情報を一緒に渡します。

上記のSaveメソッドは次のようになります(これは間違いなく正しいコードではありません):

string[] methodNames = new[] { "Open" };
Guid IID = ...
int methodId = wordApplication.GetIDsOfNames(IID, methodNames, methodNames.Length, lcid, dispid);
SafeArray args = new SafeArray(new[] { fileName, missing, missing, .... });
wordApplication.Invoke(methodId, ... args, ...);

これらはすべて、ドキュメントを開くだけです。

VBにはオプションの引数があり、このほとんどをすぐにサポートしていたため、このC#コードは次のとおりです。

wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);

基本的には、C#が表現力の点でVBに追いついていますが、COMだけでなく拡張可能にすることで正しい方法でそれを行います。もちろん、これはVB.NETまたは.NETランタイム上に構築された他の言語でも使用できます。

IDispatchインターフェイスの詳細については、 Wikipedia:IDispatch を参照してください。それは本当につまらないものです。

ただし、Pythonオブジェクトとやり取りしたい場合はどうでしょうか。それには、COMオブジェクトに使用されるものとは異なるAPIがあり、Pythonオブジェクトも本質的に動的であるため、呼び出す適切なメソッドやパラメーターなどを見つけるためにリフレクションマジックに頼る必要があります.NETリフレクションではなく、Python向けに作成されたもので、上記のIDispatchコードによく似ていますが、まったく異なります。

そしてRubyの場合は?まだ別のAPI。

JavaScript?同じ取引、そのための異なるAPIも同様です。

動的キーワードは、次の2つの要素で構成されます。

  1. C#の新しいキーワード、dynamic
  2. さまざまなタイプのオブジェクトの処理方法を認識し、dynamicキーワードが必要とする特定のAPIを実装し、呼び出しを正しい方法にマップするランタイムクラスのセット。 APIは文書化されているため、対象外のランタイムに由来するオブジェクトがある場合は追加できます。

ただし、dynamicキーワードは、既存の.NET専用コードを置き換えるものではありません。確かに、あなたはcanできますが、そのために追加されませんでした。そして、C#プログラミング言語の作者はAnders Hejlsbergを先頭にしています彼らはまだC#を強く型付けされた言語と見なしており、その原則を犠牲にしないことを最も強く主張しました。

これは、次のようなコードを記述できますが、

dynamic x = 10;
dynamic y = 3.14;
dynamic z = "test";
dynamic k = true;
dynamic l = x + y * z - k;

コンパイルさせてください。それは一種の魔法のように実行時のシステムを実行するためのものではありません。

全体の目的は、他の種類のオブジェクトと簡単に会話できるようにすることでした。

キーワード、提案者、反対者、議論、暴言、賞賛などについての資料がインターネット上にたくさんあります。

次のリンクから始めて、さらにグーグルで検索することをお勧めします。

誰も言及していないことに驚いています 複数のディスパッチ 。これを回避する通常の方法は Visitor pattern を使用することです。これは常に可能であるとは限らないため、isチェックがスタックされます。

そこで、ここに私自身のアプリケーションの実際の例を示します。代わりに:

public static MapDtoBase CreateDto(ChartItem item)
{
    if (item is ElevationPoint) return CreateDtoImpl((ElevationPoint)item);
    if (item is MapPoint) return CreateDtoImpl((MapPoint)item);
    if (item is MapPolyline) return CreateDtoImpl((MapPolyline)item);
    //other subtypes follow
    throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}

あなたがやる:

public static MapDtoBase CreateDto(ChartItem item)
{
    return CreateDtoImpl(item as dynamic);
}

private static MapDtoBase CreateDtoImpl(ChartItem item)
{
    throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}

private static MapDtoBase CreateDtoImpl(MapPoint item)
{
    return new MapPointDto(item);
}

private static MapDtoBase CreateDtoImpl(ElevationPoint item)
{
    return new ElevationDto(item);
}

最初の場合、ElevationPointMapPointのサブクラスであり、配置されていない場合beforeMapPointitには到達しないことに注意してください。最も近い一致メソッドが呼び出されるため、これは動的の場合には当てはまりません。

コードから推測できるように、ChartItemオブジェクトからシリアル化可能なバージョンへの変換を実行しているときに、この機能が役に立ちました。私は訪問者でコードを汚染したくありませんでした。また、無用のシリアル化固有の属性を持つChartItemオブジェクトも汚染したくありませんでした。

21

静的型付き言語(CLR)がDLR(動的言語ランタイム)で実行されている動的言語(python、Ruby ...)と相互運用しやすくなります。 MSDN を参照してください。 =:

たとえば、次のコードを使用して、C#でXMLのカウンターをインクリメントできます。

Scriptobj.SetProperty("Count", ((int)GetProperty("Count")) + 1);

DLRを使用すると、同じ操作に代わりに次のコードを使用できます。

scriptobj.Count += 1;

MSDNには次の利点がリストされています。

  • 動的言語の.NET Frameworkへの移植を簡素化
  • 静的に型付けされた言語で動的機能を有効にします
  • DLRと.NET Frameworkの将来の利点を提供します
  • ライブラリとオブジェクトの共有を可能にします
  • 高速な動的ディスパッチと呼び出しを提供します

詳細については、 MSDN を参照してください。

11

使用例:

Communプロパティ 'CreationDate'を持つ多くのクラスを使用します。

public class Contact
{
    // some properties

    public DateTime CreationDate { get; set; }        
}

public class Company
{
    // some properties

    public DateTime CreationDate { get; set; }

}

public class Opportunity
{
    // some properties

    public DateTime CreationDate { get; set; }

}

'CreationDate'プロパティの値を取得する共通メソッドを記述する場合、リフレクションを使用する必要があります。

    static DateTime RetrieveValueOfCreationDate(Object item)
    {
        return (DateTime)item.GetType().GetProperty("CreationDate").GetValue(item);
    }

「動的」コンセプトにより、コードはよりエレガントになります。

    static DateTime RetrieveValueOfCreationDate(dynamic item)
    {
        return item.CreationDate;
    }
4
Akli

COM相互運用。特にIUnknown。それのために特別に設計されました。

3
Aen Sidhe

私にとっての「動的」型変数の最適な使用例は、最近、ADO.NETでデータアクセスレイヤーを記述して(sing SQLDataReader)、コードが既に記述されたレガシーストアドプロシージャを呼び出していたときでした。ビジネスロジックの大部分を含むこれらのレガシーストアドプロシージャは数百あります。私のデータアクセスレイヤーは、何らかの操作を行うために、C#ベースのビジネスロジックレイヤーに何らかの構造化データを返す必要がありました(ほとんどありませんが)。すべてのストアドプロシージャは、異なるデータセットを返します(テーブル列)。したがって、返されたデータを保持してBLLに渡すために多数のクラスまたは構造体を作成する代わりに、非常にエレガントできれいに見える以下のコードを作成しました。

public static dynamic GetSomeData(ParameterDTO dto)
        {
            dynamic result = null;
            string SPName = "a_legacy_stored_procedure";
            using (SqlConnection connection = new SqlConnection(DataConnection.ConnectionString))
            {
                SqlCommand command = new SqlCommand(SPName, connection);
                command.CommandType = System.Data.CommandType.StoredProcedure;                
                command.Parameters.Add(new SqlParameter("@empid", dto.EmpID));
                command.Parameters.Add(new SqlParameter("@deptid", dto.DeptID));
                connection.Open();
                using (SqlDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        dynamic row = new ExpandoObject();
                        row.EmpName = reader["EmpFullName"].ToString();
                        row.DeptName = reader["DeptName"].ToString();
                        row.AnotherColumn = reader["AnotherColumn"].ToString();                        
                        result = row;
                    }
                }
            }
            return result;
        }
1
user1451111

主にRADおよびPythonの被害者がコード品質を破壊するために使用します。 IntelliSense およびコンパイル時のバグ検出。

1

dynamicタイピングの別の使用例は、共分散または反分散の問題が発生する仮想メソッドです。そのような例の1つは、呼び出されるオブジェクトと同じ型のオブジェクトを返す悪名高いCloneメソッドです。この問題は、静的な型チェックをバイパスするため、動的な戻り値では完全には解決されませんが、少なくともプレーンなobjectを使用する場合のように、常にcastいキャストを使用する必要はありません。そうでなければ、キャストは暗黙的になります。

public class A
{
    // attributes and constructor here
    public virtual dynamic Clone()
    {
        var clone = new A();
        // Do more cloning stuff here
        return clone;
    }
}

public class B : A
{
    // more attributes and constructor here
    public override dynamic Clone()
    {
        var clone = new B();    
        // Do more cloning stuff here
        return clone;
    }
}    

public class Program
{
    public static void Main()
    {
        A a = new A().Clone();  // No cast needed here
        B b = new B().Clone();  // and here
        // do more stuff with a and b
    }
}
0
Frederic
  1. Pythonnetを使用して、CPythonなどの動的言語を呼び出すことができます。

dynamic np = Py.Import("numpy")

  1. 数値演算子を適用するときに、ジェネリックをdynamicにキャストできます。これにより、型の安全性が確保され、ジェネリックの制限が回避されます。これは本質的に*ダックタイピングです:

T y = x * (dynamic)x、ここでtypeof(x) is T

0
denfromufa