我已经为Dapper注册了一个自定义类型处理程序,以便Json序列化一个字符串。这按预期工作。然而,在编码时,我在重构后发现了两段代码,后者没有触发数据类型处理程序,但在我看来应该是什么区别?
首先是按预期工作的代码 - 自定义类型处理程序由dapper调用,并且数据在插入表时被序列化:
if (val.GetType() != typeof (String))
{
var v = new JsonString {Value = val};
this.Connection.Execute("insert into misc (k,v) values (@keyName, @v)",
new { keyName, v });
}
else
{
this.Connection.Execute("insert into misc (k,v) values (@keyName, @val)",
new { keyName, val });
}
现在对于在代码中插入完全限定类型字符串而不是序列化数据的代码并不起作用,但我认为它在语义上相似:
var v = val.GetType() != typeof (String)
? new JsonString {Value = val}
: val;
// t is set to type of JsonString, therefore the dapper type handler should be used
var t = v.GetType();
this.Connection.Execute("insert into misc (k,v) values (@keyName, @v)",
new { keyName, v });
它是一个小巧的bug还是一个c#古怪的?我假设它与三元条件中的自动键入有关,这是失败点,但t
设置为由Dapper(或者更确切地说是自定义类型处理程序)序列化的数据类型。有什么区别?
答案 0 :(得分:3)
我将假设val
的编译时类型(即声明类型)为System.Object
。我将解释为什么这两种情况不相同。
必须注意区分变量的编译时类型和实际运行时类型(由.GetType()
找到)。
在第一段代码中,在运行时val
不是String
的分支中,我们声明:
var v = new JsonString {Value = val};
此处var
由JsonString
代替,因为这是=
赋值右侧的编译时类型。然后是匿名类型实例:
new { keyName, v }
将成为一个成员(我将称之为class Anonymous1
)与成员
public JsonString v { get { ... } }
现在,在第二段代码中,我们改为:
var v = val.GetType() != typeof (String)
? new JsonString {Value = val}
: val;
三元运算符的操作数的编译时类型是:
{bool} ? {JsonString} : {object}
在编译时,必须找到JsonString
和object
的最佳常见类型。这种常见类型是object
。因此,object
在此处成为v
的编译时类型,即var
在此处表示object
。
然后是匿名类型:
new { keyName, v }
是“Anonumous2
”类型,其“2nd”属性显示为:
public object v { get { ... } }
总而言之:在第一种情况下,你传入一个对象,该对象具有一个声明为v
的属性JsonString
,当被检索时返回一个恰好运行的JsonSting
- 时间JsonString
。在第二个代码示例中,您传入一个对象,该对象具有声明为v
的属性object
,在检索时返回一个恰好具有运行时类型object
的{{1}}。
我对Dapper的工作方式不太了解!但大概是当它看到(通过反射?)属性类型为JsonString
时,它只是在其上调用object
。如果.ToString()
碰巧属于运行时类型object
,则应该没问题,因为string
被覆盖了。但string.ToString()
不是那样的。
当Dapper看到该属性被声明为JsonString.ToString()
时,Dapper做的事情比调用JsonString
更聪明。
答案 1 :(得分:1)
假设JsonString和String之间没有可用的隐式转换,第二个示例中的代码不起作用。如果存在可用的隐式转换,则需要提供有关正在发生的异常的更多信息。
如果JsonString之间没有可用的转换,则使用类型var声明的变量具有由编译器推断的类型((https://msdn.microsoft.com/en-us/library/bb384061.aspx)。在这种情况下,变量v的类型为JsonString或String,具体取决于哪个如果编译器假定它是JsonString类型,那么任何带有String的赋值都将失败。
在你的第二个代码示例中,第一行代码是错误的,因为赋值导致两个不同的数据类型被赋值给变量v。