简体   繁体   English

C#Dynamics:Convert.ChangeType与Cast

[英]C# Dynamics: Convert.ChangeType versus Cast

Could someone explain why casting a dynamic object as a class returns that class while using Convert.ChangeType returns a dynamic object, particularly at runtime? 有人可以解释为什么在使用Convert.ChangeType时,将动态对象作为类返回该类会返回动态对象,特别是在运行时吗? For instance: 例如:

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

The broader problem: I have a series of helper classes which implement a generic interface. 更广泛的问题:我有一系列辅助类来实现通用接口。 I need to pass a list of these classes around to other objects; 我需要将这些类的列表传递给其他对象; however the different helper classes use different types for the generic parameter, so I cannot pass a list of the helper classes directly: 但是不同的帮助器类对泛型参数使用不同的类型,所以我不能直接传递辅助类的列表:

interface IHelper<T>
{
    IEnumerable<T> Foo();
}
var HelperList = new List<IHelper<T>> // Can't do this because T varies from helper to helper!

So I thought I could fake out the runtime by creating a container class which contains the helper class and the generic type, but leverages dynamics: 所以我认为我可以通过创建一个包含辅助类和泛型类型的容器类来伪造运行时,但是利用了动态:

class ContainerClass
{
    IHelper<dynamic> HelperClass;
    Type dType;                      // Specifies the type for the dynamic object
}

Now I can create and pass a List of ContainerClass around. 现在我可以创建并传递一个ContainerClass列表。 All the processing works great until I need to assign the results from Foo() back to the destination IEnumerables, at which point I get runtime errors saying that type object cannot be converted to such and such concrete class, even though the unboxed object types match those required. 所有处理工作都很好,直到我需要将Foo()的结果分配回目标IEnumerables,此时我得到运行时错误,说明类型对象无法转换为这样的具体类,即使未装箱的对象类型匹配那些要求。 If I attempt similar syntax as in dTest2 above, the runtime is still unable to figure out the "conversion". 如果我尝试上面的dTest2中的类似语法,运行时仍然无法找出“转换”。

I realize this is probably an abuse of dynamic and poor programming practice to boot. 我意识到这可能是滥用动态和糟糕的编程习惯来启动的。 I will certainly use a different solution if and when I can identify one, but for now I either need to make this work or go with something less ambitious. 如果我能找到一个解决方案,我当然会使用不同的解决方案,但是现在我要么需要做这个工作,要么选择不那么雄心勃勃的东西。

At execution time, there's no such thing as dynamic really. 在执行时,真的没有dynamic

However, the call to Convert.ChangeType is providing a dynamic value as an argument. 但是,对Convert.ChangeType的调用是提供dynamic值作为参数。 Any method call using a dynamic argument is treated as having a return value of dynamic , because the compiler doesn't know what the actual signature will be until execution time. 使用dynamic参数的任何方法调用都被视为具有dynamic的返回值,因为编译器在执行时间之前不知道实际签名是什么。

However, if you use a cast, an is or an as expression, or a constructor call there's only one type that the result can be - so that's the type of the expression. 但是,如果使用强制转换, isas表达式,或构造函数调用,则结果只有一种类型 - 这就是表达式的类型。

As for your broader problem - it's not clear to me that using dynamic would particularly help you. 至于你更广泛的问题 - 我不清楚使用dynamic会特别有助于你。 You may want to declare a base interface for IHelper<T> - a non-generic interface, that is only ever used for actual IHelper<T> instances. 您可能希望为IHelper<T>声明一个基本接口 - 一个非泛型接口,它只用于实际的IHelper<T>实例。 Then you can have a List<IHelper> where every element is actually an IHelper<T> for some T , but with T varying across instances. 然后你可以得到一个List<IHelper> ,其中每个元素实际上是一些TIHelper<T> ,但是T在实例之间变化。 The IHelper interface isn't really required here, although if your IHelper<T> interface really contains other members which don't use T , those could be moved to IHelper instead. 这里并不真正需要 IHelper接口,但如果你的IHelper<T>接口确实包含其他不使用T成员,那么可以将它们移动到IHelper However, just having it for clarity can be useful. 然而,仅仅为了清晰起见可能是有用的。

Now, when you need to use a specific IHelper , then dynamic typing could briefly be useful. 现在,当你需要使用特定的IHelper那么动态类型可以简单地是有用的。 You can declare a generic method, and let dynamic typing figure out the type argument at execution time. 您可以声明一个泛型方法,并让动态类型在执行时找出类型参数。 For example: 例如:

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
}

Based on Jon Skeet's answer I think you can do something really interesting and avoid the dynamic keyword because it has a performance impact. 根据Jon Skeet的回答,我认为你可以做一些非常有趣的事情并避免使用动态关键字,因为它会影响性能。

Use IHelper<T> : IHelper . 使用IHelper<T> : IHelper Now you can store the helpers like into a List<IHelper> . 现在,您可以将帮助程序存储到List<IHelper> Now you could call the Foo method by maping types to a generic method. 现在你可以通过将类型maping到泛型方法来调用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.
    }
}

Note: you can cast IEnumerable<SomeClass> to IEnumerable<object> and UseHelper<SomeClass> to UsehelperDelegate because of Covariance and Contravariance . 注意:您可以投IEnumerable<SomeClass>IEnumerable<object>UseHelper<SomeClass>UsehelperDelegate因为协变和逆变

Edit: It turns out you can actualy create a new concrete function from the generic and add it to the map. 编辑:事实证明,你可以从通用创建一个新的具体功能,并将其添加到地图。 This way you can avoid using dynamic . 这样就可以避免使用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);

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

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