【问题标题】:Is there a way to return Anonymous Type from method?有没有办法从方法返回匿名类型?
【发布时间】:2010-11-22 17:08:55
【问题描述】:

我知道我不能写这样的方法:

public var MyMethod()
{
   return new{ Property1 = "test", Property2="test"};
}

否则我可以这样做:

public object MyMethod()
{
   return new{ Property1 = "test", Property2="test"}
}

但我不想做第二个选项,因为如果我这样做,我将不得不使用反射。


我为什么要这样做:

今天我在我的 aspx 页面中有一个方法,它返回一个数据表作为结果,我 无法更改它,我试图将此 DataTable 转换为具有我想要工作的属性的匿名方法和。我不想创建一个类只是为了做到这一点,因为我需要多次执行相同的查询,所以我认为创建一个返回匿名类型的方法会是一个好主意。

【问题讨论】:

  • 你会用它做什么?你是怎么得出你想做这样的事情的结论的?
  • @Guffa,我在我的 aspx 页面中有一个方法,它返回一个数据表作为结果,我试图将此数据表转换为具有我想要使用的属性的匿名方法。我不想创建一个类只是为了做到这一点,因为我需要多次执行相同的查询,所以我通过创建一个返回匿名方法的方法将是一个好主意。
  • @Cleiton - 创建一个包含数据的类将花费更少的精力。我开始意识到,仅仅因为我可以使用匿名类型轻松地操作数据,这并不意味着当我需要将它们从一层传递到另一层时,我应该停止创建类来定义这些类型。
  • @Guffa:有时从函数返回多个值或相关值的集合很方便。大多数语言都为临时的即时集合提供元组,但 C# 3.5 尚不支持该功能。唯一的 C# 替代方案是:创建一系列外参数,将临时对象包装在一个类中,或返回一个对象 []。
  • 不要忘记,从 C# 3.0 开始,您可以声明一个只需很少输入的类型。示例:public class NonAnonymousType { public int Foo { get; set; } public string Bar { get; set; } }(格式请见谅)

标签: c# .net anonymous-types


【解决方案1】:

将其作为System.Object 返回是唯一从方法返回匿名类型的方式。不幸的是,没有其他方法可以做到这一点,因为匿名类型是专门为防止以这种方式使用而设计的。

有一些技巧可以结合返回Object 来让您接近。如果您对此解决方法感兴趣,请阅读Can't return anonymous type from method? Really?

免责声明:尽管我链接的文章确实显示了一种解决方法,但这并不意味着这样做是个好主意。我强烈反对您使用这种方法,因为创建常规类型会更安全、更容易理解。

【讨论】:

  • 需要注意的是,这不是一个推荐的想法。 Intellisense 将不再识别匿名类型的强类型属性(您的示例中的 Property1 和 Property2)。实际上,您可以使用反射来获取属性,但您可能还不如用锤子敲打您的拇指来完成您投入的所有工作,更不用说您只是拿了一些本应让您的生活更轻松的东西,但实际上更难了。
  • +1 我自己说得再好不过了。是的,它可以做到,但这并不意味着它是一个好主意。
  • 你也可以返回一个“动态”,我猜这可能是一个变相的 System.Object。
【解决方案2】:

或者,您可以在 .NET 4.0 及更高版本中使用 Tuple 类:

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

Tuple<string, string> Create()
{
return Tuple.Create("test1", "test2");
} 

然后你可以像这样访问属性:

var result = Create();
result.Item1;
result.Item2;

【讨论】:

  • 有趣。我来这里是为了寻找使用元组作为返回类型的替代方法。
【解决方案3】:

作为替代方案,从 C# 7 开始,我们可以使用 ValueTuple。来自here的一个小例子:

public (int sum, int count) DoStuff(IEnumerable<int> values) 
{
    var res = (sum: 0, count: 0);
    foreach (var value in values) { res.sum += value; res.count++; }
    return res;
}

在接收端:

var result = DoStuff(Enumerable.Range(0, 10));
Console.WriteLine($"Sum: {result.Sum}, Count: {result.Count}");

或者:

var (sum, count) = DoStuff(Enumerable.Range(0, 10));
Console.WriteLine($"Sum: {sum}, Count: {count}");

【讨论】:

    【解决方案4】:
    public object MyMethod() 
    {
        return new
        {
             Property1 = "test",
            Property2 = "test"
         };
    }
    
    static void Main(..)
    {
        dynamic o = MyMethod();  
        var p1 = o.Property1;
        var p2 = o.Property2;
    }
    

    【讨论】:

      【解决方案5】:

      最简单的解决方案是创建一个类,将值放入属性中,然后返回它。如果匿名类型让你的生活变得更艰难,那么你就没有正确使用它们。

      【讨论】:

        【解决方案6】:

        尽管有关于这是否是一个好主意的警告...dynamic 似乎对于私有方法来说工作得很好。

        void Main()
        {
            var result = MyMethod();
            Console.WriteLine($"Result: {result.Property1}, {result.Property2}");
        }
        
        public dynamic MyMethod()
        {
            return new { Property1 = "test1", Property2 = "test2" };
        }
        

        你可以在LinqPad.运行这个例子,它会输出:

        结果:test1,test2

        【讨论】:

        • 我认为这不是最好的,因为动态会内置到每个数据类型中,包括对象,所以你最好使用对象,开销更少。
        【解决方案7】:

        不,匿名类型不能存在于创建它们的上下文之外,因此不能用作方法返回类型。您可以将实例返回为 object,但为此目的显式创建您自己的容器类型会更好。

        【讨论】:

          【解决方案8】:

          我认为 Andrew Hare 是对的,您只需要返回“对象”即可。对于编辑评论,我觉得在 OO 代码中处理原始对象可能是一种“代码气味”。在某些情况下这样做是正确的,但大多数时候,如果要返回相关类型,最好定义一个要返回的接口,或者使用某种基类类型。

          【讨论】:

          • 在 OO 代码中使用对象是一种气味,我们遇到了什么问题。
          【解决方案9】:

          不,不支持在方法之外扩展匿名类的范围。在方法之外,该类是真正匿名的,反射是访问其成员的唯一方法。

          【讨论】:

            【解决方案10】:

            对不起,你真的不应该这样做。您可以通过反射或通过创建通用辅助方法来为您返回类型来解决它,但这样做确实违反了该语言。只需声明类型,以便清楚发生了什么。

            【讨论】:

              【解决方案11】:

              如果可能,您还可以反转控制流:

                  public abstract class SafeAnon<TContext>
                  {
                      public static Anon<T> Create<T>(Func<T> anonFactory)
                      {
                          return new Anon<T>(anonFactory());
                      }
              
                      public abstract void Fire(TContext context);
                      public class Anon<T> : SafeAnon<TContext>
                      {
                          private readonly T _out;
              
                          public delegate void Delayed(TContext context, T anon);
              
                          public Anon(T @out)
                          {
                              _out = @out;
                          }
              
                          public event Delayed UseMe;
                          public override void Fire(TContext context)
                          {
                              UseMe?.Invoke(context, _out);
                          }
                      }
                  }
              
                  public static SafeAnon<SomeContext> Test()
                  {
                      var sa = SafeAnon<SomeContext>.Create(() => new { AnonStuff = "asdf123" });
              
                      sa.UseMe += (ctx, anon) =>
                      {
                          ctx.Stuff.Add(anon.AnonStuff);
                      };
              
                      return sa;
                  }
              
                  public class SomeContext
                  {
                      public List<string> Stuff = new List<string>();
                  }
              

              然后在其他地方:

                  static void Main()
                  {
                      var anonWithoutContext = Test();
              
                      var nowTheresMyContext = new SomeContext();
                      anonWithoutContext.Fire(nowTheresMyContext);
              
                      Console.WriteLine(nowTheresMyContext.Stuff[0]);
              
                  }
              

              【讨论】:

                猜你喜欢
                • 2011-02-13
                • 2010-09-08
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-01-16
                • 1970-01-01
                • 1970-01-01
                • 2013-07-02
                相关资源
                最近更新 更多