web-dev-qa-db-ja.com

アクティベーターを使用して汎用タイプのインスタンスを作成し、そのタイプにキャストする方法は?

ジェネリック型Store<T>があり、Activatorを使用してこの型のインスタンスを作成します。アクティベーターを使用した後、どのようにしてobject型の結果オブジェクトをインスタンス化された型にキャストできますか?ジェネリックのインスタンス化に使用した型を知っています。次のコードをご覧ください。

class Store<T> where T : IStorable 
{}

class Beer : IStorable 
{}

class BeerStore : Store<Beer>
{}

Type storeType = someObjectThatImplementsIStorable.GetType();
Type classType = typeof(Store<>);
Type[] typeParams = new Type[] { storeType };   
Type constructedType = classType.MakeGenericType(typeParams);

object x = Activator.CreateInstance(constructedType, new object[] { someParameter });

私がやりたいのは次のようなものです:

var store = (Store<typeof(objectThatImplementsIStorable)>)x;

しかし、それは明らかな理由で機能しません。代わりとして、私は試しました:

var store = (Store<IStorable>)x;

おそらく私の意見では機能するかもしれませんが、InvalidCastExceptionを提供します。

オブジェクトxにあることがわかっているStore<T>メソッドに再度アクセスするにはどうすればよいですか?

35
Bazzz

実際の型Tはリフレクションを介してのみ使用できるため、リフレクションを介してStore<T>のメソッドにもアクセスする必要があります。

Type constructedType = classType.MakeGenericType(typeParams);

object x = Activator.CreateInstance(constructedType, new object[] { someParameter });
var method = constructedType.GetMethod("MyMethodTakingT");
var res = method.Invoke(x, new object[] {someObjectThatImplementsStorable});

[〜#〜] edit [〜#〜]ジェネリックを使用せず、代わりにIStoreを使用する追加のIStorableインターフェイスを定義することもできます。

interface IStore {
    int CountItems(IStorable item);
}
class Store<T> : IStore where T : IStorable {
    int CountItems(IStorable item) {
        return count;
    }
}

Store<T>は汎用のままですが、CountItemsにキャストすることでIStoreにアクセスできます:

var x = (IStore)Activator.CreateInstance(constructedType, new object[] { someParameter });
var count = x.CountItems((IStorable)someObjectThatImplementsStorable);
30
dasblinkenlight

あなたはそれを包むだけですか?

何かのようなもの

public Store<T> IConstructStore<T>(T item) where T : IStorable 
{
 return Activator.CreateInstance(typeof(Store<T>), new object[] { someParameter }) as Store<T>;
}

または私はあなたがやろうとしていることを逃していますか?

IE

class Program
{
    static void Main(string[] args)
    {
        Beer b = new Beer();
        var beerStore = IConstructStore(b);
        Console.WriteLine(beerStore.test);
        Console.WriteLine(beerStore.GetType().ToString());
    }

    public static Store<T> IConstructStore<T>(T item) where T : IStorable
    {
        return Activator.CreateInstance(typeof(Store<T>), new object[] { }) as Store<T>;
    }
}

interface IStorable { }

class Store<T> where T : IStorable
{
    public int test = 1;
}

class Beer : IStorable
{ }

プリント

1 
ConsoleApp1.Store'1[ConsoleApp1.Beer]
4
Luke McGregor

私の意見で最も適切な答えは、「この方法ではできない」でしょう。

インターフェイスIStorageを導入して、共変または反変にできます(そのオプションを見たことはありますか?)。オプションではない場合、たとえばStorageで使用される入力と出力の両方のジェネリック型がある場合、必要なものを実装する方法はありません。その理由は、次の理由により、Storage<Beer>Storage<IStorable>として安全に使用できないためです。

Storage<IStorable> store = new Storage<Beer>(); // let's pretend we can do it 
store.Save(new StorableButNotBeer()); // what will happen here?

私が見るようにあなたのための唯一の可能な回避策は、このメソッドからキャストを移動し、すべての正確なタイプを知っている場所にオブジェクトをキャストすることです:

public void object CreateStore(Type istorableType)
{
    // here is your activator code, but you will have to return an object
}

var beerStore = (Store<Beer>)CreateStore(typeof(Beer));
3
Snowbear

Tはtypeof(Storeの使用を回避するタイプStoreでなければなりません

SomeObjectThatImplementsIStorableのタイプがMyStorableであるとしましょう。

例えばMyStorable someObjectThatImplementsIStorable = new MyStorable(); ... //ここにコードの残りの部分。

その場合、xはStoreにキャストできませんが、Storeにキャストできます。以下が機能します:(Store)x

MyStorableはIStorableを実装していますが、StoreとStoreの間に関係はありません。これらは、互いに派生しない2つの異なるクラスです。

あなた.

0
Uri