C# Dynamics:Convert.ChangeType 与 Cast

Posted

技术标签:

【中文标题】C# Dynamics:Convert.ChangeType 与 Cast【英文标题】:C# Dynamics: Convert.ChangeType versus Cast 【发布时间】:2014-01-31 06:07:43 【问题描述】:

有人可以解释为什么将动态对象转换为类会返回该类,而使用 Convert.ChangeType 会返回动态对象,尤其是在运行时?例如:

 dynamic dObject = new SomeClass();
 var dTest1 = dObject as SomeClass;                           // returns SomeClass
 var dTest2 = Convert.ChangeType(dObject, typeof(SomeClass)); // returns dynamic

更广泛的问题:我有一系列实现通用接口的辅助类。我需要将这些类的列表传递给其他对象;但是不同的辅助类对泛型参数使用不同的类型,所以我不能直接传递辅助类的列表:

interface IHelper<T>

    IEnumerable<T> Foo();

var HelperList = new List<IHelper<T>> // Can't do this because T varies from helper to helper!

所以我想我可以通过创建一个包含辅助类和泛型类型的容器类来伪造运行时,但会利用动态:

class ContainerClass

    IHelper<dynamic> HelperClass;
    Type dType;                      // Specifies the type for the dynamic object

现在我可以创建并传递一个 ContainerClass 列表。所有处理都很好,直到我需要将 Foo() 的结果分配回目标 IEnumerables,此时我收到运行时错误,指出类型对象无法转换为某某具体类,即使未装箱的对象类型匹配那些需要的。如果我尝试与上面 dTest2 中类似的语法,运行时仍然无法找出“转换”。

我意识到这可能是对动态和糟糕的编程实践的滥用。如果我能确定一个解决方案,我肯定会使用不同的解决方案,但现在我要么需要完成这项工作,要么选择不那么雄心勃勃的东西。

【问题讨论】:

【参考方案1】:

在执行时,真的没有 dynamic 这样的东西。

但是,对 Convert.ChangeType 的调用提供了一个 dynamic 值作为参数。 任何使用dynamic 参数的方法调用都被视为具有dynamic 的返回值,因为编译器直到执行时才知道实际签名是什么。

但是,如果您使用强制转换、isas 表达式或构造函数调用,则结果只能是 一个 类型 - 所以这就是表达式的类型.

至于您更广泛的问题 - 我不清楚使用 dynamic 会特别帮助您。您可能想要为IHelper&lt;T&gt; 声明一个基本接口——一个非通用接口,它只用于实际的IHelper&lt;T&gt; 实例。然后你可以有一个List&lt;IHelper&gt;,其中每个元素实际上对于某些T 来说是一个IHelper&lt;T&gt;,但T 在各个实例中会有所不同。 IHelper 接口在这里并不是真正必需,尽管如果您的IHelper&lt;T&gt; 接口确实包含其他不使用T 的成员,则可以将它们移至IHelper。但是,为了清晰起见,它会很有用。

现在,当您需要使用特定的IHelper 时,then 动态类型可能会暂时有用。您可以声明一个泛型方法,并让动态类型在执行时找出类型参数。例如:

private readonly IList<IHelper> helpers;

...

public void UseHelpers()

    foreach (dynamic helper in helpers)
    
        UseHelper(helper); // Figures out type arguments itself
    


private void UseHelper<T>(IHelper<T> helper)

    // Now you're in a generic method, so can use T appropriately

【讨论】:

感谢乔恩,它阐明了演员阵容与动态。我实际上需要管理从 IHelper 实例返回的 IEnumerable 集合,这些实例稍后用于填充新类实例的属性(通过反射)。但是,您的建议给了我更多的想法;我会尝试这些,如果遇到问题,我会发布一个更详细的新问题。【参考方案2】:

根据 Jon Skeet 的回答,我认为您可以做一些非常有趣的事情并避免使用动态关键字,因为它会对性能产生影响。

使用IHelper&lt;T&gt; : IHelper。现在您可以将助手存储到List&lt;IHelper&gt; 中。现在您可以通过将类型映射到泛型方法来调用 Foo 方法。

public IEnumerable<T> UseHelper<T> (IHelper<T> helper)




delegate IEnumerable<object> UseHelperDelegate(IHelper helper)
Dictionary<Type, UseHelperDelegate> helpersMap;

helpersMap.Add(typeof(int), UseHelper<int>); // Add others if you want

public IEnmerable<object> UseHelperWithMap(IHelper helper)

    Type helperType = helper.GetType();
    IEnumerable<object> retValue;
    if (helpersMap.Contains(helperType))
    
         retValue = helpersMap[helperType](helper);
    
    else // if the type is not maped use DLR
    
         dynamic dynamicHelper = helper;
         retValue = UseHelper(dynamicHelper)
         // I wonder if this can actually be added to the map here
         // to improve performance when the same type is called again.
    

注意:由于Covariance and Contravariance,您可以将IEnumerable&lt;SomeClass&gt; 转换为IEnumerable&lt;object&gt;UseHelper&lt;SomeClass&gt; 转换为UsehelperDelegate

编辑:事实证明,您实际上可以从泛型创建一个新的具体函数并将其添加到地图中。这样就可以避免使用dynamic

var useHelperGeneric = this.GetType().GetMethods().FirstOrDefault(
               m=> m.IsGenericMethod && m.Name == "UseHelper");
var useHelper = useHelperGeneric.MakeGenericMethod(new Type[]  helper.GetType() );
var newFunction = (UserHelperDelegate)useHelper.MakeDelegate(typeof(UseHelperDelegate));

helpersMap.Add(helper.GetType(), newFunction);

newFunction(helper);

【讨论】:

我会在“聪明”代码之前使用简单的代码。这很可能不是性能瓶颈——动态类型已经有了一些非常智能的缓存。

以上是关于C# Dynamics:Convert.ChangeType 与 Cast的主要内容,如果未能解决你的问题,请参考以下文章

Dynamics 365中使用JavaScript和C#调用操作示例

错误 C# Dynamics CRM:在配置的安全令牌服务上找不到身份验证端点用户名

Dynamics CRM - 如何通过 C# Plugin 给 Contact的 主键(FullName)赋值

补充三:Dynamics 365操纵示例

Dynamics AX 2012 DLL 配置文件未更新

从 Dynamics 365 运行自定义工作流时出现 2 分钟超时问题