C#Dynamics:Convert.ChangeType vs Cast

时间:2014-01-12 13:39:06

标签: c# generics dynamic casting type-conversion

有人可以解释为什么在使用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中的类似语法,运行时仍然无法找出“转换”。

我意识到这可能是滥用动态和糟糕的编程习惯来启动的。如果我能确定一个解决方案,我肯定会使用不同的解决方案,但是现在我要么需要做这个工作,要么做一些不那么雄心勃勃的工作。

2 个答案:

答案 0 :(得分:9)

在执行时,实际上没有dynamic这样的东西。

但是,对Convert.ChangeType的调用提供了dynamic值作为参数。 使用dynamic参数的任何方法调用都被视为具有返回值dynamic,因为编译器在执行时间之前不知道实际签名是什么。 / p>

但是,如果您使用强制转换,isas表达式或构造函数调用,那么结果只有一个类型 - 这就是表达式的类型。

至于你更广泛的问题 - 我不清楚使用dynamic会对你有特别的帮助。您可能希望为IHelper<T>声明一个基本接口 - 一个非通用接口,仅用于实际的IHelper<T>实例。然后,您可以拥有一个List<IHelper>,其中每个元素实际上 IHelper<T> T,但T跨实例不同。这里IHelper接口并不是必需,但如果您的IHelper<T>接口确实包含其他不使用T的成员,则可以将其移至而是IHelper。但是,只是为了清晰起见可能很有用。

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

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
}

答案 1 :(得分:4)

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

使用IHelper<T> : IHelper。现在,您可以将帮助程序存储到List<IHelper>中。现在你可以通过将类型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.
    }
}

注意:由于Covariance and Contravariance,您可以将IEnumerable<SomeClass>投放到IEnumerable<object>UseHelper<SomeClass>投放到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);