J'ai un type générique Store<T>
et utilisez Activator
pour créer une instance de ce type. Maintenant, comment, après avoir utilisé l'activateur, puis-je reconstituer l'objet résultant de type object
en type instancié? Je connais le type que j'ai utilisé pour instancier le générique. Veuillez consulter le code suivant:
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 });
Ce que je voudrais faire, c'est quelque chose comme ceci:
var store = (Store<typeof(objectThatImplementsIStorable)>)x;
mais cela ne fonctionne pas pour des raisons évidentes. Comme alternative, j'ai essayé:
var store = (Store<IStorable>)x;
qui pourrait éventuellement fonctionner à mon avis, mais donne un InvalidCastException
.
Comment puis-je accéder à nouveau au Store<T>
les méthodes que je connais sont dans l'objet x
?
Étant donné que le type réel T
n'est disponible que par réflexion, vous devez accéder aux méthodes de Store<T>
par la réflexion aussi:
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 Vous pouvez également définir une interface IStore
supplémentaire qui n'utilise pas de génériques et utilise à la place IStorable
:
interface IStore {
int CountItems(IStorable item);
}
class Store<T> : IStore where T : IStorable {
int CountItems(IStorable item) {
return count;
}
}
Votre Store<T>
resterait générique, mais vous auriez accès à son CountItems
en effectuant un cast vers IStore
:
var x = (IStore)Activator.CreateInstance(constructedType, new object[] { someParameter });
var count = x.CountItems((IStorable)someObjectThatImplementsStorable);
Vous ne pouvez pas simplement l'envelopper?
quelque chose comme
public Store<T> IConstructStore<T>(T item) where T : IStorable
{
return Activator.CreateInstance(typeof(Store<T>), new object[] { someParameter }) as Store<T>;
}
ou est-ce que je manque ce que vous essayez de faire?
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
{ }
impressions
1
ConsoleApp1.Store'1[ConsoleApp1.Beer]
La réponse la plus appropriée à mon avis serait "vous ne pouvez pas le faire de cette manière".
Vous pourriez essayer d'introduire une interface IStorage
et essayer de la rendre covariante ou contravariante (avez-vous vu cette option?). Si ce n'est pas une option, par exemple si vous avez à la fois des types génériques d'entrée et de sortie utilisés dans Storage
, il n'y a aucun moyen d'implémenter ce que vous voulez. La raison en est que Storage<Beer>
ne peut pas être utilisé en toute sécurité en tant que Storage<IStorable>
en raison de ce cas:
Storage<IStorable> store = new Storage<Beer>(); // let's pretend we can do it
store.Save(new StorableButNotBeer()); // what will happen here?
La seule solution de contournement possible pour vous, comme je le vois, consiste à déplacer la conversion de cette méthode et à lancer l'objet à l'endroit où vous connaissez tous les types exacts:
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));
T doit être le type Store en évitant l'utilisation de typeof (Store
Disons que someObjectThatImplementsIStorable est de type MyStorable.
par exemple. MyStorable someObjectThatImplementsIStorable = new MyStorable (); ... // reste de votre code ici.
Ensuite, x ne peut pas être converti en magasin, mais il peut être converti en magasin. Les éléments suivants fonctionneront: (Store) x
Notez que bien que MyStorable implémente IStorable, il n'y a aucune relation entre Store et Store. Ce sont deux classes distinctes qui ne dérivent pas l'une de l'autre.
u.