为什么动态类型可以工作而类型转换却不能?

发布于 2024-09-29 06:42:59 字数 2316 浏览 1 评论 0原文

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

附加的 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);
}

其中缺少一些细节,但我希望它能够明确目的。

My guess until now was that a dynamic type just "switches off" type checking during compilation and does something similar to a type cast when a message is invoked on a dynamic instance. Obviously something else is going on.

The attached NUnit test case shows my problem: Using a dynamic type I can use a method only available in the concrete subclass, but I cannot do the same using a cast (results in InvalidCastException). I'd rather do the casting since this gives me full code completion in VS.

Can anyone explain what's happening and/or give me a hint how I could get code completion in my case without having to reimplement the WorkWithAndCreate-method in every concrete subclass?

cheers, Johannes

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()
        {
        }
    }
}

Here's the formatted real world example from my comment:

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

Some details are missing from it but I hope it makes the purpose clear.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

早乙女 2024-10-06 06:42:59

dynamic 起作用的原因是 dynamic 不依赖于对象类型的编译时知识。 MyGenericClass 没有方法 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);
}

The reason dynamic works is that dynamic does not depend on compile time knowledge of the object types. MyGenericClass<string> does not have the method OnlyInConcreteClass(), but the instance you are passing of course does have the method and dynamic finds this.

By the way, you can make WorkWithAndCreate work like this:

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

Then, the call will work too:

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

You now don't have to cast it anymore.

And concerning your builder, would the following work?

private static TResult AnInstance<TBuilder, TResult>(Action<TBuilder> buildingAction)
    where TBuilder : Builder<TResult>, new()
{
    return new TBuilder().BuildFrom(buildingAction);
}
玩世 2024-10-06 06:42:59

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

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

你说:

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

实际上,它使用反射来查找方法、属性和字段您在运行时按名称调用。不会进行任何转换,除非您实际上将对象转换回其基础类型。

至于你的实际问题,你能举一个更具体的例子吗?可能有更好的设计,但您还没有告诉我们您想要做什么 - 只是您目前正在做什么。

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

如果您确实想让它按原样工作,则必须使用泛型类型参数而不是动态类型参数来编写它。有关如何正确执行此操作的信息,请参阅 Pieter 的解决方案。

This is an example of how to use dynamic:

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

You said:

My guess until now was that a dynamic type just "switches off" type checking during compilation and does something similar to a type cast when a message is invoked on a dynamic instance

Actually, it uses reflections to look up the methods, properties, and fields you invoke by name, at runtime. No casting is done, unless you actually cast the object back to its underlying type.

As for your actual problem, can you give a more concrete example? There may be a better design, but you haven't told us what you're trying to do - just what you're currently doing.

Hazarding a guess, you may want to use a base interface, and make all your functions accept that base interface. Then put the methods you want to call on that interface, and implement them in your concrete types. Usually dynamic is used as a work around when you don't have a base type, or can't modify the base type to add virtual or abstract methods.

If you really want to get this working, as-is, you must write it with a generic type parameter, not a dynamic type parameter. See Pieter's solution for how to do this properly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文