29
votes

I need to cast a property to its actual type dynamically. How do I/Can I do this using reflection?

To explain the real scenario that I am working on a bit. I am trying to call the "First" extension method on an Entity Framework property. The specific property to be called on the Framework context object is passed as a string to the method (as well as the id of the record to be retrieved). So I need the actual type of the object in order to call the First method.

I can't use the "Where" method on the object as the lambda or delegate method still needs the actual type of the object in order to access the properties.

Also as the object is generated by the Entity Framework I can't cast the type to an interface and operate on that.

This is scenario code:

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
        }
    }
}
7
How would you cast it to a type you don't know? You know it at runtime. So you can only handle it as an object.Stefan Steinegger
Could you explain a little more your scenario? Suppose there was a way to do it dynamically (actually there isn't but...) what would you do next with this variable? You cannot call .IntProperty because you don't know its type, so back to the beginning there's no need to cast it to ClassA. You just continue with reflection to get IntProperty and so on...Darin Dimitrov
Casting is largely a compile-time thing, not really a reflection thing. What exactly do you mean to do, to "cast the property to its actual type"? There are some options involving generics, but without a clear idea what you want to achieve it is hard to give a good answer. Even with generics (via MakeGenericMethod etc), you'd just have it as a T - but that doesn't actually let you do much with it... So: what do you want to do once you have it typed...Marc Gravell♦
Can you clarify your question. Because I couldn't understand it properly.Paulo Santos
There is no true dynamic casting in the framework. Typically problems like this are solved using interfaces or generics.Robert

7 Answers

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

I had some time so I tried to solve my problem using VS2010 and I think I was right previously when I though that the dynamic keywork would 'solve' my question. See the code below.

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
votes

Once you get the reflected type, you can use Convert.ChangeType().

1
votes

Having a variable of a specific type is really only useful at compile time, and will not help you at runtime in using it in this way. Try to write the code where you would utilize this... you'll find that it keeps pushing the requirement to know the type to compile time at some level (maybe further up the call chain, but you'll still eventually need to type the concrete type for this to be useful).

One thing to keep in mind, though - if your type is a reference type, the object is still truly the type you've created. It's not like there is a benefit to having the object saved as your type vs. object. This is the beauty of reflection (as well as part of why it works). There really is no reason to try to "change" it's type at runtime in a cast, since it's still going to be an object.

1
votes

Though I should post the solution to the real world problem.

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;

I think the new CLR4 dynamic keywork might be the "nice" solution.

Thanks for all the responces.

1
votes

How about setting the root value as a string and then carry it around as a string until you need to convert it to the target type?

0
votes
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 );

//return instance;