web-dev-qa-db-fra.com

Transforme une propriété à son type actuel de manière dynamique à l'aide de la réflexion

Je dois lancer dynamiquement une propriété sur son type actuel. Comment puis-je/puis-je faire cela en utilisant la réflexion?

Pour expliquer le scénario réel sur lequel je travaille un peu. J'essaie d'appeler la méthode "First" extension sur une propriété Entity Framework. La propriété spécifique à appeler sur l'objet de contexte Framework est transmise à la méthode sous forme de chaîne (ainsi que l'identifiant de l'enregistrement à extraire). J'ai donc besoin du type réel de l'objet pour appeler la méthode First. 

Je ne peux pas utiliser la méthode "Where" sur l'objet car la méthode lambda ou déléguée a toujours besoin du type réel de l'objet pour accéder aux propriétés.

De plus, comme l'objet est généré par Entity Framework, je ne peux pas transtyper le type sur une interface et opérer sur celle-ci.

C'est le code du scénario:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Reflection;

namespace NmSpc
{

    public class ClassA
    {
        public int IntProperty { get; set; }
    }

    public class ClassB
    {
        public ClassA MyProperty { get; set; }
    }

    public class ClassC
    {
        static void Main(string[] args)
        {
            ClassB tester = new ClassB();

            PropertyInfo propInfo = typeof(ClassB).GetProperty("MyProperty");
            //get a type unsafe reference to ClassB`s property
            Object property = propInfo.GetValue(tester, null);

            //get the type safe reference to the property
            ClassA typeSafeProperty = property as ClassA;

            //I need to cast the property to its actual type dynamically. How do I/Can I do this using reflection?
            //I will not know that "property" is of ClassA apart from at runtime
        }
    }
}
25
Keith K

J'avais un peu de temps alors j'ai essayé de résoudre mon problème en utilisant VS2010 et je pense avoir eu raison précédemment lorsque je pensais que le travail de clé dynamique «résoudrait» ma question. Voir le code ci-dessous.

using System.Reflection;

namespace TempTest
{
    public class ClassA
    {
        public int IntProperty { get; set; }
    }

    public class ClassB
    {
        public ClassB()
        {
            MyProperty = new ClassA { IntProperty = 4 };
        }
        public ClassA MyProperty { get; set; }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            ClassB tester = new ClassB();

            PropertyInfo propInfo = typeof(ClassB).GetProperty("MyProperty");
            //get a type unsafe reference to ClassB`s property
            dynamic property = propInfo.GetValue(tester, null);

            //casted the property to its actual type dynamically
            int result = property.IntProperty; 
        }
    }
}
5
Keith K
public object CastPropertyValue(PropertyInfo property, string value) { 
if (property == null || String.IsNullOrEmpty(value))
    return null;
if (property.PropertyType.IsEnum)
{
    Type enumType = property.PropertyType;
    if (Enum.IsDefined(enumType, value))
        return Enum.Parse(enumType, value);
}
if (property.PropertyType == typeof(bool))
    return value == "1" || value == "true" || value == "on" || value == "checked";
else if (property.PropertyType == typeof(Uri))
    return new Uri(Convert.ToString(value));
else
    return Convert.ChangeType(value, property.PropertyType);  }
31
Nisus

Une fois que vous avez obtenu le type reflété, vous pouvez utiliser Convert.ChangeType().

3
Marty

Bien que je devrais poster la solution au problème du monde réel.

string objectType = "MyProperty";

using (MyEntitiesContext entitiesContext = new MyEntitiesContext())
{
try
{
    string queryString = @"SELECT VALUE " + objectType+  " FROM MyEntitiesContext." + objectType + " AS " + objectType + " WHERE " + objectType + ".id = @id";

    IQueryable<Object> query = entitiesContext.CreateQuery<Object>(queryString, new ObjectParameter("id", objectId));

    foreach (Object result in query)
    {
        return result;
    }
}
catch (EntitySqlException ex)
{
    Console.WriteLine(ex.ToString());
}
}

return null;

Je pense que le nouveau système de clé dynamique CLR4 pourrait être la solution "agréable".

Merci pour tous les responces.

1
Keith K

Avoir une variable d'un type spécifique n'est vraiment utile qu'au moment de la compilation et ne vous aidera pas au moment de son utilisation à l'utiliser de cette façon. Essayez d’écrire le code dans lequel vous utiliseriez ceci ... vous constaterez qu’il continue à insister sur la nécessité de connaître le type pour compiler le temps à un certain niveau (peut-être plus en amont de la chaîne d’appel, mais vous aurez toujours besoin de taper le type concret pour que cela soit utile).

Une chose à garder à l’esprit cependant: si votre type est un type de référence, l’objet est toujours le type que vous avez créé. Ce n'est pas comme s'il y avait un avantage à enregistrer l'objet en tant que type ou objet. C'est la beauté de la réflexion (ainsi qu'une partie de la raison pour laquelle cela fonctionne). Il n'y a vraiment aucune raison d'essayer de "changer" son type au moment de l'exécution dans un casting, car ce sera toujours un objet.

1
Reed Copsey

Pourquoi ne pas définir la valeur racine en tant que chaîne, puis la transporter sous forme de chaîne jusqu'à ce que vous ayez besoin de la convertir en type cible?

1
euwe
Type resultType = typeof(T);
IEnumerable<PropertyDescriptor> properties = TypeDescriptor.GetProperties(resultType).Cast<PropertyDescriptor>();

object instance = Activator.CreateInstance(resultType);

var objValue = "VALUE FOR HEADER";
var resultHeader = "HeaderName";
var prop = properties.Single(p => string.Equals(p.Name, resultHeader, StringComparison.InvariantCultureIgnoreCase));

var targetType = Nullable.GetUnderlyingType(prop.PropertyType) !=null Nullable.GetUnderlyingType(prop.PropertyType):prop.PropertyType;

objValue  = Convert.ChangeType(objValue , targetType);
prop.SetValue(instance, objValue );

// retourne une instance;

0
user8097903