【问题标题】:Is it possible to declare an anonymous type in C# with a variable/dynamic set of fields?是否可以在 C# 中使用变量/动态字段集声明匿名类型?
【发布时间】:2011-09-15 20:25:28
【问题描述】:

在 C# 中,我想知道是否可以声明一个匿名类型,其中字段直到运行时才知道。

例如,如果我有一个键/值对列表,我可以根据该列表的内容声明一个匿名类型吗?我正在处理的具体案例是将参数传递给 Dapper,我事先不知道我将拥有多少个参数。

List<Tuple<string, string>> paramList = new List<Tuple<string, string>>() {
    new Tuple<string, string>("key1", "value1"),
    new Tuple<string, string>("key2", "value2")
    ...
};

我想将此 List(或等效的 Map)转换为可以作为查询参数传递给 Dapper 的匿名类型。所以理想情况下,如果定义为匿名类型,上面的列表最终会是这样:

new { key1=value1, key2=value2, ... }

我在 StackOverflow 上看到了几个关于在声明匿名类型后扩展匿名类型的问题(“扩展对象”),或者在创建对象后在对象上声明任意字段,但我没有需要这样做...我只需要预先动态声明一次类型。我怀疑它需要一些花哨的反思,如果可能的话。

我的理解是编译器在编译时为匿名类定义了一个类型,所以如果该类的字段直到运行时才可用,我可能会不走运。实际上,我的用例实际上可能与使用“扩展对象”来定义任意字段没有什么不同。

或者,如果有人知道将查询参数传递给 Dapper(而不是声明匿名类)的更好方法,我也很想听听。

谢谢!

更新

很抱歉延迟回到这个!这些答案都很棒,希望能给大家加分。我最终使用了 jbtule 的解决方案(由 Sam Saffron 编辑),将 IDynamicParameters 传递给 Dapper,所以我觉得我必须给他答案。其他答案也很好,回答了我提出的具体问题。我真的很感谢大家在这方面的时间!

【问题讨论】:

    标签: c# dynamic anonymous-types dapper


    【解决方案1】:

    Dapper 的创建者非常清楚这个问题。 INSERTUPDATE 助手确实需要这种功能。

    QueryExecuteQueryMultiple 方法采用 dynamic 参数。这可以是匿名类型、具体类型或实现IDynamicParameters 的对象。

    public interface IDynamicParameters
    {
        void AddParameters(IDbCommand command, Identity identity);
    }
    

    这个接口非常方便,AddParameters 在运行任何 SQL 之前被调用。这不仅使您可以对发送到 SQL 的参数进行丰富的控制。它允许您连接特定于数据库的 DbParameters,因为您可以访问该命令(您可以将其转换为特定于数据库的命令)。这允许支持表值参数等。

    Dapper 包含此接口的实现,可用于您的目的,称为DynamicParameters。这允许您连接匿名参数包并添加特定值。

    您可以使用方法AddDynamicParams 附加匿名类型。

    var p = new DynamicParameters();
    p.AddDynamicParams(new{a = "1"});
    p.AddDynamicParams(new{b = "2", c = "3"});
    p.Add("d", "4")
    var r = cnn.Query("select @a a, @b b, @c c, @d d", p);
    // r.a == 1, r.b == 2, r.c == 3, r.d == 4
    

    【讨论】:

      【解决方案2】:

      在 C# 中,我想知道是否可以声明一个匿名类型,其中字段直到运行时才知道。

      匿名类型由编译器生成。你想知道编译器是否会为你生成一个编译器生成的类型编译器不知道的字段类型。显然它不能这样做;你猜对了,你运气不好。

      我在 StackOverflow 上看到了几个关于在声明匿名类型后扩展匿名类型的问题(“extendo 对象”)

      我们通常称这些“expando”对象。

      如果您想做的是基于键值对字典创建一个 expando 对象,那么请使用 ExpandoObject 类来实现。有关详细信息,请参阅此 MSDN 文章:

      http://msdn.microsoft.com/en-us/magazine/ff796227.aspx

      如果您想要在运行时生成一个真正的 .NET 类,您也可以这样做。正如您正确指出的那样,您需要一些花哨的反射才能做到这一点。您要做的是制作一个可收集的程序集(之所以这么说是因为与普通程序集不同,您在运行时生成它,并且垃圾收集器会在您完成后清理它。)

      请参阅http://msdn.microsoft.com/en-us/library/dd554932.aspx,了解有关如何使用 TypeBuilder 制作可收藏程序集并向其中发出类型的详细信息。

      【讨论】:

      • 抱歉造成混淆,“ExpandoObject”是我的意思 =) 我还不确定你建议的选项中的哪一个对我有用,我需要做一些实验,将不同的对象传递给Dapper,看看哪些有效,哪些无效。 RE:你的第一段,我想我已经说服自己,当我完成我的问题时,它不会起作用!你说的很清楚,谢谢! =)
      • Expandos 和 dapper 的问题在于没有对它们进行内部特殊处理,因此不会从底层 IDictionary 或其他内容中提取参数。这就是为什么在这种情况下你可能想使用IDynamicParameters@Egahn ...你可以使用reflection.emit从2个匿名类型中融合并生成一个新类型,但在这种情况下你不需要。
      【解决方案3】:

      您不能使用匿名类型。匿名类型由编译器生成,而不是在运行时生成。你当然可以使用dynamic

      dynamic dynamicObj = new ExpandoObject();    
      var objAsDict = (IDictionary<String, Object>)dynamicObj;
      
      foreach(var item in paramList)
      {
          objAsDict.Add(item.Item1, item.Item2);
      }
      

      然后您可以将 dynamicObj 用作常规对象:

      Console.WriteLine(dynamicObj.key1); // would output "value1"
      

      【讨论】:

      • 令人着迷,我不知道你能做到这一点。我将不得不尝试这样做并将生成的对象传递给 Dapper 以查看它是否有效。我预见的问题是,我相信我传递给 Dapper 的 SQL 查询字符串中的参数名称决定了它尝试访问的参数对象上的哪个字段,我也传递给 Dapper ......所以如果我的 SQL 查询有一个像“ @A" 在其中,我传入的参数对象需要一个名为 "A" 的字段。不过我会试一试。
      • 我不在电脑前,但它是如何工作的?您是否不必至少将其转换为字典而不是字典?我认为将其转换为接口不可能神奇地实现 add 方法。我知道它会编译,但实际上不会将它添加到集合中吗?
      • @John Buchanan - ExpandoObject 实现 IDictionary。 IDictionary 包含 Add(TKey, TValue) 方法。因此,一旦您转换为 IDictionary...添加作品。
      • @Justin Niessner - 对不起我的无知,但为什么要选演员?既然 ExpandoObject 实现了 IDictionary,为什么不只是 dynamicObj.Add()?
      猜你喜欢
      • 1970-01-01
      • 2015-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-06
      • 1970-01-01
      • 2011-09-16
      • 2017-10-25
      相关资源
      最近更新 更多