为什么动态类型在不进行投射的情况下工作?

时间:2010-10-29 07:50:23

标签: c# dynamic casting generics

我的猜测是,动态类型只是在编译期间“关闭”类型检查,并且在动态实例上调用消息时执行类似于类型转换的操作。显然还有其他事情正在发生。

附加的NUnit测试用例显示了我的问题:使用动态类型我可以使用仅在具体子类中可用的方法,但我不能使用强制转换(导致InvalidCastException)。我宁愿做演员,因为这给了我完整的代码完成VS。

任何人都可以解释发生了什么和/或给我一个提示如何在我的案例中完成代码完成而不必在每个具体的子类中重新实现WorkWithAndCreate方法?

欢呼,约翰内斯

using System;
using NUnit.Framework;

namespace SlidesCode.TestDataBuilder
{
    [TestFixture]
    public class MyTest
    {
        [Test]
        public void DynamicWorks()
        {
            string aString = CreateDynamic(obj => obj.OnlyInConcreteClass());
            Assert.AreEqual("a string", aString);
        }

        private static string CreateDynamic(Action<dynamic> action)
        {
            return new MyConcreteClass().WorkWithAndCreate(action);
        }

        [Test]
        public void CastingDoesNotWorkButThrowsInvalidCastException()
        {
            string aString = CreateWithCast(obj => obj.OnlyInConcreteClass());
            Assert.AreEqual("a string", aString);
        }

        private static string CreateWithCast(Action<MyConcreteClass> action)
        {
            return new MyConcreteClass().WorkWithAndCreate((Action<MyGenericClass<string>>) action);
        }
    }

    internal abstract class MyGenericClass<T>
    {
        public abstract T Create();
        public T WorkWithAndCreate(Action<MyGenericClass<T>> action)
        {
            action(this);
            return this.Create();
        }
    }

    internal class MyConcreteClass : MyGenericClass<string>
    {
        public override string Create()
        {
            return "a string";
        }

        public void OnlyInConcreteClass()
        {
        }
    }
}

以下是我的评论中格式化的真实世界示例:

Customer customer = ACustomer(cust =>
        {
            cust.With(new Id(54321));
            cust.With(AnAddress(addr => addr.WithZipCode(22222)));
        });

private static Address AnAddress(Action<AddressBuilder> buildingAction)
{
    return new AddressBuilder().BuildFrom(buildingAction);
}

private static Customer ACustomer(Action<CustomerBuilder> buildingAction)
{
    return new CustomerBuilder().BuildFrom(buildingAction);
}

它缺少一些细节,但我希望它能明确这个目的。

2 个答案:

答案 0 :(得分:3)

dynamic的作用原因是dynamic不依赖于对象类型的编译时知识。 MyGenericClass<string>没有方法OnlyInConcreteClass(),但您传递的实例当然有方法,而dynamic找到了这个方法。

顺便说一下,你可以让WorkWithAndCreate像这样工作:

public T WorkWithAndCreate<T1>(Action<T1> action)
    where T1 : MyGenericClass<T>
{
    action((T1)this);
    return this.Create();
}

然后,呼叫也会起作用:

private static string CreateWithCast(Action<MyConcreteClass> action)
{
    return new MyConcreteClass().WorkWithAndCreate(action);
}

你现在不必再施展它了。

关于你的建造者,以下工作会如何?

private static TResult AnInstance<TBuilder, TResult>(Action<TBuilder> buildingAction)
    where TBuilder : Builder<TResult>, new()
{
    return new TBuilder().BuildFrom(buildingAction);
}

答案 1 :(得分:1)

这是如何使用动态的示例:

http://msdn.microsoft.com/en-us/library/dd264736.aspx

你说:

  

我的猜测是,动态类型只是在编译期间“关闭”类型检查,并且在动态实例上调用消息时执行与类型转换类似的操作

实际上,它使用反射来查找在运行时通过名称调用的方法,属性和字段。除非您实际将对象强制转换回其基础类型,否则不会执行转换。

至于你的实际问题,你能举一个更具体的例子吗?可能有更好的设计,但你没有告诉我们你想做什么 - 就像你现在正在做的那样。

有猜测,您可能希望使用基础interface,并使所有功能都接受该基本接口。然后将要调用的方法放在该接口上,并在具体类型中实现它们。通常dynamic在没有基类型时用作解决方法,或者无法修改基类型以添加​​虚拟或抽象方法。

如果你真的想让它按原样运行,你必须使用泛型类型参数而不是动态类型参数来编写它。请参阅Pieter的解决方案,了解如何正确执行此操作。