it-swarm.com.ru

Создание объекта с типом, определенным во время выполнения

Я нахожусь в ситуации, когда я хотел бы создать экземпляр объекта типа, который будет определен во время выполнения. Мне также нужно выполнить явное приведение к этому типу.

Что-то вроде этого:

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);
}

Это явно недействительный код, но я надеюсь, что он передает суть того, что я пытаюсь сделать. Метод, над которым я на самом деле работаю, должен будет выполнить маршалинг для ~ 35 различных типов. У меня есть несколько других методов, которые должны будут сделать что-то подобное с тем же набором типов. Итак, я хотел бы изолировать логику определения типа от этих методов, чтобы мне нужно было написать ее только один раз, и чтобы методы оставались чистыми и читаемыми.

Я должен признать, что был полным новичком в дизайне. Кто-нибудь может предложить хороший подход к этой проблеме? Я подозреваю, что может быть соответствующий шаблон проектирования, о котором я не знаю.

58
Odrade

Есть несколько способов создать объект определенного типа на лету, один из них:

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

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

И вы получите экземпляр MyClass в obj.

Другой способ заключается в использовании отражения:

// 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[] { });

А из одного возвращенного ConstructorInfo вы можете "Invoke ()" его с аргументами и вернуть экземпляр класса, как если бы вы использовали оператор "new".

113
chakrit

Вы можете в основном делать то, что вы описываете, но так как вы не знаете тип во время компиляции, вам придется сохранить экземпляр слабо типизированных; проверяйте его тип в каждой точке, в которой вы его используете, и затем преобразуйте его соответствующим образом (это не будет необходимо в c # 4.0, который поддерживает dynamic ):

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

Я думаю, что вы ищете Activator.CreateInstance

10
Jason Punyon

Создать экземпляр среды выполнения, определенной Type, легко, используя Activator.CreateInstance, как уже упоминали другие. Однако приведение к нему, как вы делаете в своем примере в строке Marshal.PtrToStructure, невозможно, так как тип должен быть известен во время компиляции для преобразования. Также обратите внимание, что Activator.CreateInstance нельзя использовать вместе с IntPtr.

Если у ваших типов есть общий базовый класс (отличный от Object), вы можете привести его к указанному базовому типу и вызвать его функции. В противном случае вызов функций будет возможен только с помощью отражения.

Так что либо:

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();
}

Или же:

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

Вы можете пойти динамически:

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