简体   繁体   English

从匿名类型获取字段

[英]Get fields from anonymous type

I have this class: 我有这个课:

public class allFields
{
    public string EAN { get; set; }
    public string title { get; set; }
    public string qty { get; set; }
    public string price { get; set; }
    public DateTime date { get; set; }
}

And a function that return an anonymous type: 还有一个返回匿名类型的函数:

public IEnumerable<object> stockEtatQty()
{   
    List<allFields> afList = new List<allFields>();

    var query = from x in ctx.book
                where x.qty > 0
                select x;
    foreach (var item in query)
    {
        allFields af = new allFields();
        af.EAN = item.EAN;
        af.title = item.Titre;
        af.qty = ""+item.Quantite;
        afList.Add(af);
    }
    var q = from x in afList
            select new { EAN=x.EAN, Title=x.title, Quantity=x.qty };

    return q; //q is a IEnumerable<'a> where a is new {string EAN, string Title, string Quantity}
}

In my WinForm a use this function as below: 在我的WinForm中,使用以下功能:

private void QuantityToolStripMenuItem_Click(object sender, EventArgs e)
{
    ServiceStock sstock = new ServiceStock();
    var q = sstock.stockEtatQty().ToList();// q is a list<object>
    string str = "";
    foreach (var item in q)
    {
        str += item + Environment.NewLine;
    }
    MessageBox.Show(str);
}

The result is: 结果是:

{ EAN = 1, Title = CSharp Security, Quantity = 970 }
{ EAN = 2, Title = MISC, Quantity = 100 }
...

What I want? 我想要的是?
I want not like the result above, but separate each field apart of the item in the loop foreach , eg get item.EAN , item.Title and item.Quantity . 我不喜欢上面的结果,但是在foreach循环中将item每个字段分开,例如get item.EANitem.Titleitem.Quantity

If there is no solution for my problem I would like to know an alternative, 如果无法解决我的问题,我想知道一个替代方法,
Thanks for help. 感谢帮助。

The obvious solution is to create a custom type (let's call it BookInfo ) and return a IEnumerable<BookInfo> instead of a IEnumerable<object> (and maybe override ToString if you want to put the formatting into this class itself). 显而易见的解决方案是创建一个自定义类型(我们将其称为BookInfo )并返回IEnumerable<BookInfo>而不是IEnumerable<object> (如果要将格式本身放入此类,则可以覆盖ToString )。

Then you can easily format the output. 然后,您可以轻松格式化输出。

public class BookInfo
{
    public string EAN {get;set;}
    public string Title {get;set;}
    public int Quantity {get;set;}
}

public IEnumerable<BookInfo> stockEtatQty()
{   
    ...

    var q = from x in afList
            select new BookInfo { EAN=x.EAN, Title=x.title, Quantity=x.qty };

    return q;
}

private void QuantityToolStripMenuItem_Click(object sender, EventArgs e)
{
    ServiceStock sstock = new ServiceStock();
    var q = sstock.stockEtatQty();

    var message = string.Join(Environment.NewLine, 
                              q.Select(item => String.Format("{0} {1} {2}", item.EAN, item.Title, item.Quantity)));
    MessageBox.Show(message);
}

Since the static type information about the object of anonymous type is lost by the time that you exit stockEtatQty() method, you could cast the object to dynamic and access fields like this: 由于在退出stockEtatQty()方法时,有关匿名类型对象的静态类型信息已丢失,因此可以将对象转换为dynamic字段,并按如下方式访问字段:

str = string.Join(Environment.NewLine, q.Cast<dynamic>().Select(item => 
    string.Format("{0} {1} {2}", item.EAN, item.Title, item.Quantity)
));

The cast to dynamic tells the compiler that EAN , Title , and Quantity need to be resolved at runtime. 强制转换为dynamic告诉编译器EANTitleQuantity需要在运行时解析。

Note that I also replaced the foreach loop with a call to string.Join to improve performance: repeated string concatenation creates unnecessary partial string objects, which string.Join avoids. 请注意,我还用对string.Join的调用替换了foreach循环以提高性能:重复的字符串串联会创建不必要的部分字符串对象,而string.Join避免了。 Another solution would be to use StringBuider instead of string concatenation += . 另一个解决方案是使用StringBuider代替字符串串联+=

stockEtatQty is in a project (Service) and QuantityToolStripMenuItem_Click is in another project (View) stockEtatQty在一个项目中(服务), QuantityToolStripMenuItem_Click在另一个项目中(视图)

Unfortunately, this means that you would not be able to use anonymous types: anonymous types are generated with internal visibility, limiting their use to the assembly in which they are produced. 不幸的是,这意味着您将无法使用匿名类型:匿名类型是在内部可见的情况下生成的,将它们的使用限制在生成它们的程序集中。 You can use a work-around based on ExpandoObject described in this answer : 您可以根据此答案中所述的ExpandoObject使用解决方法:

var q = afList.Select(x => {
    dynamic res = new ExpandoObject();
    res.EAN=x.EAN;
    res.Title=x.title;
    res.Quantity=x.qty;
    return res;
});

You can use collection of dynamic objects instead of simple objects as return type of your method: 您可以使用动态对象的集合代替简单对象作为方法的返回类型:

public IEnumerable<dynamic> stockEtatQty()

Then you will not have IntelliSense but at runtime properties will be found: 然后,您将没有IntelliSense,但会在运行时找到属性:

foreach (var item in sstock.stockEtatQty())    
    str += String.Format("{0}", item.EAN) + Environment.NewLine;

But I suggest you to create custom class with EAN, Title and Quantity properties. 但是我建议您使用EAN,Title和Quantity属性创建自定义类。 Or just use your allFields instead of anonymous objects. 或者只是使用allFields而不是匿名对象。


Consider also to use StringBuilder for string creation to avoid creating lot of in-memory strings: 还考虑使用StringBuilder进行字符串创建,以避免创建大量内存中字符串:

var builder = new StringBuilder();
foreach (var item in sstock.stockEtatQty())
   builder.AppendFormat("{0}{1}", item.EAN, Environment.NewLine);

MessageBox.Show(builder.ToString());

Create a new class that represents the new object structure and return that. 创建一个表示新对象结构的新类,然后将其返回。

var q = from x in afList
        select new SmallerType { EAN=x.EAN, Title=x.title, Quantity=x.qty };

WinForm Function WinForm函数

foreach (SmallerType item in q)
{
  //
}

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

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