5

この種のコードの用語はわかりませんが、括弧の後に変数をインスタンス化できるかどうかを知りたいのですが、リフレクションを使用しています。

XMLファイルからロードされるマップがあります。これは(int X、int Y、string S)のコレクションであり、X、Yはある地形の位置であり、Sは地形のタイプを表す文字列です。文字列と関連するタイプの間を渡すための辞書があります。たとえば、1つのキーと値のペアは「Tree」、typeof(Tree)の場合があります。

リフレクションを使用する場合、パラメーターを使用してインスタンス化できることはわかっていますが、私が快適に使用できる唯一の方法は、Activator.CreateInstance(Type t)を使用すること、つまり空のコンストラクターを使用することです。

マップをハードコーディングした場合、元々は次のようにインスタンス化されていました(i、j forループ内):

case: "Tree"
 world.Add( new Tree(i,j) );

リフレクションと保存ファイルについて考え始めたときに、これを次のように変更しました。

world.Add( new Tree() { X = i, Y = j }

ただし、これはリフレクションでは機能しないことに気付いたので、次のことを行う必要があります(TreeはTerrainから継承し、ディクショナリはXML保存データ文字列をタイプに変換するだけです)。

Type type = dictionary[dataItem.typeAsString];
Terrain t = (Terrain)Activator.CreateInstance(type);
t.X = i;
t.Y = j;
world.Add(t);

私はこれを次のようなものを使用して行いたいと思います

Type type = dictionary[dataItem.typeAsString];
world.Add((Terrain)Activator.CreateInstance(type) { X = i, Y = j }

このようなショートカットはありますか?ワールドを編集できない場合は、追加してXとYを取得し、そこにある地形にキャストしてこれらの変数にアクセスできると思いますが、a)この{var1 = X、var2=Y}プログラミングの名前についてはまだ興味があります。 、およびb)反射を使用するときに類似したものが存在するかどうか。

4

5 に答える 5

5

この構文はオブジェクト初期化構文と呼ばれ、プロパティを設定するための単なる構文糖衣です。

コードvar result = new MyType { X = x }は次のようにコンパイルされます。

MyType __tmp = new MyType();
__tmp.X = x;
MyType result = __tmp;

インスタンス化された型が実行時にのみわかっている場合は自分でそれを行う必要がありPropertyInfo.SetValue、型がコンパイル時にわかっている場合は通常のプロパティセッターを使用する必要があります。

于 2013-01-16T16:09:43.763 に答える
3

あなたが言及したオブジェクト初期化構文(3.0のLINQで導入された)はコンパイラーの幻想であるため、答えはノーです。のように、これを入力すると

var foo = new Foo { Bar = "baz" };

コンパイラは実際にそれをCLS準拠のILに変換します。

var foo = new Foo();
foo.Bar = "baz";

Phil Haackには、コンパイラによって行われたこの書き換えの詳細だけでなく、実装する型を処理するときに発生する可能性のあるいくつかの副作用についても説明したすばらしいブログ投稿があります。IDisposable

これはすべてコンパイラによるフェイントにすぎないため、リフレクション(つまりActivator.CreateInstance(Type t))を使用する同等のものはありません。他の人はあなたに回避策を与えるでしょう、しかし結局、直接同等のものはありません。

おそらく、管理できる最も近い一般的なハックは、オブジェクトを受け入れるメソッドを作成し、リフレクションを使用してそのオブジェクトのプロパティとそれぞれの値を識別し、オブジェクトの初期化を実行することです。このようなものを使用する可能性があります

var foo = Supercollider.Initialize<Foo>(new { Bar = "baz" });

コードは次のようになります(これは私の頭から離れています)

public sealed class Supercollider
{    
    public static T Initialize<T>(object propertySource)
    {
        // you can provide overloads for types that don't have a default ctor
        var result = Activator.CreateInstance(typeof(T)); 
        foreach(var prop in ReflectionHelper.GetProperties(typeof(T)))
            ReflectionHelper.SetPropertyValue(
                result, // the target
                prop,  // the PropertyInfo 
                propertySource); // where we get the value
    }    
}

匿名オブジェクトから各プロパティを取得し、同じ名前とタイプのターゲットタイプのプロパティを見つけてから、匿名オブジェクトのそのプロパティから値を取得し、ターゲットのプロパティの値をこの値に設定する必要があります。 。信じられないほど難しいことではありませんが、実行時の例外や、コンパイラが匿名型のプロパティに別の型を選択するという問題が発生しやすく、より具体的にする必要があります(たとえば、new { Bar = (string)null })。

于 2013-01-16T16:15:15.983 に答える
2
(T)Activator.CreateInstance(typeof(T), param1, param2, ...);

ここで説明されているように。

于 2013-01-16T16:09:57.413 に答える
2
public sealed class ReflectionUtils
    {

        public static T ObjectInitializer<T>(Action<T> initialize)
        {
            var result = Activator.CreateInstance<T>();
            initialize(result);
            return result;
        }
    }

public class MyModel
{
    public string Name{get;set;}
}

そしてその後、電話をかけるだけです:

var myModel = ReflectionUtils.ObjectInitializer<MyModel>(m => 
   {
     m.Name = "Asdf"
   });

利点は、このようにして型安全性が得られ、必要最小限にリフレクションを使用できることです。リフレクションはコストのかかる操作であり、可能な限り回避する必要があることは誰もが知っているからです。

于 2017-03-31T13:20:23.000 に答える
0

これらの引数を取るコンストラクターを作成してから、

Activator.CreateInstance(type, i, j)

ただし、オブジェクト初期化構文を使用することはできません。これは、プロパティを設定するための単なる砂糖菓子です。

于 2013-01-16T16:13:19.830 に答える