2011-05-06 6 views
9

HI, Tengo un requisito para crear una instancia para el objeto list en tiempo de ejecución mediante la reflexión. Por ejemplo tengo 2 clases como a continuación,Cómo crear la instancia de la lista <T> en el archivo C# utilizando Reflection

class Class1 
{ 
    List<Class2> class2List; 
    public List<Class2> Class2List 
    { 
     get;set; 
    } 
} 
class Class2 
{ 
    public string mem1; 
    public string mem2; 
} 

después de crear la instancia de Class1 en tiempo de ejecución en otra clase Class3, quiero asignar valores a todas las propiedades de la clase. En este caso, Class2List es una propiedad de List<Class2>. En Runtime, no sé el tipo de clase List<Class2>. ¿Cómo puedo inicializar la propiedad, es decir, List<Class2> dentro de class3 en tiempo de ejecución?

Cualquier sugerencia son muy apreciadas ...

Respuesta

17

En lugar de cuestionar sus motivos o tratar de deshacer lo que está haciendo - Yo sólo voy a responder a la pregunta del título .

dado tienen una instancia de tipo listElemType que representa el argumento de tipo que se va a pasar al tipo List<> en tiempo de ejecución:

var listInstance = (IList)typeof(List<>) 
    .MakeGenericType(listElemType) 
    .GetConstructor(Type.EmptyTypes) 
    .Invoke(null); 

y luego se puede trabajar con la lista a través de su IList implementación de la interfaz.

O, de hecho, puede detenerse en la llamada MakeGenericType y usar el tipo que genera en una llamada al Activator.CreateInstance - como en la respuesta de Daniel Hilgarth.

Entonces, dado un objeto target cuya propiedad que desea establecer:

object target; //the object whose property you want to set 
target.GetType() 
    .GetProperty("name_of_property")  //- Assuming property is public 
    .SetValue(target, listInstance, null); //- Assuming .CanWrite == true 
              // on PropertyInfo 

Si usted no sabe las propiedades del tipo representado por target, entonces es necesario utilizar

target.GetType().GetProperties(); 

para obtener todas las propiedades públicas de esa instancia. Sin embargo, solo el hecho de poder crear una instancia de la lista realmente no va a poder ayudarlo allí; tendrá que tener una solución más genérica que pueda adaptarse a cualquier tipo. A menos que vayas a apuntar específicamente a tipos de listas.

Me parece que es posible que tenga una interfaz común o base ...

+0

En lugar de crear matrices de longitud cero para las llamadas GetConstructor e Invoke, puede usar 'Type.EmptyTypes' para GetConstructor y pasar' null' a Invoke. Además, deberá convertir el objeto devuelto por Invoke: 'var listInstance = (IList) (typeof (List <>) .MakeGenericType (listElemType) .GetConstructor (new Type [0]) .Invoke (new object [0])); ' – phoog

+0

gracias - Sabía sobre' Type.EmptyTypes' y 'null' sobre' Invoke', pero simplemente lo estaba superando muy rápido ... Sin embargo, el molde que faltaba era porque pensaba agregar el IList poco después de haber escrito el código! Se actualizará. –

+0

justo como lo que estoy buscando – ArhiChief

0

¿Por qué necesita para utilizar la reflexión para establecer la propiedad?
Después de crear la instancia de Class1 simplemente puede establecer la propiedad:

Class1 instance = Activator.CreateInstanc<Class1>(); 
instance.Class2List = new List<Class2>(); 
+2

Gracias por la respuesta. Sin embargo, no sé qué propiedades tiene la clase1 en Runtime. Por ejemplo, le di Class2, en realidad no conozco el tipo de clase. Entonces, ¿cómo puedo hacer esto? – Ananya

0

Aquí una muestra (sin manejo de errores adecuado!) Que inicializa la propiedad Lista de Clase 1. ¿Qué desea rellenar/inicializar en una propiedad/campo de su objeto si tiene una interfaz? ¿Qué desea llenar para otros tipos de objetos (tal vez con más de 1 parámetro de constructor)?

Tal vez una inversión del contenedor de control que serviría en su solución (como por ejemplo Ninject http://ninject.org/, Spring.Net http://www.springframework.net/, Unidad http://unity.codeplex.com/) o los miembros ya están inicializado correctamente en los objetos que se están utilizando.

using System; 
using System.Collections.Generic; 

namespace ConsoleApplication3 
{ 
    public sealed class Class1 
    { 
     //[1]better solution (this way you wouldn't require reflection at all) 
     // private readonly List<Class2> _class2List = new List<Class2>(); 
     private List<Class2> _class2List; 
     public List<Class2> Class2List 
     { 
      get { return _class2List; } 
      set { 
        _class2List = value; //set not allowed if you apply [1] 
       } 
     } 
     public string TestPropertyToIgnore { get; set; } 
    } 

    public class Class2 
    { 
     public string Mem1; 
     public string Mem2; 
    } 


    class Program 
    { 
     static void Main() 
     { 
      var typeClass1 = Type.GetType("ConsoleApplication3.Class1"); 
      var objectClass1 = Activator.CreateInstance(typeClass1); 

      foreach(var property in objectClass1.GetType().GetProperties()) 
      { 
       var propertyType = property.PropertyType; 
       if (!propertyType.IsClass 
        || !property.CanRead 
        || !property.CanWrite 
        || property.GetValue(objectClass1, null) != null 
        || !IsGenericListOfT(propertyType) 
        ) 
       { 
        continue; 
       } 

       property.SetValue(objectClass1, Activator.CreateInstance(propertyType), null); 

      } 

      //this would raise a NullReference exception if list is still null 
      Console.WriteLine(((Class1) objectClass1).Class2List.Count); 
     } 

     private static bool IsGenericListOfT(Type propertyType) 
     { 
      return propertyType.IsGenericType 
        && propertyType.GetGenericTypeDefinition() == typeof (List<>); 
     } 
    } 
} 
Cuestiones relacionadas