简体   繁体   English

是否可以使用变量/动态字段集在C#中声明匿名类型?

[英]Is it possible to declare an anonymous type in C# with a variable/dynamic set of fields?

In C#, I would like to figure out if it's possible to declare an anonymous type where the fields are not known until run-time. 在C#中,我想弄清楚是否可以声明一个匿名类型,其中字段在运行时才知道。

For example, if I have a List of key/value pairs, can I declare an anonymous type based on the contents of that list? 例如,如果我有一个键/值对列表,我可以根据该列表的内容声明一个匿名类型吗? The specific case I'm working with is passing parameters to Dapper, where I don't know ahead of time how many parameters I will have. 我正在使用的具体情况是将参数传递给Dapper,我不知道我将提供多少参数。

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

I'd like to convert this List (or an equivalent Map) into an anonymous type that I can pass to Dapper as query parameters. 我想将此List(或等效的Map)转换为匿名类型,我可以将其作为查询参数传递给Dapper。 So ideally, the above list would wind up looking like this, if defined as an anonymous type: 理想情况下,如果将以上列表定义为匿名类型,则上面的列表最终会如下所示:

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

I've seen several questions on StackOverflow asking about extending anonymous types after they are declared ("extendo objects"), or declaring arbitrary fields on an object after it's created, but I don't need to do that... I just need to declare the types dynamically up-front once. 我在StackOverflow上看到了几个问题,询问在声明它们之后扩展匿名类型(“extendo objects”),或者在创建对象后声明任意字段,但我不需要这样做......我只是需要一次性地动态声明类型。 My suspicion is that it will require some fancy reflection, if it's possible at all. 我怀疑它是否需要一些花哨的反思,如果可能的话。

My understanding is that the compiler defines a type for anonymous classes under the hood at compile-time, so if the fields of that class are not available until run-time, I might be out of luck. 我的理解是编译器在编译时为引擎定义了一个匿名类的类型,所以如果该类的字段在运行时才可用,我可能会运气不好。 My use case may in fact be no different in actuality than using an "extendo object" to define arbitrary fields, whenever. 事实上,我的用例实际上与使用“extendo对象”定义任意字段无关。

Alternatively, if anyone knows of a better way to pass query parameters to Dapper (rather than declaring an anonymous class), I would love to hear about that as well. 或者,如果有人知道更好的方法将查询参数传递给Dapper(而不是声明一个匿名类),我也很想知道这一点。

Thanks! 谢谢!

UPDATE UPDATE

Sorry for the delay in getting back to this one! 抱歉延迟回到这个! These answers were all great, I wish I could give points to everyone. 这些答案都很棒,我希望我能给大家一点。 I ended up using jbtule's solution (with edit by Sam Saffron), passing IDynamicParameters to Dapper, so I felt I had to give the answer to him. 我最终使用了jbtule的解决方案(由Sam Saffron编辑),将IDynamicParameters传递给Dapper,所以我觉得我必须给他答案。 The other answers were also good, and answered specific questions that I had asked. 其他答案也很好,并回答了我提出的具体问题。 I really appreciate everyone's time on this! 我非常感谢大家的时间!

Dapper's creators were very aware of this problem. Dapper的创作者非常清楚这个问题。 This kind of functionality is really needed for INSERT and UPDATE helpers. INSERTUPDATE助手确实需要这种功能。

The Query , Execute and QueryMultiple methods take in a dynamic parameter. QueryExecuteQueryMultiple方法接受dynamic参数。 This can either be an anonymous type, a concrete type or an object that implements IDynamicParameters . 这可以是匿名类型,具体类型或实现IDynamicParameters的对象。

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

This interface is very handy, AddParameters is called just before running any SQL. 这个界面非常方便,在运行任何SQL之前调用AddParameters Not only does this give you rich control over the parameters sent to SQL. 这不仅可以丰富地控制发送到SQL的参数。 It allows you to hook up DB specific DbParameters, since you have access to the command (you can cast it to the db specific one). 它允许您连接DB特定的DbParameters,因为您可以访问该命令(您可以将其强制转换为特定于db的命令)。 This allows for support of Table Values Parameters and so on. 这允许支持表值参数等。

Dapper contains an implementation of this interface that can be used for your purposes called DynamicParameters . Dapper包含此接口的实现,可用于您的目的,称为DynamicParameters This allows you to both concatenated anonymous parameter bags and add specific values. 这允许您连接匿名参数包并添加特定值。

You can use the method AddDynamicParams to append an anonymous type. 您可以使用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

In C#, I would like to figure out if it's possible to declare an anonymous type where the fields are not known until run-time. 在C#中,我想弄清楚是否可以声明一个匿名类型,其中字段在运行时才知道。

Anonymous types are generated by the compiler. 匿名类型由编译器生成。 You want to know if the compiler will generate you a compiler-generated type with field types not known to the compiler . 您想知道编译器是否会生成编译器生成的类型, 其中包含编译器不知道的字段类型 Clearly it cannot do so; 显然它不能这样做; as you correctly surmise, you are out of luck. 正如你猜测的那样,你运​​气不好。

I've seen several questions on StackOverflow asking about extending anonymous types after they are declared ("extendo objects") 我已经在StackOverflow上看到几个问题,询问在声明它们之后扩展匿名类型(“extendo objects”)

We usually call those "expando" objects. 我们通常称这些“expando”对象。

If what you want to do is make an expando object based on a dictionary of key-value pairs, then use the ExpandoObject class to do that. 如果你想要做的是基于键值对的字典创建一个expando对象,那么使用ExpandoObject类来做到这一点。 See this MSDN article for details: 有关详细信息,请参阅此MSDN文章:

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

If what you want to do is generate a bona-fide .NET class at runtime, you can do that too. 如果你想要做的是在运行时生成一个真正的.NET类,你也可以这样做。 As you correctly note, you need some fancy reflection to do so. 正如您所正确指出的那样,您需要一些花哨的反思才能这样做。 What you want to do is make a collectible assembly (so-called because unlike a normal assembly, you generate it at runtime and the garbage collector will clean it up when you are done with it.) 你想要做的是制作一个可收集的程序集(所谓的,因为与普通程序集不同,你在运行时生成它,垃圾收集器会在你完成它时清理它。)

See http://msdn.microsoft.com/en-us/library/dd554932.aspx for details on how to make a collectible assembly and emit a type into it using a TypeBuilder. 有关如何使用TypeBuilder创建可收集程序集并向其中发出类型的详细信息,请参阅http://msdn.microsoft.com/en-us/library/dd554932.aspx

You can't use an anonymous type. 您不能使用匿名类型。 Anonymous types are generated by the compiler rather than at run-time. 匿名类型由编译器生成,而不是在运行时生成。 You could certainly use dynamic though: 你当然可以使用dynamic

dynamic dynamicObj = new ExpandoObject();    
var objAsDict = (IDictionary<String, Object>)dynamicObj;

foreach(var item in paramList)
{
    objAsDict.Add(item.Item1, item.Item2);
}

You can then use dynamicObj as a regular object: 然后,您可以将dynamicObj用作常规对象:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM