繁体   English   中英

C#:动态运行时转换

[英]C#: Dynamic runtime cast

我想实现一个具有以下签名的方法

dynamic Cast(object obj, Type castTo);

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

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

我认为您在这里混淆了转换和转换的问题。

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

通常很难知道 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;

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

这应该工作:

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 等语言中发现的那种“类型转换”(因为它还将值转换为所需的类型)。 我不知道这是否是一件好事,但它确实有效...... :-)

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

使用动态的问题是您不能将任何函数直接附加到动态对象。 您必须使用可以计算出您不想每次都计算出的作业的东西。

在计划这个简单的解决方案时,我研究了在尝试重新键入相似对象时有效的中介是什么。 我发现二进制数组、字符串(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<string, object>。

到目前为止我得到的最好的:

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;
}

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

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

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

尝试一个通用的:

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

编辑:Grr,没看到。 是的,您可以反射性地关闭泛型,并且隐藏在非泛型扩展方法中并不难:

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

我只是不确定你会从中得到什么。 基本上,您采用的是动态,强制转换为反射类型,然后将其塞回动态中。 也许你是对的,我不应该问。 但是,这可能会做你想做的。 基本上,当您进入动态领域时,您将不再需要执行大多数转换操作,因为您可以通过反射方法或反复试验来发现对象是什么和做什么,因此没有多少优雅的方法可以做到这一点。

对@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;
    }

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

 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,那么这应该是最快的方法了,编译时和手写代码是一样的

或者:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM