如何在C#中返回一个通用列表集合?

5

我有一些linq to sql方法,当执行查询时,它返回一些匿名类型。

我想将该匿名类型返回到我的服务层,以便对其进行逻辑处理等操作。

但我不知道如何返回它。

我原以为可以这样做:

public List<T> thisIsAtest()
{
     return query;
}

但是我遇到了这个错误。
Error   1   The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?)

我不确定我缺少哪个汇编代码,或者是否是这种情况。

谢谢

编辑

好的,我的第一个问题解决了,但现在我有一个新问题,因为我不太了解匿名类型,所以不知道如何解决。

我收到以下错误

无法隐式转换类型 'System.Collections.Generic.List' 到 'System.Collections.Generic.List

这是查询

   DbContext.Table.Where(u => u.Table.UserId == userId && u.OutOFF != 0)
       .GroupBy(u => new { u.Table.Prefix })
       .Select(group => new { prefix = group.Key, 
                              Marks = group.Sum(item => (item.Mark * item.Weight) / item.OutOFF) })
       .ToList();

Edit 2

public class ReturnValue
{
   string prefix { get; set; }
   decimal? Marks { get; set; } 
}

public List<ReturnValue> MyTest(Guid userId)
{
   try
   {
       var result = dbContext.Table.Where(u => u.Table.UserId == userId && u.OutOFF != 0).GroupBy(u => new { u.Table.Prefix })
       .Select(group => new { prefix = group.Key, Marks = group.Sum(item => (item.Mark * item.Weight) / item.OutOFF) }).ToList();
       return result;
   }
   catch (SqlException)
   {
       throw;
   }

Anonymous Types:

a is new{string Prefix}
b is new{ 'a prefix, decimal? marks}

为什么你不能够定义一个新类型 class ReturnValue { int prefix; decimal Marks; } 然后返回 new ReturnValue(....) 而不是匿名类型呢? - marc_s
我需要一个例子。我得到了帮助制作这个查询,它做了我想要的事情,但如果可以更改或添加一些内容使其不是匿名的,我完全支持。 - chobo2
你没有使用我提到的代码!在 .Select() 中,你需要使用:.Select(group => new RETURNVALUE { prefix = group.Key, ... - 这不是你所写的!你没有添加在 .Select() 中创建的 TYPE - 这就是你的问题。 - marc_s
5个回答

13
public List<T> thisIsAtest<T>()
{
     return query;
}

那个问题解决了,但现在我得到了这个错误:无法隐式转换类型“System.Collections.Generic.List<AnonymousType#1>”为“System.Collections.Generic.List<T>”。 - chobo2
1
你正在返回一个匿名类型列表的查询结果,但你只能返回一个 <T> 类型的列表。你可以使用 Jon Skeet 的对象和转换技巧,但强烈不建议这样做。 - Yuriy Faktorovich

8
你不能 - 没有任何例外。你不能在它们自己的范围之外使用匿名类型,例如,你不能将它们作为方法的返回值返回。
如果需要返回它们,则需要定义一个新的具体类来代替匿名类型,并在匿名类型的位置使用该类。
请参见Rick Strahl的博客文章关于匿名类型的作用域,以及MSDN文档,其中明确说明:

匿名类型具有方法作用域。要将匿名类型或包含匿名类型的集合传递到方法边界之外,必须首先将类型转换为对象。但是,这会破坏匿名类型的强类型性。如果必须存储查询结果或将其传递到方法边界之外,请考虑使用普通命名的结构体或类,而不是匿名类型。

好吧,当然 - 确实有肮脏可怕的黑客技巧来返回匿名类型。但是,如果微软的MSDN和Jon Skeet都反对这种做法,那么 - 就不要这样做。根据定义和意图,匿名类型绑定到它们所定义的方法。
为chobo2的UPDATE:我不知道你的数据类型是什么 - 只是猜测 - 但假设"prefix"是一个int,"marks"是一个decimal,你可以定义一个新类:
public class ReturnValue
{
    int prefix { get; set; }
    decimal Marks { get; set; } 
}  

然后你的代码将成为一个返回List<ReturnValue>的方法:

public List<ReturnValue> thisIsAtest()
{
   DbContext.Table.Where(u => u.Table.UserId == userId && u.OutOFF != 0)
     .GroupBy(u => new { u.Table.Prefix })
     .Select(group => new ReturnValue 
                          { prefix = group.Key, 
                            Marks = group
                              .Sum(item => (item.Mark * item.Weight) / item.OutOFF) })
     .ToList();
}

重点在于:在您的.Select方法中,不要创建匿名类型的新实例:
     .Select(group => new { prefix = group.Key, marks = .... }

您需要创建一个具体类型的实例:

     .Select(group => new ReturnValue { prefix = group.Key, marks = .... }

通过这种方式,您将拥有一个具体的类 ReturnValue - 可以使用任何您喜欢的名称 - 然后您可以轻松地返回该类型的列表,并在其他地方使用该类型。


这并不是真的。你可以返回匿名类型实例作为 object 类型。我不是说这是好的做法,但是确实是可能的。也就是说,public object DoNotDoThis() { return new { FirstName = "Donald", LastName = "Knuth" }; } 是合法的。 - jason
2
@Jason:是的,理论上可以这样做,但这样做会破坏所有类型安全和其他方面。不要这样做——如果需要返回它,只需创建一个真正的具体类即可——更容易、更清晰。 - marc_s
@marc_s:我完全同意你的看法。这也是为什么示例方法的名称为DoNotDoThis。至于说“匿名类型绑定到它们所定义的方法”,这并不完全准确。请记住,编译器会为您生成一个类定义,并且它在定义程序集中。通过某些巫术(即使用IL或反射进行妙手调整),您可以访问并创建类型的实例。这也是非常非常不良的做法,决不能这样做,但是它是可能的。 - jason
我想,官方的MSDN文档已经非常清楚了,语言设计师的意图是什么——匿名类型从未被设计或用于创建它们的范围之外。这就是我试图传达的信息... - marc_s
好的,问题似乎已经解决了。谢谢。很高兴知道将匿名类型转换为具体类型并不难。 - chobo2
显示剩余4条评论

3
你想从常规方法返回一个匿名类型?我相信你可以通过反射实现,但是这样会没有类型安全性和一系列其他问题。更不用说从调用代码的角度来看,它看起来很奇怪。你基本上必须返回对象。

最好使用类或结构体并将值填充到其中。


0

匿名类型非常有限,只能在声明它们的方法的作用域内使用。

您可以将它们作为普通对象传回,但是这样就失去了类型信息。我见过一些解决方案传递一个匿名类型; 但是它们使用反射在稍后检索属性。


0
如果您将泛型列表声明为全局变量,则不需要使用返回。无论如何,我在我的代码中仍然使用了返回函数,只是因为marc_s说我不能。哈哈
 public static class Globals
    {
        public static List<string> CurPart = new List<string>();
    }
    public List<string> MSQueryRead1DArray(string sql, string db)
    {
        return Globals.CurPart;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接