Dapper:这两段代码有什么区别?
Dapper: What's the difference between these two pieces of code?
我已经为 Dapper 注册了一个自定义类型处理程序来 Json 序列化一个字符串。这按预期工作。然而,在编码时,我发现重构后有两段代码,后一段没有触发数据类型处理程序,但我认为应该有,所以有什么区别?
首先是按预期工作的代码 - 自定义类型处理程序由 dapper 调用,数据在插入 table:
时被序列化
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 });
}
现在,代码无法正常工作,因为它插入 table 完全限定类型字符串而不是序列化数据,但我认为它在语义上相似:
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(或自定义类型处理程序)序列化的数据类型。有什么不同?
假设 JsonString 和 String 之间没有可用的隐式转换,第二个示例中的代码将不起作用。如果有可用的隐式转换,您需要提供有关正在发生的异常的更多信息。
在 JsonString 之间没有可用的转换,用 var 类型声明的变量具有由编译器推断的类型 ((https://msdn.microsoft.com/en-us/library/bb384061.aspx)。在这种情况下,变量 v 是 JsonString 类型或 String 类型,具体取决于分配的哪一部分发生。如果编译器假定它是 JsonString 类型,则任何带有 String 的分配都将失败。
在您的第二个代码示例中,第一行代码是错误的,因为赋值导致两种不同的数据类型被赋值给变量 v。
我假设 val
的编译时类型(即声明类型)是 System.Object
。我将解释为什么这两种情况不等同。
必须小心区分变量的编译时类型和实际的 运行 时类型(由 .GetType()
找到)。
在第一段代码中,在 运行 时 val
不是 String
的分支中,我们声明:
var v = new JsonString {Value = val};
此处 var
被 JsonString
替代,因为这是 =
赋值右侧的编译时类型。然后是匿名类型实例:
new { keyName, v }
将成为class(我将其称为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
”,其“第二个”属性 为:
public object v { get { ... } }
所以总结一下:在第一种情况下,你传入一个对象,它有一个 属性 v
声明为 JsonString
,当检索 returns 一个 JsonSting
恰好有 运行 次 JsonString
。在第二个代码示例中,您传入一个对象,该对象具有声明为 object
的 属性 v
,在检索时 returns 恰好具有 运行-时间类型JsonString
.
我不太了解Dapper的工作原理!但大概当它看到(通过反射?) 属性 类型是 object
时,它只是简单地调用 .ToString()
。如果 object
碰巧是 运行 时间类型 string
,那应该没问题,因为 string.ToString()
被覆盖了。但是JsonString.ToString()
不是那样的
当 Dapper 看到 属性 被声明为 JsonString
时,Dapper 会做一些比调用 .ToString()
.
更聪明的事情
我已经为 Dapper 注册了一个自定义类型处理程序来 Json 序列化一个字符串。这按预期工作。然而,在编码时,我发现重构后有两段代码,后一段没有触发数据类型处理程序,但我认为应该有,所以有什么区别?
首先是按预期工作的代码 - 自定义类型处理程序由 dapper 调用,数据在插入 table:
时被序列化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 });
}
现在,代码无法正常工作,因为它插入 table 完全限定类型字符串而不是序列化数据,但我认为它在语义上相似:
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(或自定义类型处理程序)序列化的数据类型。有什么不同?
假设 JsonString 和 String 之间没有可用的隐式转换,第二个示例中的代码将不起作用。如果有可用的隐式转换,您需要提供有关正在发生的异常的更多信息。
在 JsonString 之间没有可用的转换,用 var 类型声明的变量具有由编译器推断的类型 ((https://msdn.microsoft.com/en-us/library/bb384061.aspx)。在这种情况下,变量 v 是 JsonString 类型或 String 类型,具体取决于分配的哪一部分发生。如果编译器假定它是 JsonString 类型,则任何带有 String 的分配都将失败。
在您的第二个代码示例中,第一行代码是错误的,因为赋值导致两种不同的数据类型被赋值给变量 v。
我假设 val
的编译时类型(即声明类型)是 System.Object
。我将解释为什么这两种情况不等同。
必须小心区分变量的编译时类型和实际的 运行 时类型(由 .GetType()
找到)。
在第一段代码中,在 运行 时 val
不是 String
的分支中,我们声明:
var v = new JsonString {Value = val};
此处 var
被 JsonString
替代,因为这是 =
赋值右侧的编译时类型。然后是匿名类型实例:
new { keyName, v }
将成为class(我将其称为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
”,其“第二个”属性 为:
public object v { get { ... } }
所以总结一下:在第一种情况下,你传入一个对象,它有一个 属性 v
声明为 JsonString
,当检索 returns 一个 JsonSting
恰好有 运行 次 JsonString
。在第二个代码示例中,您传入一个对象,该对象具有声明为 object
的 属性 v
,在检索时 returns 恰好具有 运行-时间类型JsonString
.
我不太了解Dapper的工作原理!但大概当它看到(通过反射?) 属性 类型是 object
时,它只是简单地调用 .ToString()
。如果 object
碰巧是 运行 时间类型 string
,那应该没问题,因为 string.ToString()
被覆盖了。但是JsonString.ToString()
不是那样的
当 Dapper 看到 属性 被声明为 JsonString
时,Dapper 会做一些比调用 .ToString()
.