C#4.0は「ダイナミック」と呼ばれる新しいタイプを導入しました。それはすべて良さそうに聞こえますが、プログラマーは何のためにそれを使用するでしょうか?
それがその日を救うことができる状況はありますか?
C#4.0は「ダイナミック」と呼ばれる新しいタイプを導入しました。それはすべて良さそうに聞こえますが、プログラマーは何のためにそれを使用するでしょうか?
それがその日を救うことができる状況はありますか?
回答:
dynamicキーワードはC#4.0の新機能であり、変数の型が変更される可能性があること、または実行時まで不明であることをコンパイラーに伝えるために使用されます。キャストしなくてもオブジェクトと対話できると考えてください。
dynamic cust = GetCustomer();
cust.FirstName = "foo"; // works as expected
cust.Process(); // works as expected
cust.MissingMethod(); // No method found!
custをタイプCustomerにキャストしたり宣言したりする必要がないことに注意してください。これを動的に宣言したため、ランタイムが引き継ぎ、FirstNameプロパティを検索して設定します。もちろん、動的変数を使用している場合は、コンパイラの型チェックをあきらめることになります。つまり、呼び出しcust.MissingMethod()はコンパイルされ、実行時まで失敗しません。MissingMethodがCustomerクラスで定義されていないため、この操作の結果はRuntimeBinderExceptionです。
上記の例は、メソッドとプロパティを呼び出すときにダイナミックがどのように機能するかを示しています。もう1つの強力な(そして潜在的に危険な)機能は、さまざまなタイプのデータの変数を再利用できることです。Python、Ruby、Perlのプログラマーは、これを利用する方法を100万通り考えられると確信していますが、私はC#を長い間使用しているので、「間違っている」と感じます。
dynamic foo = 123;
foo = "bar";
OK、それであなたはおそらく上記のようなコードを頻繁に書くことはないでしょう。ただし、変数の再利用が便利になったり、レガシーコードの汚れた部分をクリーンアップしたりできる場合があります。私がよく遭遇する単純なケースの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");
Math.Sqrtがdoubleを想定しているため、2.5がdoubleとして入力されているため2行目はコンパイルされず、3行目はコンパイルされません。明らかに、あなたがしなければならないすべては、変数の型をキャストおよび/または変更することですが、動的を使用することが理にかなっている状況があるかもしれません。
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
dynamic
、標準のc#機能と静的型付け、または型推論(var
)で解決できる(多分さらに良い)問題を解決するためにc#を使用するという考えは好きではありません。DLRとの相互運用性の問題に関してのみ使用してdynamic
ください。C#のような静的型付き言語でコードを記述する場合は、それを行い、動的言語をエミュレートしないでください。それは醜いです。
dynamic
必要のないコードで変数を頻繁に使用する場合(squarerootの例のように)、クリーンなコンパイル時のエラーチェックをあきらめます。代わりに、実行時エラーが発生する可能性があります。
このdynamic
キーワードは、C#4.0の他の多くの新機能と共に追加され、異なるAPIを持つ他のランタイムに存在する、または他のランタイムに由来するコードと簡単に対話できるようになりました。
例を挙げましょう。
オブジェクトのようなCOMオブジェクトがあり、Word.Application
ドキュメントを開きたい場合、それを行うためのメソッドには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は、以下を導入することで、より使いやすくなっています。
ref
COM APIのオプション上記の呼び出しの新しい構文は次のようになります。
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#コンパイラが必要なコードを挿入し、ランタイムで新しいクラスを操作して、以前とほぼ同じことを行うことですが、構文は非表示になっているため、何で、かつあまりありませんか。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に追いついています。もちろん、これはVB.NETまたは.NETランタイムの上に構築されたその他の言語でも使用できます。
IDispatch
インターフェースについて詳しくは、ウィキペディアのIDispatchを参照してください。それは本当に悲惨なものです。
ただし、Pythonオブジェクトと通信したい場合はどうでしょうか。COMオブジェクトに使用されているものとは異なるAPIがあり、Pythonオブジェクトも本質的に動的であるため、リフレクションマジックを使用して、呼び出す適切なメソッドやそのパラメーターなどを見つける必要がありますが、.NETではありません。リフレクション、Python用に書かれたもので、上記のIDispatchコードとほとんど同じですが、まったく異なります。
そしてRubyについては?まだ別のAPI。
JavaScript?同じことで、APIも異なります。
dynamicキーワードは、次の2つで構成されています。
dynamic
dynamic
キーワードに必要な特定のAPIを実装し、呼び出しを適切な方法にマップするランタイムクラスのセット。APIも文書化されているため、カバーされていないランタイムからのオブジェクトがある場合は、それを追加できます。dynamic
ただし、このキーワードは、既存の.NETのみのコードを置き換えるものではありません。もちろん可能ですが、その理由で追加されませんでした。AndersHejlsbergを前に置いたC#プログラミング言語の作者は、C#を依然として強く型付けされた言語と見なしており、犠牲にすることはありません。その原則。
つまり、次のようなコードを記述できます。
dynamic x = 10;
dynamic y = 3.14;
dynamic z = "test";
dynamic k = true;
dynamic l = x + y * z - k;
それをコンパイルしても、それは一種のマジック・レッツ・フィギュア・アウト・アウト・ユー・ミーント・アット・ランタイム・システムのようなものではありませんでした。
全体の目的は、他のタイプのオブジェクトと簡単に対話できるようにすることでした。
キーワード、支持者、反対者、議論、怒り、賛美などについては、インターネット上にたくさんの資料があります。
以下のリンクから始めて、さらにグーグルで検索することをお勧めします。
dynamic
ため、リフレクションのようなメソッド呼び出しを実行するための他のエコシステムをサポートし、ドキュメント化された方法でデータ構造に一種のブラックボックスアプローチを提供するために追加されました。
複数の派遣について誰も言及しなかったことに驚きます。これを回避する通常の方法は、訪問者パターンによるものであり、常に可能であるとは限らないため、スタックされた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);
}
最初のケースElevationPoint
はのサブクラスでMapPoint
あり、前 MapPoint
に配置されていない場合は到達しないことに注意してください。これは、最も近い一致メソッドが呼び出されるため、動的の場合には当てはまりません。
コードから推測できるように、ChartItemオブジェクトからシリアル化可能なバージョンへの変換を実行しているときに、この機能が役立ちました。私はビジターでコードを汚染したくありませんでした。また、不要ChartItem
なシリアル化固有の属性でオブジェクトを汚染したくありませんでした。
is
スタックを積み重ねたサイクロマティック複雑度の増加。
magic
。魔法のようなものはありません。
これにより、静的型付き言語(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を参照してください。
使用例:
共有プロパティ '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'プロパティの値を取得するCommunメソッドを作成する場合、リフレクションを使用する必要があります。
static DateTime RetrieveValueOfCreationDate(Object item)
{
return (DateTime)item.GetType().GetProperty("CreationDate").GetValue(item);
}
「動的」の概念により、コードははるかにエレガントになります。
static DateTime RetrieveValueOfCreationDate(dynamic item)
{
return item.CreationDate;
}
コードの品質、IntelliSense、およびコンパイル時のバグ検出を破壊するために、RADおよびPythonの被害者によって主に使用されます。
実行時に評価されるため、JavaScriptでできるようにタイプを好きなように切り替えることができます。これは合法です:
dynamic i = 12;
i = "text";
そして、必要に応じてタイプを変更できます。最後の手段として使用してください。それは有益ですが、生成されたILの面で多くのことが舞台裏で行われていると聞いたので、それはパフォーマンスの価格になる可能性があります。
'動的'型変数の最適な使用例は、最近、ADO.NETでデータアクセス層を(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;
}
dynamic
タイピングのもう1つの使用例は、共分散または反変の問題が発生する仮想メソッドです。そのような例の1つは、Clone
呼び出されたオブジェクトと同じタイプのオブジェクトを返す悪名高いメソッドです。この問題は静的な型チェックをバイパスするため、動的な戻りで完全に解決されるわけではありませんが、少なくともplainを使用するときは常に醜いキャストを使用する必要はありませんobject
。そうでなければ、キャストは暗黙的になります。
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
}
}