【问题标题】:C#: Dynamic runtime castC#:动态运行时强制转换
【发布时间】:2011-02-07 19:43:30
【问题描述】:

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

dynamic Cast(object obj, Type castTo);

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

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

【问题讨论】:

  • 你是说需要动态调用隐式或显式转换运算符?
  • 任何一个,但目前是明确的
  • 相关(我正在寻找 C# 的 dynamic_cast 版本,谷歌把我带到了这里,但答案在另一个线程中):stackoverflow.com/questions/9316159/…

标签: c# c#-4.0 casting


【解决方案1】:

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

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

通常很难知道 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 完全相同。它仍然会指向同一个底层对象。

【讨论】:

  • 嘿 Jared,我正在寻找演员表,但你说得很好,我打错了问题,它应该是从对象(或其他一些基类)到动态的。现在会更正它。
  • @George 在这种情况下,为什么不直接转换为 dynamic 呢?如果底层操作确实是强制转换,则不需要此方法
  • 用例是传入 BaseType 的对象,我有一个 IList 处理程序,其中每个处理程序都实现 IHandle,其中 T:BaseType。我需要运行适用于这种特定类型的所有处理程序,因为动态无法正确猜测(可能是因为 IHandle 是协变的)。我使用反射让它工作了,但是 oy vey。
  • 在 C# 中,在更改引用类型的意义上没有“铸造”之类的东西。在 C# 中,转换和强制转换是相同。顺便说一句,当您将一个对象向上转换到它的继承树时,它的类型不会改变或需要改变,因为它更高类型的对象。将 'int' 转换为 'double' 会创建一个与原始 'int' 相等的新 'double' ,尽管将 'HttpWerbRequest' 转换为 'WebRequest' 不会做任何事情,因为 'HttpWebRequest' 是一个 'WebRequest'。以及引用类型与值类型部分......
  • 不错的代码...但是为什么要调用方法转换?系统命名空间陷阱。称它为DynamicConvert 或类似名称。
【解决方案2】:

这应该可行:

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

【讨论】:

  • 嗯,正要试一试,但这不会总是把这个沮丧的对象返回给对象吗?
  • @Keith:刚刚对其进行了测试,它确实适用于String(这是一个类,而不是一个值类型)。
  • 只是因为 String 是 IConvertible。您使用的类型必须使用 IConvertible 方法生成,因为该方法只是进行适当的 Convert.ToABCD() 调用。因此,它仍然限制了您在引用类型之间进行转换的能力。 String 恰好是个例外,因为 IConvertible 要求您实现 ToString。
  • 是的,这不适用于实体类型(这是我的用例)
  • @George Mauer:所以你的用例对我来说没有意义。正如@JaredPar 的回答所解释的,一个对象就是一个对象——如果你已经在使用它的动态引用,你应该能够毫无问题地访问它的方法和/或属性。所以也许问题你的用例......顺便说一句,这就是为什么我(错误地)假设你只是想为简单的类型值获得一种快速转换方法。
【解决方案3】:

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

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

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

【讨论】:

  • 现在我在未来,我知道动态正是为了保存从 Python 脚本解析的函数而设计的。所以,是的,他们可以拥有功能,是的,这不是最好的。我不会再使用我的第二个代码示例,即使它有效。它增加了开销。
【解决方案4】:

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

【讨论】:

    【解决方案5】:

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

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

    与使用反射调用的Cast&lt;T&gt; 相比,它的优势在于这也适用于任何具有动态转换运算符的IDynamicMetaObjectProvider即。 TryConvert 在DynamicObject

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

        【解决方案7】:

        尝试通用:

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

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

        【讨论】:

        • 如果T 在编译类型中未知,则这不起作用(没有反射)。
        • @KiethS,在运行时提供类型。我知道,别问。我想我可以写这个并使用反射来关闭泛型,但必须有更好的方法。
        • @George:我不相信你可以在这里使用反射。
        【解决方案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;
        }
        

        【讨论】:

        • 在您最初发表评论后,我得到的结果差不多。
        • 这对我来说没有意义。调用DynamicCast(obj, typeof(Foo))(dynamic)obj 有何不同,除了在obj(具有compile-time 类型@ 987654325@) 不是Foo?此外,约束where T : object 是没有意义的,也是不允许的。
        • @JeppeStigNielsen 这是拳击的问题。假设您有 PersonGetFullName 方法和 Professor : Person 覆盖 GetFullName 后缀为“PhD”。现在假设实体的类型为Professor,但在我们的方法之外转换为Person,也许这就是它被ORM或模型绑定器或其他东西加载的方式。如果您致电((dynamic)entitity).GetFullName(),它将运行Person.GetFullName()。我已经很久没有遇到这个问题了,所以细节可能有点错误,但与此有关。
        • 这仍然没有意义。首先,您所写的“装箱”与将值类型(如结构)放入引用类型变量有关。您使用ProfessorPerson 的示例必然涉及类,而不是结构。其次,如果Professor 覆盖该方法,虚拟调度将确保使用“最衍生”的实现。即使发生方法隐藏,上面的方法DynamicCast 也无济于事。在所有情况下,DynamicCast(obj, typeof(Foo)) 似乎都等同于(obj is Foo) ? (dynamic)obj : null。只需使用(dynamic)obj
        • xDerivedClass 的实例时,您似乎相信(dynamic)(BaseClass)x 的行为与(dynamic)(DerivedClass)x 不同。但是这种差异应该从哪里来?该对象不记得曾经引用过什么类型的引用。
        【解决方案9】:

        或者:

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

        【讨论】:

        • 要求好像是在运行时指定类型。
        • 扩展方法的第一个参数不能是'dynamic'类型
        • 他用它来表示任何类型都可能存在。如果你不把它当作超级文字和一个伪例子,这还不错。
        猜你喜欢
        • 2017-01-23
        • 1970-01-01
        • 2011-05-28
        • 1970-01-01
        • 2020-11-17
        • 2017-08-21
        • 1970-01-01
        • 1970-01-01
        • 2012-09-06
        相关资源
        最近更新 更多