在C#中返回匿名类型

126

我的查询返回一个匿名类型,而且这个查询是在一个方法中的。你应该如何编写它:

public "TheAnonymousType" TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                           select new { SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

      return "TheAnonymousType";
    }
}

6
为什么要返回匿名类型?你如何在其他地方使用这个结果呢? - Yuck
可能是重复的问题:如何从方法返回匿名类型? - nawfal
8
如果您返回的是JSON或其他内容,C#类型并不重要,那么该怎么办呢? - aw04
16
我认为这个问题并不无理。实际上我已经需要做过几次了。当使用实体框架时,如果你想在一个函数中查询并在多个地方使用结果,这一点就更加明显。当我需要在屏幕上显示结果然后需要将相同的结果用于报告或导出到Excel时,我经常需要这样做。查询可能包含很多来自UI的过滤器等,你不想在几个地方创建相同的查询,否则在想要添加结果时容易失步。 - Kevbo
1
可能是重复的问题:有没有办法从方法中返回匿名类型? - HaveNoDisplayName
显示剩余5条评论
16个回答

112

无法做到。

您只能返回object或者对象的容器,例如IEnumerable<object>, IList<object>等。


63
或者使用 "动态的"。这使得处理起来稍微容易一些。 - vcsjones
啊,好的,所以你只能在方法内使用匿名类型,但不能作为返回值? - frenchie
2
@frenchie:是的,只能在成员函数体内使用。如果你想返回它,就把它变成一个众所周知的类型。 - abatishchev
12
使用“动态(dynamic)”并不是一个解决方案,匿名类型的字段不是公共的,而是内部的。 - Hans Passant
8
假设调用方与当前程序集在同一程序集中,那么此时这个功能仍然有些用处。就总的来说,这些字段是公开可见的,但类型是内部的。我认为你不应该返回一个匿名类型,这个观点我是支持的。 - vcsjones

53

你可以返回dynamic,这将会给你一个匿名类型的运行时检查版本,但只适用于 .NET 4+

    public dynamic Get() {
        return new { Message = "Success" };
    }

50

在 C# 7 中,我们可以使用元组来实现这一点:

public List<(int SomeVariable, string AnotherVariable)> TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                       select new { SomeVariable = ....,
                                    AnotherVariable = ....}
                       ).ToList();

      return TheQueryFromDB
                .Select(s => (
                     SomeVariable = s.SomeVariable, 
                     AnotherVariable = s.AnotherVariable))
                 .ToList();
  }
}

虽然您可能需要安装System.ValueTuple NuGet包。


这正是我在寻找的,听起来像是JS类型。 - Fabrice T
6
值得注意的是,如果您决定对命名元组进行JSON序列化,它将给您{Item1: xxx, Item2: xxx}等(即名称会丢失)。 - Alex from Jitbit

28

您无法返回匿名类型。您可以创建一个可以返回的模型吗?否则,您必须使用object

这是Jon Skeet关于此主题的一篇文章

文章中的代码:

using System;

static class GrottyHacks
{
    internal static T Cast<T>(object target, T example)
    {
        return (T) target;
    }
}

class CheesecakeFactory
{
    static object CreateCheesecake()
    {
        return new { Fruit="Strawberry", Topping="Chocolate" };
    }

    static void Main()
    {
        object weaklyTyped = CreateCheesecake();
        var stronglyTyped = GrottyHacks.Cast(weaklyTyped,
            new { Fruit="", Topping="" });

        Console.WriteLine("Cheesecake: {0} ({1})",
            stronglyTyped.Fruit, stronglyTyped.Topping);            
    }
}

或者,这里是另一篇类似的文章

或者,正如其他人所评论的那样,你可以使用 dynamic


9
当然,我可以创建一个类型;我本来想避免这样做。 - frenchie
第一个链接已经失效了,你知道它是否已经转移到其他地方了吗? - Rémi

18

当需要返回值时,您可以使用Tuple类作为匿名类型的替代:

注意:Tuple最多可以有8个参数。

return Tuple.Create(variable1, variable2);

或者,以原始帖子中的示例为例:

public List<Tuple<SomeType, AnotherType>> TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                           select Tuple.Create(..., ...)
                           ).ToList();

      return TheQueryFromDB.ToList();
    }
}

http://msdn.microsoft.com/en-us/library/system.tuple(v=vs.110).aspx


元组可以有超过8个参数,因为第8个参数将用于新的元组。通过使用它,它是完全透明的。(string mystring1, string mystring2, ...., string mystring42) myVar; - Tzwenni

12

C#编译器是一个两阶段编译器。在第一阶段,它只检查命名空间、类层次结构、方法签名等。只有在第二阶段才会编译方法体。

匿名类型直到方法主体被编译才确定。

因此,在第一阶段编译器无法确定方法的返回类型。

这就是为什么匿名类型不能用作返回类型的原因。

正如其他人建议的那样,如果您正在使用.NET 4.0或更高版本,可以使用Dynamic

如果我是您,我可能会创建一种类型,并从该方法中返回该类型。这样对于未来维护您代码的程序员来说更容易理解和阅读。


9
三个选项:
选项1:
public class TheRepresentativeType {
    public ... SomeVariable {get;set;}
    public ... AnotherVariable {get;set;}
}

public IEnumerable<TheRepresentativeType> TheMethod(SomeParameter)
{
   using (MyDC TheDC = new MyDC())
   {
     var TheQueryFromDB = (....
                           select new TheRepresentativeType{ SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

     return TheQueryFromDB;
   } 
}

选项2:
public IEnumerable TheMethod(SomeParameter)
{
   using (MyDC TheDC = new MyDC())
   {
     var TheQueryFromDB = (....
                           select new TheRepresentativeType{ SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();
     return TheQueryFromDB;
   } 
}

你可以将其作为对象进行迭代。
选项3:
public IEnumerable<dynamic> TheMethod(SomeParameter)
{
   using (MyDC TheDC = new MyDC())
   {
     var TheQueryFromDB = (....
                           select new TheRepresentativeType{ SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

     return TheQueryFromDB; //You may need to call .Cast<dynamic>(), but I'm not sure
   } 
}

您将能够将其迭代为动态对象并直接访问其属性。


1
非常感谢!选项3对我有用!我认为这个答案被低估了。 - Fellow7000

6
使用C# 7.0,我们仍然无法返回匿名类型,但我们支持元组类型,因此可以返回一个元组的集合(在这种情况下是System.ValueTuple<T1,T2>)。目前Tuple types不支持表达式树,需要将数据加载到内存中。
你想要的代码的最短版本可能如下所示:
public IEnumerable<(int SomeVariable, object AnotherVariable)> TheMethod()
{
    ...

    return (from data in TheDC.Data
        select new { data.SomeInt, data.SomeObject }).ToList()
        .Select(data => (SomeVariable: data.SomeInt, AnotherVariable: data.SomeObject))
}

或者使用流畅的 Linq 语法:

return TheDC.Data
    .Select(data => new {SomeVariable: data.SomeInt, AnotherVariable: data.SomeObject})
    .ToList();
    .Select(data => (SomeVariable: data.SomeInt, AnotherVariable: data.SomeObject))

使用 C# 7.1,我们可以省略元组的属性名称,它们将从元组初始化中推断出来,就像匿名类型一样工作:
select (data.SomeInt, data.SomeObject)
// or
Select(data => (data.SomeInt, data.SomeObject))

2
在这种情况下,您可以返回对象列表。
public List<object> TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                           select new { SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

      return TheQueryFromDB ;
    }
}

2
public List<SomeClass> TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                           select new SomeClass{ SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

      return TheQueryFromDB.ToList();
    }
}

public class SomeClass{
   public string SomeVariable{get;set}
   public string AnotherVariable{get;set;}
}

创建自己的类并查询它是我所知道的最佳解决方案。据我所知,您不能在另一个方法中使用匿名类型返回值,因为它不会被识别。但是,它们可以在同一个方法中使用。我曾经将它们作为或返回,尽管它仍然无法让您看到匿名类型变量内部的内容。
我曾经遇到过类似的问题,当我试图重构一些代码时,您可以在此处检查它:Refactoring and creating separate methods

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