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


74

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

dynamic Cast(object obj, Type castTo);

有人知道该怎么做吗?obj肯定实现了castTo,但是需要正确地进行强制转换才能使我的应用程序的某些运行时绑定工作解决。

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


您是说需要动态调用隐式或显式转换运算符吗?
加布

要么一个,但此刻是明确的
George Mauer

2
相关的(我一直在寻找的C#的dynamic_cast和谷歌的版本把我带到这里,但得到的答复是在另一个线程):stackoverflow.com/questions/9316159/...
杰森ç

Answers:


111

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

  • 投射:更改指向对象的引用类型的动作。向上或向下移动对象层次结构或移至已实现的界面
  • 转换:从其他类型的原始源对象创建一个新对象,并通过对该类型的引用对其进行访问。

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

在这种情况下,几乎可以肯定您不希望进行强制转换操作。将a强制转换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
在这种情况下,@ George为什么不dynamic直接转换为?如果基础操作确实是
强制转换,

用例是传入BaseType的对象,并且我有一个IList <dynamic>处理程序,其中每个处理程序都实现IHandle <T>,其中T:BaseType。我需要运行适用于此特定类型的所有处理程序,因为动态无法正确猜测(可能是因为IHandle是协变的)。我通过反射来工作了,但是还好。
George Mauer

1
在C#中,从更改引用类型的意义上讲,没有“ Casting”之类的东西。在C#中,转换和转换是相同的。顺便说一句,当您向上继承其继承树中的对象时,它的类型不会更改或需要更改,因为它更高类型的对象。将'int'转换为'double'会创建一个与原始'int'相等的新'double',尽管将'HttpWerbRequest'转换为'WebRequest'并没有任何作用,因为'HttpWebRequest'是'WebRequest'。和引用类型VS值类型部分....
AK_

2
漂亮的代码...但是为什么调用方法Convert?系统名称空间陷阱。称它DynamicConvert或类似。
phil soady 2013年

36

这应该工作:

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(它是一个类,而不是值类型)。
rsenna 2011年

4
仅因为String是IConvertible。您使用的类型必须使用IConvertible方法生成,因为该方法只是进行适当的Convert.ToABCD()调用。因此,它仍然限制了您在引用类型之间进行转换的能力。字符串碰巧是例外,因为IConvertible要求您实现ToString。
KeithS

3
是的,这不适用于实体类型(这是我的用例)
George Mauer

2
@乔治·毛厄:所以您的用例对我来说没有意义。正如@JaredPar答案所解释的,对象就是对象-如果您已经在使用对其的动态引用,则应该可以毫无问题地访问其方法和/或属性。因此,问题可能出您的用例上……BTW,这就是为什么我(错误地)认为您只是想为简单的类型化值获得一种快速转换方法的原因。
rsenna 2011年

10

到目前为止我所能获得的最好成绩:

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

在您最初发表评论后,我最终得到了差不多的结果。
KeithS

2
对我来说这没有意义。调用DynamicCast(obj, typeof(Foo))与的区别是什么(dynamic)obj,除了在obj(具有编译时类型object)不是n的情况下获得空引用的情况下,该调用有什么不同Foo?而且,该约束where T : object是无意义的并且是不允许的。
Jeppe Stig Nielsen

1
@JeppeStigNielsen这是拳击问题。假设你有Person一个GetFullName方法和Professor : Person它覆盖GetFullName与“博士”的后缀它。现在假设该实体是类型,Professor但被强制转换为Person我们方法的外部,也许这就是ORM或模型绑定程序或其他东西加载实体的方式。如果您调用((dynamic)entitity).GetFullName()它将运行Person.GetFullName()。自从这个问题出现以来已经有很长时间了,所以具体情况可能有点不对,但是这与此有关。
乔治·莫尔

1
仍然没有意义。首先,“装箱”与您将值类型(如结构)放入引用类型变量中有关。您的示例涉及Professor并且Person必然涉及类,而不涉及结构。其次,如果Professor 重写该方法,则虚拟调度将确保使用“最派生”实现。即使发生了方法隐藏,DynamicCast上面的方法也无济于事。在所有情况下,DynamicCast(obj, typeof(Foo))似乎都等效于(obj is Foo) ? (dynamic)obj : null。只需使用(dynamic)obj
杰普·斯蒂格·尼尔森

3
你似乎认为,(dynamic)(BaseClass)x可以具有不同的行为(dynamic)(DerivedClass)x时,x是的一个实例DerivedClass。但是,这种差异应从何而来?该对象不记得曾经引用过什么类型的引用。
杰普·斯蒂格·尼尔森

9

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

使用动力学的问题在于您不能直接将任何函数附加到动态对象。您必须使用一些可以计算出您不想每次都分配的作业。

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

我的理论是,Newtonsoft可以通过使用字符串中介程序来做到这一点。

不利的一面是,我相当确定将字符串转换为对象时,它将通过在当前程序集中搜索具有匹配属性的对象来使用反射,创建类型,然后实例化属性,这将需要更多反射。如果为true,则所有这些都可以视为可避免的开销。

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>。


7

开放源代码框架Dynamitey有一个静态方法,它使用DLR包括中投转换后期绑定他人

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

相对于Cast<T>使用反射的调用,此方法的优点在于,它也适用于IDynamicMetaObjectProvider具有动态转换运算符的任何对象。在上尝试转换DynamicObject


2

尝试通用:

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

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


1
如果T编译类型未知,这将不起作用(无反射)。
杰森

@KiethS,在运行时提供类型。我知道,不要问。我想我可以编写此代码并使用反射来关闭泛型,但必须有一种更好的方法。
乔治·莫尔

1
@乔治:我不相信您可以在这里使用反射来解决问题。
亚当·罗宾逊

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

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,这实际上应该是最快的方法,它与编译时的手写代码相同


-2

或者:

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

3
要求似乎是在运行时指定类型。
Jimmy

扩展方法的第一个参数不能类型的“动态”
阿明穆罕默德
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.