C#:动态运行时强制转换

时间:2011-02-07 19:43:30

标签: c# c#-4.0 casting

我想用以下签名实现一个方法

dynamic Cast(object obj, Type castTo);

任何人都知道该怎么做? obj肯定实现了castTo,但需要正确地进行转换才能使我的应用程序的某些运行时绑定工作正常。

编辑:如果某些答案没有意义,那是因为我最初不小心输入了dynamic Cast(dynamic obj, Type castTo); - 我的意思是输入应该是object或其他一些保证的基类

9 个答案:

答案 0 :(得分:78)

我认为你在这里混淆铸造和转换的问题。

  • 强制转换:更改指向对象的引用类型的行为。向上或向下移动对象层次结构或向已实现的接口移动
  • 转换:从不同类型的原始源对象创建新对象,并通过对该类型的引用访问它。

通常很难知道C#中的2之间的区别,因为它们都使用相同的C#运算符:强制转换。

在这种情况下,你几乎肯定不会寻找演员表演。将dynamic投射到另一个dynamic本质上是一种身份转换。它没有提供任何价值,因为您只是将dynamic引用返回到同一个底层对象。结果查找将没有什么不同。

相反,您在此方案中看起来需要的是转换。这是将底层对象变形为不同类型并以dynamic方式访问结果对象。最好的API是Convert.ChangeType

public static dynamic Convert(dynamic source, Type dest) {
  return Convert.ChangeType(source, dest);
}

编辑

更新后的问题有以下几行:

  

obj肯定实现了castTo

如果是这种情况,那么Cast方法不需要存在。可以简单地将来源object分配给dynamic引用。

dynamic d = source;

听起来您要尝试完成的是通过source引用在dynamic层次结构中查看特定接口或类型。这根本不可能。生成的dynamic引用将直接看到实现对象。它不会查看源层次结构中的任何特定类型。因此,在层次结构中转换为不同类型然后返回dynamic的想法与首先分配给dynamic完全相同。它仍将指向相同的基础对象。

答案 1 :(得分:28)

这应该有效:

public static dynamic Cast(dynamic obj, Type castTo)
{
    return Convert.ChangeType(obj, castTo);
}

修改

我写了以下测试代码:

var x = "123";
var y = Cast(x, typeof(int));
var z = y + 7;
var w = Cast(z, typeof(string)); // w == "130"

它确实类似于在PHP,JavaScript或Python等语言中发现的“类型转换”(因为它还值转换为所需类型)。我不知道这是不是一件好事,但它肯定有用......: - )

答案 2 :(得分:8)

我到目前为止最好:

dynamic DynamicCast(object entity, Type to)
{
    var openCast = this.GetType().GetMethod("Cast", BindingFlags.Static | BindingFlags.NonPublic);
    var closeCast = openCast.MakeGenericMethod(to);
    return closeCast.Invoke(entity, new[] { entity });
}
static T Cast<T>(object entity) where T : class
{
    return entity as T;
}

答案 3 :(得分:7)

开源框架Dynamitey有一个静态方法,它使用DLR进行后期绑定,包括others之间的强制转换。

dynamic Cast(object obj, Type castTo){
    return Dynamic.InvokeConvert(obj, castTo, explict:true);
}

这比使用反射调用的Cast<T>的优势在于,它也适用于具有动态转换运算符的任何IDynamicMetaObjectProvider ie。 Try DynamicObject

答案 4 :(得分:5)

我意识到这已得到回答,但我使用了不同的方法,并认为可能值得分享。此外,我觉得我的方法可能会产生不必要的开销。但是,我无法观察或计算在我们观察到的负载下发生的任何事情。我一直在寻找有关这种方法的任何有用的反馈。

使用动态的问题是您无法直接将任何函数附加到动态对象。你必须使用能够找出你不想每次都想弄清楚的作业的东西。

在规划这个简单的解决方案时,我查看了在尝试重新键入类似对象时有效的中间人。我发现二进制数组,字符串(xml,json)或硬编码转换(IConvertable)是常用的方法。由于代码可维护性因素和懒惰,我不想进入二进制转换。

我的理论是Newtonsoft可以通过使用字符串中介来实现这一点。

作为一个缺点,我相当肯定在将字符串转换为对象时,通过在当前程序集中搜索具有匹配属性的对象来使用反射,创建类型,然后实例化属性,这将需要更多反射。如果为真,那么所有这些都可以被认为是可以避免的开销。

C#:

//This lives in a helper class
public static ConvertDynamic<T>(dynamic data)
{
     return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(Newtonsoft.Json.JsonConvert.SerializeObject(data));
}

//Same helper, but in an extension class (public static class),
//but could be in a base class also.
public static ToModelList<T>(this List<dynamic> list)
{
    List<T> retList = new List<T>();
    foreach(dynamic d in list)
    {
        retList.Add(ConvertDynamic<T>(d));
    }
}

话虽如此,这符合我放在一起的另一个实用工具,它让我可以将任何对象变成动态。我知道我必须使用反射来正确地做到这一点:

public static dynamic ToDynamic(this object value)
{
    IDictionary<string, object> expando = new ExpandoObject();

    foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType()))
        expando.Add(property.Name, property.GetValue(value));

    return expando as ExpandoObject;
}

我必须提供这个功能。分配给动态类型变量的任意对象无法转换为IDictionary,并将破坏ConvertDynamic函数。对于要使用的此函数链,必须提供动态的System.Dynamic.ExpandoObject或IDictionary&lt; string,object&gt;。

答案 5 :(得分:1)

尝试通用:

public static T CastTo<T>(this dynamic obj, bool safeCast) where T:class
{
   try
   {
      return (T)obj;
   }
   catch
   {
      if(safeCast) return null;
      else throw;
   }
}

这是扩展方法格式,因此它的用法就像它是动态对象的成员一样:

dynamic myDynamic = new Something();
var typedObject = myDynamic.CastTo<Something>(false);
编辑:格尔,没有看到。是的,你可以反思地关闭泛型,并且不难以隐藏在非泛型扩展方法中:

public static dynamic DynamicCastTo(this dynamic obj, Type castTo, bool safeCast)
{
   MethodInfo castMethod = this.GetType().GetMethod("CastTo").MakeGenericMethod(castTo);
   return castMethod.Invoke(null, new object[] { obj, safeCast });
}

我只是不确定你会从中得到什么。基本上你是一个动态的,强迫一个演员到一个反射类型,然后把它填充回动态。也许你是对的,我不应该问。但是,这可能会做你想要的。基本上当你进入动态土地时,你就失去了执行大多数施法操作的需要,因为你可以通过反射方法或反复试验来发现一个物体是什么和做了什么,所以没有很多优雅的方法来做到这一点。 p>

答案 6 :(得分:1)

对@JRodd版本进行少量修改以支持来自Json(JObject)的对象

public static dynamic ToDynamic(this object value)
    {
        IDictionary<string, object> expando = new ExpandoObject();

        //Get the type of object 
        Type t = value.GetType();

        //If is Dynamic Expando object
        if (t.Equals(typeof(ExpandoObject)))
        {
            foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType()))
                expando.Add(property.Name, property.GetValue(value));
        }
        //If coming from Json object
        else if (t.Equals(typeof(JObject)))
        {
            foreach (JProperty property in (JToken)value)
                expando.Add(property.Name, property.Value);
        }
        else //Try converting a regular object
        {
            string str = JsonConvert.SerializeObject(value);
            ExpandoObject obj = JsonConvert.DeserializeObject<ExpandoObject>(str);

            return obj;
        }

       

        return expando as ExpandoObject;
    }

答案 7 :(得分:0)

您可以使用表达式管道来实现此目的:

 public static Func<object, object> Caster(Type type)
 {
    var inputObject = Expression.Parameter(typeof(object));
    return Expression.Lambda<Func<object,object>>(Expression.Convert(inputObject, type), inputPara).Compile();
 }

您可以像这样调用它:

object objAsDesiredType = Caster(desiredType)(obj);

缺点:该lambda的编译比已经提到的几乎所有其他方法都要慢

优点:您可以缓存lambda,这实际上应该是最快的方法,它与编译时的手写代码相同

答案 8 :(得分:-3)

可替换地:

public static T Cast<T>(this dynamic obj) where T:class
{
   return obj as T;
}