为什么DapperRow.GetType()返回null?

Dar*_*ead 3 c# types dapper

据我所知,Object.GetType()永远不应该返回null.(相关讨论)

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
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

我怀疑动态或私有类型是null的原因,所以我编写我的类库进行测试:

namespace Lib
{
  public class Blah
  {
    public static dynamic SecretObject;
    static Blah()
    {
        SecretObject = new PrivateType();
    }
  }
  class PrivateType
  {
  }
} 
Run Code Online (Sandbox Code Playgroud)

在另一个项目中,获取动态类型静态字段并调用GetType():

    dynamic obj = Lib.Blah.SecretObject;
    Console.WriteLine(obj.GetType().Name); // "Lib.PrivateType"
Run Code Online (Sandbox Code Playgroud)

根据测试结果,即使将私有类型转换为动态,我仍然可以从GetType()获取私有类型信息,为什么DapperRow.GetType()返回null?

Dav*_*d L 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);
}
Run Code Online (Sandbox Code Playgroud)

DapperRowMetaObject是一个自定义实现DynamicMetaObject本质上劫持和覆盖可以针对动态类型调用哪些方法以及这些调用应该转换为什么.在这种情况下,调用DapperRow的IDictionary.Item getter以外的任何东西或者DapperRow.SetValue会失败,因为它们总是被路由到这两个调用,但是对于目标属性不存在的任何"get"调用,该值将被默认为null在表中.

public bool TryGetValue(string name, out object value)
{
    var index = table.IndexOfName(name);
    if (index < 0)
    { // doesn't exist
        value = null;
        return false;
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

此时,对空动态值调用的任何方法都将抛出RuntimeBinderException:

RuntimeBinderException:无法对空引用执行运行时绑定

您可以通过替换GetType()将抛出完全相同的异常的另一个调用来轻松地测试此假设:

var rec = cn.Query("select getdate() as D").Single();
var t = rec.AsEnumerable();
Console.WriteLine(t.ToList());
Run Code Online (Sandbox Code Playgroud)

请记住,仍然可以直接访问动态对象本身任何属性的基础类型信息:

var rec = cn.Query("select getdate() as D").Single();
var t = rec.D.GetType();
Console.WriteLine(t.Name);
Run Code Online (Sandbox Code Playgroud)