有没有办法从方法返回匿名类型?


71

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

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

否则我可以这样做:

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

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


为什么我要这样做:

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


你会用它做什么?您如何得出结论,您想做类似的事情?
加法

@Guffa,我的aspx页中有一个返回结果的数据表的方法,我试图将此数据表转换为具有我要使用的属性的匿名方法。我不想只创建一个类来做到这一点,因为我将需要多次执行相同的查询,因此我一直想创建一个返回匿名方法的方法,这是一个很好的想法。
克莱顿

1
@Cleiton-创建包含数据的类将花费更少的精力。我已经意识到,仅仅因为我可以使用匿名类型轻松地操作数据,并不意味着当我需要将它们从一层传递到另一层时,我应该停止创建类来定义这些类型。
Jagd

@Guffa:有时从一个函数中返回多个值或一组相关值很方便。大多数语言为临时集合提供元组,但是C#3.5尚不支持该功能。唯一的C#替代方法是:创建一系列输出参数,将临时对象包装在类中,或返回一个object []。
朱丽叶

别忘了,从C#3.0开始,您可以声明很少类型的类型。示例:(public class NonAnonymousType { public int Foo { get; set; } public string Bar { get; set; } }请原谅格式)
devuxer

Answers:


67

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

您可以通过结合使用一些技巧来Object使自己接近。如果您对此解决方案感兴趣,请阅读无法从方法返回匿名类型吗?真?

免责声明:即使我链接的文章确实显示了一种解决方法,但这并不意味着这样做是一个好主意。在创建常规类型更安全,更易于理解时,我强烈建议您不要使用这种方法。


10
应当注意,这不是推荐的想法。Intellisense将不再识别匿名类型的强类型属性(示例中的Property1和Property2)。实际上,您可以使用反射来获取属性,但是您不妨用锤子敲打您要进行的所有工作,更不用说您只是采取了一些可以让您的生活更轻松的事情,但实际上使它变得更加困难。
Jagd

+1自己不能说的更好。是的,可以做到,但这并不意味着它是一个好主意。
布赖恩·拉斯穆森

您也可以返回“动态”,我猜这可能是伪装的System.Object。
cdiggins


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

static void Main(..)
{
    dynamic o = MyMethod();  
    var p1 = o.Property1;
    var p2 = o.Property2;
}

18

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

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

16

最简单的解决方案是创建一个类,将值推入属性,然后返回它。如果匿名类型使您的生活更加艰难,那么您将无法正确使用它们。


11

尽管有关于这是个好主意的警告,但...dynamic对于私有方法来说,A似乎可以正常工作。

void Main()
{
    var result = MyMethod();
    Console.WriteLine($"Result: {result.Property1}, {result.Property2}");
}

public dynamic MyMethod()
{
    return new { Property1 = "test1", Property2 = "test2" };
}

您可以在LinqPad中运行此示例它将输出:

结果:test1,test2


我认为这不是最好的方法,因为动态会内置到包括对象在内的每个数据类型中,因此您最好使用对象,并且开销更少。
MrKekson

3

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


3

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


在OO代码中使用对象是一种气味,我们来了。
Neutrino,

2

抱歉,您真的不应该这样做。您可以通过反射或通过使用通用的辅助方法为您返回类型来破解它,但这确实与该语言背道而驰。只需声明类型即可清楚发生了什么。


2

不可以,不支持在方法之外扩展匿名类的范围。在该方法之外,该类实际上是匿名的,而反射是访问其成员的唯一方法。


2

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

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

    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.