web-dev-qa-db-fra.com

Instancier un objet avec un type déterminé par l'exécution

Je suis dans une situation où j'aimerais instancier un objet d'un type qui sera déterminé lors de l'exécution. J'ai également besoin d'effectuer un cast explicite sur ce type.

Quelque chose comme ça:

static void castTest(myEnum val)
{
    //Call a native function that returns a pointer to a structure
    IntPtr = someNativeFunction(..params..);

    //determine the type of the structure based on the enum value
    Type structType = getTypeFromEnum(val);

    structType myStruct = (structType)Marshal.PtrToStructure(IntPtr, structType);
}

Ce n'est évidemment pas un code valide, mais j'espère qu'il transmet l'essence de ce que j'essaie de faire. La méthode sur laquelle je travaille actuellement devra effectuer l'opération de marshaling sur environ 35 types différents. J'ai plusieurs autres méthodes qui devront faire quelque chose de similaire avec le même ensemble de types. Donc, je voudrais isoler la logique de détermination de type de ces méthodes afin que je n'ai besoin de l'écrire qu'une seule fois, et que les méthodes restent propres et lisibles.

Je dois admettre que je suis totalement novice en design. Quelqu'un pourrait-il suggérer une bonne approche à ce problème? Je soupçonne qu'il pourrait y avoir un modèle de conception approprié que je ne connais pas.

59
Odrade

Il existe plusieurs façons de créer un objet d'un certain type à la volée, l'une est:

// determine type here
var type = typeof(MyClass);

// create an object of the type
var obj = (MyClass)Activator.CreateInstance(type);

Et vous obtiendrez une instance de MyClass dans obj.

Une autre façon est d'utiliser la réflexion:

// get type information
var type = typeof(MyClass);

// get public constructors
var ctors = type.GetConstructors(BindingFlags.Public);

// invoke the first public constructor with no parameters.
var obj = ctors[0].Invoke(new object[] { });

Et à partir de l'une des informations ConstructorInfo renvoyées, vous pouvez "l'invoquer ()" avec des arguments et récupérer une instance de la classe comme si vous aviez utilisé un "nouveau" opérateur.

116
chakrit

Vous pouvez principalement faire ce que vous décrivez, mais comme vous ne connaissez pas le type au moment de la compilation, vous devrez conserver l'instance lâchement tapé; vérifiez son type à chaque point où vous l'utilisez, et convertissez-le de manière appropriée (cela ne sera pas nécessaire avec c # 4.0, qui prend en charge dynamique ):

Type type = CustomGetTypeMethod();
var obj = Activator.CreateInstance(type);

...


if(obj is MyCustomType)
{
    ((MyCustomType)obj).Property1;
}
else if (obj is MyOtherCustomType)
{
    ((MyOtherCustomType)obj).Property2;
}
14
Rex M

Je pense que vous cherchez Activator.CreateInstance

10
Jason Punyon

La création d'une instance d'une exécution déterminée Type est facile, en utilisant Activator.CreateInstance, comme d'autres l'ont mentionné. Cependant, le lancer, comme vous le faites dans votre exemple sur le Marshal.PtrToStructure la ligne n'est pas possible, car le type doit être connu au moment de la compilation pour le transtypage. Notez également que Activator.CreateInstance ne peut pas être utilisé avec un IntPtr.

Si vos types ont une classe de base commune (autre que Object), vous pouvez la convertir en ce type de base et appeler des fonctions sur celle-ci. Sinon, les fonctions d'appel ne seront possibles qu'en utilisant la réflexion.

Donc soit:

static void castTest(myEnum val)
{
  //Call a native function that returns a pointer to a structure
  IntPtr val = someNativeFunction(..params..);

  //determine the type of the structure based on the enum value
  Type structType = getTypeFromEnum(val);

  BaseClass myStruct = (BaseClass)Marshal.PtrToStructure(IntPtr, structType);
  myStruct.SomeFunctionDeclaredInBaseClass();
}

Ou:

static void castTest(myEnum val)
{
  //Call a native function that returns a pointer to a structure
  IntPtr val = someNativeFunction(..params..);

  //determine the type of the structure based on the enum value
  Type structType = getTypeFromEnum(val);

  object myStruct = Marshal.PtrToStructure(IntPtr, structType);
  MemberInfo[] function = FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.Instance,
    (MemberFilter)delegate(MemberInfo info, object filter)
    {
      return info.Name == filter.ToString();
    }, "SomeFunction");
  if (mi.Length > 0 && mi[0] is MethodInfo)
    ((MethodInfo)mi[0]).Invoke(myStruct, ..params..);
}
6
Aistina

Vous pouvez devenir dynamique:

using System;

namespace TypeCaster
{
    class Program
    {
        internal static void Main(string[] args)
        {
            Parent p = new Parent() { name = "I am the parent", type = "TypeCaster.ChildA" };
            dynamic a = Convert.ChangeType(new ChildA(p.name), Type.GetType(p.type));
            Console.WriteLine(a.Name);

            p.type = "TypeCaster.ChildB";
            dynamic b = Convert.ChangeType(new ChildB(p.name), Type.GetType(p.type));
            Console.WriteLine(b.Name);
        }
    }

    internal class Parent
    {
        internal string type { get; set; }
        internal string name { get; set; }

        internal Parent() { }
    }

    internal class ChildA : Parent
    {
        internal ChildA(string name)
        {
            base.name = name + " in A";
        }

        public string Name
        {
            get { return base.name; }
        }
    }

    internal class ChildB : Parent
    {
        internal ChildB(string name)
        {
            base.name = name + " in B";
        }

        public string Name
        {
            get { return base.name; }
        }
    }
}
1
Jakob Flygare