web-dev-qa-db-ja.com

C#:動的ランタイムキャスト

次のシグネチャを持つメソッドを実装したい

_dynamic Cast(object obj, Type castTo);
_

誰もそれを行う方法を知っていますか? objはcastToを確実に実装しますが、アプリの実行時バインディングの一部を機能させるには、適切にキャストする必要があります。

編集:答えのいくつかが意味をなさない場合は、最初に誤ってdynamic Cast(dynamic obj, Type castTo);と入力したためです-入力はobjectまたは他の保証された基本クラスでなければなりません

58
George Mauer

ここでキャストと変換の問題を混乱させていると思います。

  • キャスト:オブジェクトを指す参照のタイプを変更する行為。オブジェクト階層を上または下に移動するか、実装されたインターフェイスに移動する
  • 変換:異なるタイプの元のソースオブジェクトから新しいオブジェクトを作成し、そのタイプへの参照を介してアクセスします。

どちらも同じC#演算子であるキャストを使用しているため、C#の2の違いを知るのは難しい場合がよくあります。

この状況では、ほぼ確実にキャスト操作を探していません。 dynamicを別のdynamicにキャストすることは、本質的にID変換です。同じ基になるオブジェクトへのdynamic参照を取得しているだけなので、値は提供されません。結果のルックアップは同じです。

代わりに、このシナリオで必要と思われるのはコンバージョンです。それは、基になるオブジェクトを別の型にモーフィングし、dynamicの方法で結果のオブジェクトにアクセスすることです。これに最適なAPIはConvert.ChangeType

public static dynamic Convert(dynamic source, Type dest) {
  return Convert.ChangeType(source, dest);
}

[〜#〜] edit [〜#〜]

更新された質問には次の行があります。

objはcastToを確実に実装します

この場合、Castメソッドは存在する必要はありません。ソースobjectは、単にdynamic参照に割り当てることができます。

dynamic d = source;

達成しようとしているのは、source参照を介してdynamicの階層にある特定のインターフェイスまたはタイプを確認することです。それは単に不可能です。結果のdynamic参照は、実装オブジェクトを直接参照します。ソースの階層内の特定のタイプを調べません。したがって、階層内の別の型にキャストしてからdynamicに戻すという考え方は、最初にdynamicに割り当てるだけとまったく同じです。引き続き同じ基礎オブジェクトを指します。

82
JaredPar

これは動作するはずです:

public static dynamic Cast(dynamic obj, Type castTo)
{
    return Convert.ChangeType(obj, castTo);
}

編集

次のテストコードを作成しました。

var x = "123";
var y = Cast(x, typeof(int));
var z = y + 7;
var w = Cast(z, typeof(string)); // w == "130"

PHP、JavaScript、またはPython(converts目的の型の値)。それが良いことかどうかはわかりませんが、確かに機能します... :-)

30
rsenna

私がこれまでに得たベスト:

dynamic DynamicCast(object entity, Type to)
{
    var openCast = this.GetType().GetMethod("Cast", BindingFlags.Static | BindingFlags.NonPublic);
    var closeCast = openCast.MakeGenericMethod(to);
    return closeCast.Invoke(entity, new[] { entity });
}
static T Cast<T>(object entity) where T : class
{
    return entity as T;
}
8
George Mauer

オープンソースフレームワーク Dynamitey には、 others 間のキャスト変換を含むDLRを使用して遅延バインディングを行う静的メソッドがあります。

dynamic Cast(object obj, Type castTo){
    return Dynamic.InvokeConvert(obj, castTo, explict:true);
}

リフレクションを使用して呼び出されるCast<T>に対するこれの利点は、動的変換演算子ieを持つIDynamicMetaObjectProviderでも機能することです。 TryConvert on DynamicObject

7
jbtule

私はこれに答えられたことに気付きましたが、別のアプローチを使用し、共有する価値があると考えました。また、私のアプローチでは、不要なオーバーヘッドが生じる可能性があると感じています。しかし、私たちが観察している負荷の下でそれが悪いことを観察したり、計算したりすることはできません。このアプローチに関する有用なフィードバックを探していました。

ダイナミクスを使用する際の問題は、ダイナミックオブジェクトに関数を直接アタッチできないことです。毎回把握したくない割り当てを把握できるものを使用する必要があります。

この単純なソリューションを計画するとき、類似のオブジェクトを再入力しようとするときに有効な仲介者とは何かを見ました。バイナリ配列、文字列(xml、json)、または変換のハードコーディング( IConvertable )が通常のアプローチであることがわかりました。コードの保守性と遅延のために、バイナリ変換を行いたくありません。

私の理論では、 Newtonsoft は文字列仲介を使用してこれを行うことができました。

欠点として、文字列をオブジェクトに変換するときに、一致するプロパティを持つオブジェクトの現在のアセンブリを検索することでリフレクションを使用し、タイプを作成してからプロパティをインスタンス化することはかなり確信しています。 trueの場合、これらすべてを回避可能なオーバーヘッドと見なすことができます。

C#:

//This lives in a helper class
public static ConvertDynamic<T>(dynamic data)
{
     return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(Newtonsoft.Json.JsonConvert.SerializeObject(data));
}

//Same helper, but in an extension class (public static class),
//but could be in a base class also.
public static ToModelList<T>(this List<dynamic> list)
{
    List<T> retList = new List<T>();
    foreach(dynamic d in list)
    {
        retList.Add(ConvertDynamic<T>(d));
    }
}

そうは言っても、これは私がまとめた別のユーティリティに適合し、任意のオブジェクトをダイナミックにすることができます。私はそれを正しく行うために反射を使用しなければならなかったことを知っています:

public static dynamic ToDynamic(this object value)
{
    IDictionary<string, object> expando = new ExpandoObject();

    foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType()))
        expando.Add(property.Name, property.GetValue(value));

    return expando as ExpandoObject;
}

私はその機能を提供しなければなりませんでした。動的型付き変数に割り当てられた任意のオブジェクトはIDictionaryに変換できず、ConvertDynamic関数が破損します。この関数チェーンを使用するには、System.Dynamic.ExpandoObjectまたはIDictionary <string、object>のダイナミクスを提供する必要があります。

6
JRodd

ジェネリックを試してください:

public static T CastTo<T>(this dynamic obj, bool safeCast) where T:class
{
   try
   {
      return (T)obj;
   }
   catch
   {
      if(safeCast) return null;
      else throw;
   }
}

これは拡張メソッド形式であるため、その使用法は動的オブジェクトのメンバーであるかのようになります。

dynamic myDynamic = new Something();
var typedObject = myDynamic.CastTo<Something>(false);

編集:Grr、それを見ませんでした。はい、反射的にジェネリックを閉じることができ、非ジェネリック拡張メソッドで隠すことは難しくありません:

public static dynamic DynamicCastTo(this dynamic obj, Type castTo, bool safeCast)
{
   MethodInfo castMethod = this.GetType().GetMethod("CastTo").MakeGenericMethod(castTo);
   return castMethod.Invoke(null, new object[] { obj, safeCast });
}

私はあなたがこれから何を得るのか分からないだけです。基本的には、ダイナミックを使用し、キャストをリフレクトされた型に強制し、それをダイナミックに戻します。たぶんあなたは正しい、私は尋ねるべきではありません。しかし、これはおそらくあなたが望むことをするでしょう。基本的にダイナミックランドに入ると、リフレクティブメソッドまたは試行錯誤によってオブジェクトが何であるかを発見できるため、ほとんどのキャスト操作を実行する必要がなくなります。そのため、エレガントな方法はあまりありません。

1
KeithS