【问题标题】:Cast dynamic object to type using reflection c#使用反射c#将动态对象转换为类型
【发布时间】:2016-01-02 11:30:09
【问题描述】:

考虑下面的代码

 var currentType = Type.GetType("Some.Type, Some");
 dynamic myDynamic = new System.Dynamic.ExpandoObject();
 myDynamic.A = "A";
 var objectInCorrectType = ???

如何将动态转换为 currentType?

【问题讨论】:

  • 你不能。你要么做一个重新解释转换,基本上说“我知道这个引用实际上是一个伪装成 Y 的 X,所以为我把它转换为 X,将引用重新解释为对 X 的引用”,或者你进行一个涉及代码的转换进行转换,例如创建新的 X 和复制值等。没有内置方法可以将 ExpandoObject 转换或强制转换为某种特定类型,您需要自己构建。

标签: c# reflection


【解决方案1】:

正如@Lasse 评论的那样,您不能将动态对象转换为特定类型。

但是,您的问题提到了“反射”,所以我怀疑您正在寻找一种方法来简单地映射属性值(即 Lasse 的评论中的“创建新的 X 并复制值等”):

...
myDynamic.A = "A";

// get settable public properties of the type
var props = currentType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
    .Where(x => x.GetSetMethod() != null);

// create an instance of the type
var obj = Activator.CreateInstance(currentType);

// set property values using reflection
var values = (IDictionary<string,object>)myDynamic;
foreach(var prop in props)
    prop.SetValue(obj, values[prop.Name]);

【讨论】:

  • 我将排除 properties.Where(p => p.GetIndexParameters().Length > 0) 或者您还将包括索引器。而且我也相信 GetSetMethod 不能保证是公开的,即使您请求了 BindingFlags.Public,因为属性是 getter 和 setter 方法的代理,它们可能具有不同的访问修饰符。
【解决方案2】:

dynamicduck-typing 一个变量(即将类型检查延迟到运行时)。它仍然拥有一个类型化的对象,但在编译时不会检查它。

因此,由于 ExpandoObject 是一种类型,如果将其分配给类型化或动态引用,则不能将 ExpandoObject 强制转换或转换为类型,因为它与目标类型共享相同的成员.

顺便说一句,由于ExpandoObject 实现了IDictionary&lt;string, object&gt;,您可以实现某种从ExpandoObject 实例到成员匹配的目标类型的动态映射作为扩展方法:

public static class ExpandObjectExtensions
{
    public static TObject ToObject<TObject>(this IDictionary<string, object> someSource, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public)
           where TObject : class, new ()
    {
        Contract.Requires(someSource != null);
        TObject targetObject = new TObject();
        Type targetObjectType = typeof (TObject);

        // Go through all bound target object type properties...
        foreach (PropertyInfo property in 
                    targetObjectType.GetProperties(bindingFlags))
        {
            // ...and check that both the target type property name and its type matches
            // its counterpart in the ExpandoObject
            if (someSource.ContainsKey(property.Name) 
                && property.PropertyType == someSource[property.Name].GetType())
            {
                property.SetValue(targetObject, someSource[property.Name]);
            }
        }

        return targetObject;
    }
}

现在,试试下面的代码,它会像你期望的那样工作:

public class A 
{
    public int Val1 { get; set; }
}

// Somewhere in your app...
dynamic expando = new ExpandoObject();
expando.Val1 = 11;

// Now you got a new instance of A where its Val1 has been set to 11!
A instanceOfA = ((ExpandoObject)expando).ToObject<A>();

实际上,我已将这个答案建立在其他问答的基础上,在那里我可以解决将对象映射到字典的类似问题,反之亦然:Mapping object to dictionary and vice versa

【讨论】:

  • 这正是我想要的,谢谢。
【解决方案3】:

我遇到这个问题是因为我需要参加这样的课程:

public class PropertyChange
{
    [JsonProperty("name")]
    public string PropertyName { get; set; }

    [JsonProperty("value")]
    public string PropertyValue { get; set; }

    [JsonProperty("arrayValue")]
    public dynamic[] PropertyArray { get; set; }
}

并将对象的 PropertyArray 属性(使用 Newtonsoft 库从 JSON 反序列化)转换为特定类型的对象数组,其中类型可以从 PropertyName 派生。

我编写了这个名为 DynamicCast&lt;&gt; 的帮助程序类,并决定将其发布在此处,以防其他人遇到与我相同的情况。

这个帮助类允许你编写如下代码:

public class MyType
{
    public string A { get; set; }
}

var myCast = new DynamicCast<MyType>();

dynamic dyn = ExpandoObject();
dyn.A = "Hello";

var myType = myCast.Cast(dyn);
Console.WriteLine(myType.A); // prints 'Hello'

这是我如何使用它来解决反序列化问题的示例:

public class JsonTest
{
    [JsonProperty("theArray")]
    public dynamic[] TheArray { get; set; }
}

var json = "{'theArray':[{'a':'First'},{'a':'Second'}]}";
var jsonTest = JsonConvert.DeserializeObject<JsonTest>(json);

var myCast = new DynamicCast<MyType>();
var myTypes = myCast.Cast(jsonTest.TheArray).ToArray();
Console.WriteLine(myTypes[0].A); // prints 'First'

根据此处的其他答案编写了DynamicCast 类。它看起来像这样:

public class DynamicCast<T> where T: class, new()
{
    private Property[] _proprties;

    public DynamicCast()
    {
        _proprties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(x => x.GetSetMethod() != null)
            .Where(x => x.GetGetMethod() != null)
            .Select(p =>
            {
                var property = new Property
                {
                    PropertyInfo = p,
                    Name = p.Name
                };
                foreach (var attribute in p.GetCustomAttributes(false))
                {
                    if (attribute.GetType() == typeof(JsonPropertyAttribute))
                    {
                        var jsonProperty = (JsonPropertyAttribute)attribute;
                        property.Name = jsonProperty.PropertyName;
                        break;
                    }
                    if (attribute.GetType() == typeof(JsonIgnoreAttribute))
                    {
                        return null;
                    }
                }
                return property;
            })
            .Where(p => p != null)
            .ToArray();
    }

    public T Cast(IDictionary<string, object> d)
    {
        var t = new T();
        Fill(d, t);
        return t;
    }

    public T Cast(JObject d)
    {
        var t = new T();
        Fill(d, t);
        return t;
    }

    public dynamic Cast(T t)
    {
        dynamic d = new ExpandoObject();
        Fill(t, d);
        return d;
    }

    public IEnumerable<T> Cast(IEnumerable<JObject> da)
    {
        return da.Select(e => Cast(e));
    }

    public IEnumerable<T> Cast(IEnumerable<object> da)
    {
        return da.Select(e =>
        {
            if (e is JObject) return Cast((JObject)e);
            if (e is IDictionary<string, object>) return Cast((IDictionary<string, object>)e);
            return null;
        });
    }

    public void Fill(IDictionary<string, object> values, T target)
    {
        foreach (var property in _proprties)
            if (values.TryGetValue(property.Name, out var value))
                property.PropertyInfo.SetValue(target, value, null);
    }

    public void Fill(JObject values, T target)
    {
        foreach (var property in _proprties)
        {
            if (values.TryGetValue(property.Name, out var value))
            {
                if (value is JValue jvalue)
                {
                    var propertyValue = Convert.ChangeType(jvalue.Value, property.PropertyInfo.PropertyType);
                    property.PropertyInfo.SetValue(target, propertyValue, null);
                }
            }
        }
    }

    public void Fill(T obj, IDictionary<string, object> target)
    {
        foreach (var property in _proprties)
            target[property.Name] = property.PropertyInfo.GetValue(obj, null);
    }

    private class Property
    {
        public PropertyInfo PropertyInfo;
        public string Name;
    }
}

您可以在 .Net Fiddle 中亲自尝试一下:https://dotnetfiddle.net/J1JXgU

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多