据我所知,Object.GetType()永远不应该返回null。 (related discussion)
Dapper .Query()返回要被视为动态对象的私有类DapperRow实例。我发现了一件奇怪的事:DapperRow的.GetType()返回null。
以下是重现问题的示例代码。创建一个C#项目,引用Dapper并打开与SQL Server(或其他数据库)的连接,使用.Query()执行简单的select查询并检索第一行结果。使用GetType()获取结果对象的类型,返回值为null。
using (SqlConnection cn = new SqlConnection(csSql))
{
var rec = cn.Query("select getdate() as D").Single();
var t = rec.GetType(); // t == null
Console.WriteLine(t.Name); // null reference exception
}
我怀疑动态或私有类型是null的原因,所以我编写了我的类库以供测试:
namespace Lib
{
public class Blah
{
public static dynamic SecretObject;
static Blah()
{
SecretObject = new PrivateType();
}
}
class PrivateType
{
}
}
在另一个项目中,获取动态类型静态字段并调用GetType():
dynamic obj = Lib.Blah.SecretObject;
Console.WriteLine(obj.GetType().Name); // "Lib.PrivateType"
根据测试结果,即使将私有类型转换为动态,我仍然可以从GetType()获取私有类型信息,为什么DapperRow.GetType()返回null?
答案 0 :(得分:5)
DapperRow
是专门在Dapper中构建和使用的,可提供高度优化的行返回,而无需重复标头信息。这有助于缩小对象的大小并减少冗余数据,从而提高效率。
但是,看起来StackExchange团队甚至比第一眼看上去还要进一步采用元编程。
DapperRow
实现System.Dynamic.IDynamicMetaObjectProvide
接口,这需要实现GetMetaObject方法:
System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(
System.Linq.Expressions.Expression parameter)
{
return new DapperRowMetaObject(parameter,
System.Dynamic.BindingRestrictions.Empty, this);
}
DapperRowMetaObject
是DynamicMetaObject
DapperRow.SetValue
,基本上劫持并覆盖了可以针对动态类型调用哪些方法以及这些调用应该转换为什么方法。在这种情况下,调用除DapperRow的IDictionary.Item getter或public bool TryGetValue(string name, out object value)
{
var index = table.IndexOfName(name);
if (index < 0)
{ // doesn't exist
value = null;
return false;
}
...
}
以外的任何内容都将失败,因为它们始终路由到这两个调用,但对于任何“get”调用,该值将为custom implementation表中不存在target属性的地方。
RuntimeBinderException
此时,对空动态值调用的任何方法都将抛出GetType()
:
RuntimeBinderException:无法对null执行运行时绑定 参考
您可以通过将var rec = cn.Query("select getdate() as D").Single();
var t = rec.AsEnumerable();
Console.WriteLine(t.ToList());
替换为将引发完全相同异常的另一个调用来轻松测试此假设:
var rec = cn.Query("select getdate() as D").Single();
var t = rec.D.GetType();
Console.WriteLine(t.Name);
请注意,动态对象本身的任何属性 的基础类型信息仍然可以直接访问:
>>> import netifaces
>>> addrs=netifaces.ifaddresses('ens3')
>>> addrs[netifaces.AF_INET6]
[{'netmask': 'ffff:ffff:ffff:ffff::/64', 'addr': '2001:19f0:4400:4983:5400:ff:fe54:1677'}, {'netmask': 'ffff:ffff:ffff:ffff::/64', 'addr': 'fe80::5400:ff:fe5e:1977%ens3'}]