从匿名类型获取字段

本文关键字:获取 字段 类型 | 更新日期: 2023-09-27 18:11:42

我有这样一个类:

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; }
}

和返回匿名类型的函数:

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}
}

在我的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);
}

结果是:

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

我想要什么?
我不想像上面的结果一样,而是将循环foreach中的item的每个字段分开,例如得到item.EAN, item.Titleitem.Quantity

如果我的问题没有解决办法,我想知道一个替代方案,
谢谢你的帮助。

从匿名类型获取字段

显而易见的解决方案是创建一个自定义类型(我们称其为BookInfo),并返回IEnumerable<BookInfo>而不是IEnumerable<object>(如果您想将格式放入该类本身,可能会覆盖ToString)。

然后你可以很容易地格式化输出。

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);
}

由于匿名类型对象的静态类型信息在退出stockEtatQty()方法时丢失,因此可以将对象强制转换为dynamic并访问如下字段:

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

转换为dynamic告诉编译器EANTitleQuantity需要在运行时解析。

注意,为了提高性能,我还用对string.Join的调用替换了foreach循环:重复的字符串连接会创建不必要的部分字符串对象,而string.Join避免了这一点。另一个解决方案是使用StringBuider代替字符串连接+=

stockEtatQty在一个项目(Service)中,QuantityToolStripMenuItem_Click在另一个项目(View)中

不幸的是,这意味着您将不能使用匿名类型:匿名类型是通过内部可见性生成的,将它们的使用限制在生成它们的程序集中。您可以使用这个答案中描述的基于ExpandoObject的解决方案:

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

创建一个代表新对象结构的新类并返回。

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

WinForm Function

foreach (SmallerType item in q)
{
  //
}

您可以使用动态对象集合而不是简单对象作为方法的返回类型:

public IEnumerable<dynamic> stockEtatQty()

那么你将没有智能感知,但在运行时属性将被发现:

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

但是我建议你用EAN, Title和Quantity属性创建自定义类。或者直接使用allFields而不是匿名对象。


还考虑使用StringBuilder创建字符串,以避免在内存中创建大量字符串:

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