为什么动态类型可以工作而类型转换却不能?
到目前为止,我的猜测是动态类型只是在编译期间“关闭”类型检查,并在动态实例上调用消息时执行类似于类型转换的操作。显然还有其他事情正在发生。
附加的 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
dynamic
起作用的原因是dynamic
不依赖于对象类型的编译时知识。MyGenericClass
没有方法OnlyInConcreteClass()
,但是您传递的实例当然有该方法,并且dynamic
找到了这个。顺便说一句,您可以使
WorkWithAndCreate
像这样工作:然后,调用也将工作:
您现在不必再强制转换它。
至于你的建造者,以下的方法可行吗?
The reason
dynamic
works is thatdynamic
does not depend on compile time knowledge of the object types.MyGenericClass<string>
does not have the methodOnlyInConcreteClass()
, but the instance you are passing of course does have the method anddynamic
finds this.By the way, you can make
WorkWithAndCreate
work like this:Then, the call will work too:
You now don't have to cast it anymore.
And concerning your builder, would the following work?
这是如何使用动态的示例:
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:
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. Usuallydynamic
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.