StructureMap "WithCtorArg" "EqualTo" and nullable types
StructureMap doesn't like passing in Nullable types as constructor arguments. Is there a reason for this? Is there a way to get this to work?
[TestMethod]
public void Demo()
{
ObjectFactory.Initialize(x => x.ForRequestedType<TestClass>()
.TheDefault.Is.OfConcreteType<TestClass>()开发者_Python百科
.WithCtorArg("param1").EqualTo((byte?)3));
//This fails, but works if it's non-nullable
var result = ObjectFactory.GetInstance<TestClass>();
}
public class TestClass
{
public TestClass(byte? param1)
{ }
}
The underlying issue is that there's no difference, from the CLR's perspective, between a boxed (converted to Object
) instance of a nullable type, and an (unboxed) instance of the equivalent non-nullable type. Similarly, when you call GetType() on a nullable type like int?
, the Type returned is indistinguishable from a regular int
. See http://msdn.microsoft.com/en-us/library/ms366789.aspx more info about this.
This behavior is a recipe for disaster for code like StructureMap which interrogates types using GetType() on an Object-typed parameter. Since StructureMap doesn't know whether your byte?
is actually nullable, when StructureMap code-gens the constructor call, it code-gens it as a regular byte
, which bombs at runtime since StructureMap is passing the wrong type into the constructor call.
It'd be possible for StructureMap to work around this, but the changes would be non-trivial. I tried a few tweaks to the StructureMap source code (e.g. changing from using Object and GetType() to instead using generic methods which accepted a generic parameter type, which could then be interrogated to see if it was a nullable type or not. But there were more changes required (including, AFAIK, in the IL generation required to make the constructor call) and so I gave up.
You might want to bring this up with the Structure Map team itself who knows the code best. The StructureMap Google Group is a reasonable place to start. Note that your question has been asked before (see end of this post) so I'm not sure how responsive the Google Group is.
But, barring a fix in StructureMap itself, if I were you I'd consider wrapping your class in a simple wrapper which removes the need for a nullable parameter in the constructor.
Or if you're feeling brave, you can try to fix this by getting very familiar with the StructureMap source code. :-)
BTW, here's one example where the issue occurs in the StructureMap source:
/// <summary>
/// Sets the value of the constructor argument
/// </summary>
/// <param name="propertyValue"></param>
/// <returns></returns>
public T EqualTo(object propertyValue)
{
if(propertyValue.GetType().IsSimple())
_instance.SetProperty(_propertyName, propertyValue.ToString());
else
{
_instance.SetChild(_propertyName,new LiteralInstance(propertyValue));
}
return (T) _instance;
}
By converting the propertyValue argument to an object, it's impossible for the method to know that it was a nullable type, since byte?
and byte
are indistinguishable once converted to Object.
I found this code in the StructureMap source. Looks like it's not including nullable types.
protected internal bool IsSimple(Type type)
{
return type.IsPrimitive || IsString(type) || IsEnum(type);
}
精彩评论